From e8febbf9baf8d9168909f8fd1dddddc6866a7270 Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio de Barros Date: Wed, 28 Aug 2024 12:04:16 -0300 Subject: [PATCH] Preview 4.3 (#250) * Update MasterData dependencies * Fix test compile * Use `DbType.AnsiString` instead of `DbType.String` * `DbType.AnsiString` correct handling * `GetDataType` * Add `BootstrapColor.Info` conditional at `MessageToastFactory` * Performance: Unnecessary `async` keyword * Remove unnecessary `FormContext` class for easier debug. * bugfix: Insert selection not using inserted values after the operation is finished * Reload the entire form after DataImportation * Remove unnecessary class * Optimize `IconHelper` by using static ctor * Use `ValueTask` at `GetFormElementAsync` to show the intent that `FormElement` is cached. * Update ConnectionString name nullability * Use `const` instead of variable * Fix warning as error at Test project * Pagination UI * bugfix: 1xn relationships at Edit mode * Optimize LINQ methods * Use ViaCEP * CEP fixes * Fix date formatting * Remove unnecessary method * Improve debug and performance by loading actions only one time * DataPanel secret values * bugfix: Cache not working as intented at Brasil plugin * Fix Brasil plugin * Fix `FieldFormattingService` with Guid (case-insensitive) values * Append SecretValues only if Any * Fix existing secret values * bugfix error handling at insert selection * Update NCalc to 4.3.3, remove unnecessary `FieldsService` and use `ValueTask` at expressions * Update .csproj * Fix `isAtRelationship` at `GetSaveActionResult` * Optimize empty result * Use `TryGetValue` * Move actions to right * Remove redundant casts at solution * Add ConnectionId support to DbLogger * Create "val" constant and small refactor. * Use submit at InternalRedirect * bugfix: Use submit instead of requestSubmit to work on complex forms * Update jjmasterdata.js.map * Trim value at `FieldFormattingService` * bugfix: Trim searchid * Better readability * bugfix: InternalRedirectController was not saving files * Update NCalc to v5 * Remove old NCalc bug from docs * Add default options * Remove null values from secret values * Add `Name` property to CollapsePanelTagHelper.cs and small refactors * Add better exception if SQL is null or empty * Change JJAlert font-size * GetTitle is no longer async * bugfix: `JJModalDialog` missing fade animation * Html template action (#253) * Added HtmlTemplateAction.cs * Rendering HtmlTemplateAction * HTML templace action iframe * Added Fluid support * l10n and service * ShowAsButton default value false * Change to print * Revert unintented change * Remove unused method * bug fixes * NCalc is now used by default * Added `localizer` function to Fluid * Rename variable * Optimize BootstrapHelper by evaluating only once * Ignore case at GridFilter.cs * Session is essential * Fix JJAlert layout when Title is null or empty * Remove unused using directives * bugfix: ASP.NET Core mvc `TempData` not working * Update session cookie * UrlRedirect now have {AppPath} variable * `UrlRedirectService` * Fix {AppPath} * Code style and use `ValueTask` at controls and factories (rarely async code) * bugfix: Grid Actions not working at insert selection * Remove NCalc from example * Use `ValueTask` in places where the method run more `sync` than `async` (#251) * Use `ValueTask` instead of `Task` at event handlers * Use `ValueTask` where the method result is normally cached * Added `GridOnDataLoadEventHandler` because this event is always `async` * Merge with preview * Update custom_rules.md * Update NCalc to 5.2 and add `ConnectionId` by default at expressions * true * Add Roslynator code analyzer * `IDisposable` warnings * Code style * Add `MustDisposeResource` annotation * Update Jetbrains.Annotations * Code style * Performance: Roslynator analyzers * Fix WebApi error * Rename system cookies * Fix localize function * Remove cache from Brasil services * HubDev fix * Code style * Fix l10n for Unix systems * bugfix at `FieldFormattingService` * Code style * Code style * Code style * Added url redirect support to SqlCommandAction * Fix GridView.cs * BugFix: Decimal places * rollback * Change default number of the items returned on SearchBox from 10 to 30 * bugfix to build * 4.3.0-preview1 * release candidate 4.3.0.0 * removed NCalc Plugin * Ajustes para rodar no mac mobile * cors * changed number Of Items from searchbox to 30 as default * upd MD * BugFix: In the insert, when you enable the element to select and use this same dictionary as a view relationship, it appears as a list. The error was generated in commit f4a9441d0e56ba5727c5cca431a47218a6ed810a * Rider 2024.2 suggestions * Use `char` at StringBuilder * Roslynator * Code style * Small refactors * Fix plugin action with relationships * Performance improvements * Simplify with TryGetValue * bugfix: Insert selection executing back action after a record is selected * Improve LoggerDecoration.cs * Improve LoggerDecoration.cs * Read PageState from QueryString * Fix CA1853 * Code style * Fix pageState query string at relationships * Optimize OrderByData.cs * Fix insert logic * Code style * Code style * Fix Insert logic * Improve form logic using TryGetValue * Allow Fluid functions at Grid template --------- Co-authored-by: Lucio Pelinson --- .editorconfig | 12 + .github/workflows/nuget.yml | 1 - Directory.Build.props | 11 +- JJMasterData.sln | 30 -- doc/Documentation/articles/custom_rules.md | 4 +- doc/Documentation/articles/expressions.md | 23 +- doc/Documentation/articles/plugins/ncalc.md | 1 - doc/Documentation/articles/toc.yml | 2 - example/WebEntryPoint/Program.cs | 8 +- example/WebEntryPoint/WebEntryPoint.csproj | 1 - src/Commons/Commons.csproj | 23 +- .../Configuration/MasterDataServiceBuilder.cs | 2 - .../Configuration/Options/ConnectionString.cs | 2 +- .../Options/MasterDataCommonsOptions.cs | 2 +- .../Options/WritableJsonOptions.cs | 4 +- .../Options/WritableOptionsExtensions.cs | 1 - src/Commons/Data/DataAccess.cs | 39 +- src/Commons/Data/DataAccessAsync.cs | 118 ++--- src/Commons/Data/DataAccessGenerics.cs | 13 +- src/Commons/Data/DataAccessParameter.cs | 2 +- src/Commons/Data/DataAccessProviderFactory.cs | 1 - src/Commons/Data/Entity/Models/Element.cs | 4 +- .../Data/Entity/Models/ElementFieldList.cs | 11 +- .../Data/Entity/Models/ElementRelationship.cs | 1 - .../Entity/Providers/EntityProviderBase.cs | 22 +- .../Data/Entity/Providers/OracleProvider.cs | 60 ++- .../Data/Entity/Providers/PlainTextReader.cs | 10 +- .../Data/Entity/Providers/SQLiteProvider.cs | 56 ++- .../SqlServer/SqlServerAlterTableScripts.cs | 4 +- .../SqlServer/SqlServerCreateTableScripts.cs | 19 +- .../Providers/SqlServer/SqlServerProvider.cs | 79 ++-- .../SqlServerReadProcedureScripts.cs | 36 +- .../Providers/SqlServer/SqlServerScripts.cs | 9 +- .../SqlServer/SqlServerScriptsBase.cs | 6 +- .../SqlServerWriteProcedureScripts.cs | 55 +-- .../Entity/Repository/EntityRepository.cs | 12 +- .../Data/Entity/Repository/OrderByData.cs | 41 +- .../Entity/Repository/OrderByDirection.cs | 16 +- .../Extensions/DictionaryExtensions.cs | 2 + src/Commons/Extensions/EnumExtensions.cs | 1 - src/Commons/Extensions/LinqExtensions.cs | 2 +- .../MasterDataResources.pt-BR.resx | 42 +- .../MasterDataResources.zh-CN.resx | 2 +- .../Localization/MasterDataStringLocalizer.cs | 279 ++++++------ .../MasterDataStringLocalizerFactory.cs | 6 +- src/Commons/Logging/Db/DbLogger.cs | 4 +- .../Logging/Db/DbLoggerBackgroundService.cs | 2 +- src/Commons/Logging/Db/DbLoggerOptions.cs | 6 +- src/Commons/Logging/Db/DbLoggerProvider.cs | 2 +- src/Commons/Logging/File/FileLogger.cs | 6 +- .../Logging/File/FileLoggerProvider.cs | 2 +- src/Commons/Logging/LoggerDecoration.cs | 40 +- .../Cryptography/AesEncryptionAlgorithm.cs | 4 +- src/Commons/Tasks/AsyncEventHandler.cs | 2 +- src/Commons/Tasks/TaskWrapper.cs | 2 +- src/Commons/Util/Email.cs | 109 ++--- src/Commons/Util/EnumerableHelper.cs | 12 + src/Commons/Util/Format.cs | 4 +- src/Commons/Util/ReflectionUtils.cs | 2 +- src/Commons/Util/StringManager.cs | 12 +- src/Commons/Util/XmlHelper.cs | 4 +- src/Commons/Validations/ValidateBrazil.cs | 4 +- .../Configuration/ActionsServiceExtensions.cs | 8 + .../DataManagerServiceExtensions.cs | 1 - .../ExpressionsServiceExtensions.cs | 7 +- .../Options/MasterDataCoreOptions.cs | 35 ++ src/Core/Core.csproj | 8 +- .../Models/Actions/BasicAction.cs | 4 +- .../Models/Actions/FormElementActionList.cs | 28 +- .../Actions/FormElementFieldActionList.cs | 3 +- .../FormToolbar/AuditLogFormToolbarAction.cs | 2 +- .../Models/Actions/FormToolbar/BackAction.cs | 2 +- .../Actions/FormToolbar/CancelAction.cs | 2 +- .../Actions/FormToolbar/FormEditAction.cs | 2 +- .../Models/Actions/FormToolbar/SaveAction.cs | 2 +- .../Models/Actions/FormToolbarActionList.cs | 46 +- .../Models/Actions/GridTable/DeleteAction.cs | 2 +- .../Models/Actions/GridTable/EditAction.cs | 2 +- .../GridTable/InsertSelectionAction.cs | 2 +- .../Models/Actions/GridTable/ViewAction.cs | 2 +- .../Models/Actions/GridTableActionList.cs | 32 +- .../GridToolbar/AuditLogGridToolbarAction.cs | 2 +- .../Actions/GridToolbar/ConfigAction.cs | 2 +- .../Actions/GridToolbar/ExportAction.cs | 2 +- .../Actions/GridToolbar/FilterAction.cs | 2 +- .../Actions/GridToolbar/ImportAction.cs | 2 +- .../Actions/GridToolbar/InsertAction.cs | 2 +- .../Actions/GridToolbar/LegendAction.cs | 2 +- .../Actions/GridToolbar/RefreshAction.cs | 2 +- .../Models/Actions/GridToolbar/SortAction.cs | 2 +- .../Models/Actions/GridToolbarActionList.cs | 73 +-- .../Models/Actions/Plugins/PluginAction.cs | 2 +- .../Actions/Plugins/PluginActionContext.cs | 2 + .../Actions/Plugins/PluginFieldAction.cs | 2 +- .../Actions/UserCreated/HtmlTemplateAction.cs | 25 ++ .../Actions/UserCreated/InternalAction.cs | 2 +- .../Actions/UserCreated/ScriptAction.cs | 2 +- .../Actions/UserCreated/SqlCommandAction.cs | 18 +- .../Actions/UserCreated/SubmitAction.cs | 2 +- .../Actions/UserCreated/UrlRedirectAction.cs | 6 +- .../DataDictionary/Models/BootstrapColor.cs | 4 +- .../DataDictionary/Models/DataElementMap.cs | 1 - .../Models/FormElementDataItem.cs | 1 - .../DataDictionary/Models/FormElementField.cs | 4 +- .../Models/FormElementFieldList.cs | 14 +- .../Models/FormElementOptions.cs | 2 +- .../Models/FormElementRelationshipList.cs | 12 +- src/Core/DataDictionary/Models/IconHelper.cs | 80 ++-- .../DataDictionary/Models/ProcessScope.cs | 1 - .../Abstractions/IDataDictionaryRepository.cs | 2 +- .../FileSystemDataDictionaryRepository.cs | 10 +- .../Repository/SqlDataDictionaryRepository.cs | 18 +- .../DataDictionary/Services/BaseService.cs | 2 +- .../Services/ClassGenerationService.cs | 7 +- .../DataDictionary/Services/ElementService.cs | 26 +- .../DataDictionary/Services/EntityService.cs | 12 +- .../DataDictionary/Services/FieldService.cs | 1 - .../DataDictionary/Services/PanelService.cs | 3 +- .../Services/UIOptionsService.cs | 3 +- .../Structure/DataDictionaryFilter.cs | 2 +- .../DataDictionaryFormElementFactory.cs | 53 ++- .../LocalizationFormElementFactory.cs | 18 +- .../Structure/LoggerFormElementFactory.cs | 46 +- src/Core/DataManager/DataHelper.cs | 39 +- .../Abstractions/DataExportationWriterBase.cs | 18 +- .../Exportation/Abstractions/IExcelWriter.cs | 2 +- .../DataManager/Exportation/ExcelWriter.cs | 7 +- .../DataManager/Exportation/TextWriter.cs | 10 +- .../Abstractions/IAsyncExpressionProvider.cs | 4 +- .../ExpressionDataAccessCommandFactory.cs | 6 +- .../Expressions/ExpressionParser.cs | 20 +- .../Expressions/ExpressionsService.cs | 84 ++-- .../Providers/DefaultExpressionProvider.cs | 41 +- .../Providers/SqlExpressionProvider.cs | 6 +- .../Providers/ValueExpressionProvider.cs | 12 +- src/Core/DataManager/IO/FormFileManager.cs | 30 +- src/Core/DataManager/IO/FormFileService.cs | 5 +- .../Importation/DataImportationWorker.cs | 2 +- src/Core/DataManager/Models/DataContext.cs | 10 +- src/Core/DataManager/Models/DataQuery.cs | 1 - src/Core/DataManager/Models/FormContext.cs | 28 -- .../DataManager/Services/AuditLogService.cs | 21 +- .../DataManager/Services/DataItemService.cs | 51 +-- .../DataManager/Services/ElementMapService.cs | 19 +- .../Services/FieldFormattingService.cs | 95 ++-- .../Services/FieldValidationService.cs | 79 ++-- .../Services/FieldValuesService.cs | 28 +- .../DataManager/Services/FieldsService.cs | 53 --- src/Core/DataManager/Services/FormService.cs | 64 ++- .../DataManager/Services/FormValuesService.cs | 57 +-- .../Services/HtmlTemplateService.cs | 123 ++++++ .../DataManager/Services/LookupService.cs | 35 +- .../DataManager/Services/UploadAreaService.cs | 28 +- .../Services/UrlRedirectService.cs | 32 +- .../Abstractions/FormEventHandlerBase.cs | 17 +- .../Events/Abstractions/IFormEventHandler.cs | 32 +- .../Extensions/EncryptionServiceExtensions.cs | 15 - ...Extensions.cs => HttpContextExtensions.cs} | 7 +- src/Core/Http/AspNetCore/FormValuesWrapper.cs | 2 +- .../Http/AspNetCore/HttpRequestWrapper.cs | 2 +- .../Http/AspNetCore/HttpSessionWrapper.cs | 2 +- .../Http/AspNetCore/QueryStringWrapper.cs | 2 +- src/Core/Http/HttpContextWrapper.cs | 2 +- .../SystemWeb/SystemWebFormValuesWrapper.cs | 2 +- .../SystemWeb/SystemWebHttpRequestWrapper.cs | 2 +- .../SystemWeb/SystemWebHttpSessionWrapper.cs | 2 +- .../SystemWeb/SystemWebQueryStringWrapper.cs | 2 +- src/Core/Logging/LogMessages.cs | 2 - src/Core/Tasks/AsyncHelper.cs | 14 +- src/Core/Tasks/ValueTaskHelper.cs | 20 + src/Core/UI/BootstrapHelper.cs | 392 +++++++++-------- .../Components/Actions/ActionButtonFactory.cs | 54 +-- .../UI/Components/Actions/ActionContext.cs | 5 +- src/Core/UI/Components/Actions/ActionMap.cs | 15 +- .../UI/Components/Actions/ActionScripts.cs | 61 ++- src/Core/UI/Components/AsyncComponent.cs | 6 +- .../AuditLog/AuditLogViewFactory.cs | 18 +- .../UI/Components/AuditLog/JJAuditLogView.cs | 41 +- src/Core/UI/Components/ComponentBase.cs | 3 +- src/Core/UI/Components/ComponentFactory.cs | 16 +- .../UI/Components/ComponentNameGenerator.cs | 12 - .../UI/Components/ContentComponentResult.cs | 4 +- .../Controls/CheckBox/CheckBoxFactory.cs | 8 +- .../Controls/CheckBox/JJCheckBox.cs | 11 +- .../ColorPicker/ColorPickerFactory.cs | 6 +- .../Controls/ColorPicker/JJColorPicker.cs | 6 +- .../Controls/ComboBox/ComboBoxFactory.cs | 16 +- .../Controls/ComboBox/JJComboBox.cs | 18 +- .../UI/Components/Controls/ControlBase.cs | 14 +- .../UI/Components/Controls/ControlFactory.cs | 11 +- .../Controls/IconPicker/JJIconPicker.cs | 4 +- .../UI/Components/Controls/Lookup/JJLookup.cs | 7 +- .../Controls/Lookup/LookupFactory.cs | 40 +- .../Controls/RadioButton/JJRadioButton.cs | 8 +- .../RadioButton/JJRadioButtonGroup.cs | 2 +- .../RadioButton/RadioButtonGroupFactory.cs | 7 +- .../Controls/SearchBox/JJSearchBox.cs | 48 +- .../Controls/SearchBox/SearchBoxFactory.cs | 11 +- .../UI/Components/Controls/Slider/JJSlider.cs | 6 +- .../Controls/Slider/SliderFactory.cs | 10 +- .../Controls/TextArea/JJTextArea.cs | 6 +- .../Controls/TextArea/TextAreaFactory.cs | 9 +- .../Components/Controls/TextBox/JJTextBox.cs | 6 +- .../Controls/TextBox/JJTextGroup.cs | 48 +- .../Controls/TextBox/TextBoxFactory.cs | 9 +- .../Controls/TextBox/TextGroupFactory.cs | 38 +- .../Controls/TextFile/JJTextFile.cs | 8 +- .../Controls/TextFile/TextFileFactory.cs | 12 +- .../Controls/TextFile/TextFileScripts.cs | 6 +- .../Controls/TextRange/JJTextRange.cs | 6 +- .../Controls/TextRange/TextRangeFactory.cs | 2 +- .../Components/DataPanel/DataPanelControl.cs | 29 +- .../DataPanel/DataPanelExpressionScripts.cs | 3 +- .../Components/DataPanel/DataPanelFactory.cs | 37 +- .../Components/DataPanel/DataPanelLayout.cs | 11 +- .../Components/DataPanel/DataPanelScripts.cs | 4 +- .../UI/Components/DataPanel/JJDataPanel.cs | 88 ++-- .../UI/Components/EmptyComponentResult.cs | 6 +- .../Exportation/DataExportationFactory.cs | 5 +- .../Exportation/DataExportationLog.cs | 2 +- .../Exportation/DataExportationScripts.cs | 2 +- .../Exportation/DataExportationSettings.cs | 12 +- .../Exportation/JJDataExportation.cs | 6 +- src/Core/UI/Components/FileComponentResult.cs | 2 +- .../UI/Components/FormView/FormViewFactory.cs | 12 +- .../FormView/FormViewRelationshipLayout.cs | 15 +- .../UI/Components/FormView/FormViewScripts.cs | 4 +- src/Core/UI/Components/FormView/JJFormView.cs | 416 ++++++++++-------- .../UI/Components/GridView/GridCaptionView.cs | 45 +- src/Core/UI/Components/GridView/GridFilter.cs | 283 ++++++------ .../UI/Components/GridView/GridPagination.cs | 91 ++-- .../UI/Components/GridView/GridScripts.cs | 8 +- .../UI/Components/GridView/GridSettings.cs | 2 +- .../Components/GridView/GridSettingsForm.cs | 8 +- .../Components/GridView/GridSortingConfig.cs | 60 ++- .../GridView/GridSqlCommandAction.cs | 18 +- src/Core/UI/Components/GridView/GridTable.cs | 8 +- .../UI/Components/GridView/GridTableBody.cs | 164 ++++--- .../UI/Components/GridView/GridTableHeader.cs | 58 ++- .../UI/Components/GridView/GridToolbar.cs | 10 +- .../UI/Components/GridView/GridViewFactory.cs | 16 +- src/Core/UI/Components/GridView/JJGridView.cs | 216 ++++----- .../UI/Components/Html/Alert/AlertFactory.cs | 5 +- src/Core/UI/Components/Html/Alert/JJAlert.cs | 81 ++-- .../Html/Breadcrumb/BreadcrumbFactory.cs | 2 + .../Html/Breadcrumb/JJBreadcrumb.cs | 6 +- .../Html/CollapsePanel/JJCollapsePanel.cs | 4 +- .../Components/Html/HtmlComponentFactory.cs | 2 + .../UI/Components/Html/Icon/IconFactory.cs | 5 +- src/Core/UI/Components/Html/Icon/JJIcon.cs | 12 +- .../UI/Components/Html/Image/ImageFactory.cs | 3 + .../Html/LinkButton/JJLinkButton.cs | 4 +- .../Html/LinkButton/JJLinkButtonGroup.cs | 13 +- .../Html/MessageToast/MessageToastFactory.cs | 4 + .../Html/ModalDialog/JJModalDialog.cs | 3 +- .../Components/Html/Offcanvas/JJOffcanvas.cs | 2 +- .../UI/Components/Html/TabNav/JJTabNav.cs | 2 +- .../UI/Components/Html/Title/TitleFactory.cs | 1 - .../ValidationSummary/JJValidationSummary.cs | 5 +- .../UI/Components/IComponentFactoryOfT.cs | 2 +- .../FileDownloader/FileDownloaderFactory.cs | 11 +- .../IO/FileDownloader/JJFileDownloader.cs | 4 +- .../Components/IO/UploadArea/JJUploadArea.cs | 11 +- .../IO/UploadArea/UploadAreaFactory.cs | 9 +- .../IO/UploadArea/UploadAreaResultDto.cs | 6 +- .../Components/IO/UploadView/JJUploadView.cs | 23 +- .../IO/UploadView/UploadViewFactory.cs | 18 +- .../Importation/DataImportationFactory.cs | 21 +- .../Importation/DataImportationHelp.cs | 6 +- .../Importation/DataImportationLog.cs | 6 +- .../Importation/DataImportationScripts.cs | 16 +- .../Importation/JJDataImportation.cs | 9 +- src/Core/UI/Components/JsonComponentResult.cs | 2 +- src/Core/UI/Components/ProcessComponent.cs | 5 +- .../UI/Components/RenderedComponentResult.cs | 2 +- .../Abstractions/GridEventHandlerBase.cs | 13 +- .../Events/Abstractions/IGridEventHandler.cs | 25 +- .../UI/Events/GridDataLoadEventHandler.cs | 6 + src/Core/UI/Html/HtmlBuilder.Attributes.cs | 6 +- src/Core/UI/Html/HtmlBuilder.Children.cs | 18 +- src/Core/UI/Html/HtmlBuilder.cs | 102 +++-- src/Core/UI/Html/HtmlBuilderTag.cs | 92 ++-- src/Core/UI/Html/HtmlBuilderTags.cs | 46 -- src/Core/UI/Html/HtmlTag.cs | 60 +-- src/Core/UI/Routing/RouteContextFactory.cs | 7 +- .../Actions/BrasilPluginActionHandler.cs | 28 +- .../Brasil/Actions/CepPluginActionHandler.cs | 9 +- .../Brasil/Actions/CnpjPluginActionHandler.cs | 2 - .../Brasil/Actions/CpfPluginActionHandler.cs | 2 - .../MasterDataServiceBuilderExtensions.cs | 2 +- src/Plugins/Brasil/Models/SintegraCnpjDto.cs | 2 +- src/Plugins/Brasil/Models/SintegraCpfDto.cs | 2 +- src/Plugins/Brasil/Services/HubDevService.cs | 24 +- src/Plugins/Brasil/Services/ViaCepService.cs | 43 +- src/Plugins/Hangfire/BackgroundTaskManager.cs | 2 - src/Plugins/Hangfire/Hangfire.csproj | 1 + .../MasterDataServiceBuilderExtensions.cs | 1 - src/Plugins/Hangfire/TaskTrigger.cs | 4 +- src/Plugins/Hangfire/TaskWrapper.cs | 2 +- .../MongoDB/Models/MongoDBFormElement.cs | 2 +- .../MongoDbDataDictionaryRepository.cs | 8 +- .../MasterDataServiceBuilderExtensions.cs | 51 --- .../NCalcExpressionProviderOptions.cs | 22 - src/Plugins/NCalc/NCalc.csproj | 30 -- src/Plugins/NCalc/NCalcExpressionProvider.cs | 30 -- src/Plugins/NCalc/README.MD | 70 --- src/Plugins/Pdf/Pdf.csproj | 1 + .../MasterDataServiceBuilderExtensions.cs | 1 - .../Python/Engine/PythonEngineFactory.cs | 2 +- .../DataDictionaryDocumentFilter.cs | 1 - .../DataDictionaryOperationFactory.cs | 2 +- .../DataDictionaryPathItem.cs | 2 +- .../Swagger.AspNetCore.csproj | 1 + .../Swagger/DataDictionaryDocumentFilter.cs | 2 - .../Controllers/ActionsController.cs | 17 +- .../Controllers/ApiController.cs | 2 +- .../Controllers/ElementController.cs | 12 +- .../Controllers/FieldController.cs | 27 +- .../Controllers/LogController.cs | 4 +- .../Controllers/PanelController.cs | 12 +- .../DataDictionary/Models/AboutViewModel.cs | 4 +- .../Services/LocalizationService.cs | 4 +- .../Services/SettingsService.cs | 1 - .../Views/Actions/HtmlTemplateAction.cshtml | 99 +++++ .../Views/Actions/InternalAction.cshtml | 93 ++-- .../Views/Actions/SqlCommandAction.cshtml | 17 +- .../Views/Actions/UrlRedirectAction.cshtml | 61 +-- .../DataDictionary/Views/Actions/_List.cshtml | 6 + .../Views/Actions/_NavAdvanced.cshtml | 61 +-- .../Views/Actions/_ProcessOptions.cshtml | 2 +- .../Views/Actions/_Toolbar.cshtml | 2 +- .../DataDictionary/Views/Entity/Edit.cshtml | 1 - .../DataDictionary/Views/Field/Index.cshtml | 4 +- .../Views/Field/_DataItemSql.cshtml | 4 +- .../Views/Field/_FieldLayout.cshtml | 2 +- .../DataDictionary/Views/Indexes/Index.cshtml | 8 +- .../MasterData/Controllers/FormController.cs | 10 +- .../MasterData/Controllers/IconsController.cs | 2 +- .../Controllers/InternalRedirectController.cs | 27 +- .../Areas/MasterData/Models/FormViewModel.cs | 7 - .../Areas/MasterData/Views/Form/Render.cshtml | 11 +- .../MasterData/Views/Icons/_Icons.cshtml | 3 +- .../Views/InternalRedirect/Index.cshtml | 5 +- src/Web/Binders/ExpressionModelBinder.cs | 2 +- .../MasterDataWebOptionsConfiguration.cs | 2 +- .../ServiceCollectionExtensions.cs | 12 +- src/Web/Extensions/AssetPipelineExtensions.cs | 1 + src/Web/Scripts/ActionHelper.ts | 66 ++- src/Web/Scripts/DataImportationHelper.ts | 26 +- src/Web/Scripts/ListenAllEvents.ts | 3 +- src/Web/Scripts/PostFormValues.ts | 1 + src/Web/Scripts/SearchBoxListener.ts | 2 +- src/Web/Scripts/Utils.ts | 14 +- src/Web/Styles/_code-mirror.scss | 4 + src/Web/Styles/_form.scss | 4 + src/Web/TagHelpers/CheckboxTagHelper.cs | 4 +- src/Web/TagHelpers/CollapsePanelTagHelper.cs | 5 +- src/Web/TagHelpers/ComboBoxTagHelper.cs | 5 +- src/Web/TagHelpers/ExpressionTagHelper.cs | 13 +- src/Web/TagHelpers/LinkButtonTagHelper.cs | 3 - src/Web/TagHelpers/OffcanvasTagHelper.cs | 3 +- src/Web/TagHelpers/SliderTagHelper.cs | 3 +- src/Web/TagHelpers/TextGroupTagHelper.cs | 3 +- src/Web/Views/Error/Index.cshtml | 2 - .../Shared/_MasterDataLayout.Modal.cshtml | 3 +- .../Views/Shared/_MasterDataScripts.cshtml | 4 +- src/Web/Web.csproj | 2 +- .../wwwroot/css/bootstrap/bootstrap.min.css | 1 - .../bootstrap-select/bootstrap-select.min.js | 1 - .../bootstrap-toggle/bootstrap-toggle.min.js | 1 - .../bootstrap5/bootstrap.bundle.min.js | 1 - src/Web/wwwroot/js/codemirror/mode/liquid.js | 145 ++++++ .../wwwroot/js/jjmasterdata/jjmasterdata.js | 74 +++- .../js/jjmasterdata/jjmasterdata.js.map | 2 +- src/Web/wwwroot/js/popperjs/popper.min.js | 2 +- .../Controllers/DictionariesController.cs | 2 +- src/WebApi/Controllers/MasterApiController.cs | 8 +- src/WebApi/Services/DictionariesService.cs | 8 +- src/WebApi/Services/MasterApiService.cs | 45 +- src/WebApi/WebApi.csproj | 1 - test/Commons.Test/Commons.Test.csproj | 1 - .../Localization/StringLocalizerTest.cs | 1 - .../Options/WritableJsonOptionsTest.cs | 2 +- .../DataDictionary/FormElementTest.cs | 1 - .../Expressions/ExpressionsServiceTests.cs | 3 +- .../Services/FieldValidationServiceTests.cs | 7 +- .../DataManager/Services/FormServiceTests.cs | 1 - .../Core.Test/UI/Components/JJTextBoxTests.cs | 99 +++-- .../Brasil/Brasil.Test/ViaCepServiceTest.cs | 4 +- 389 files changed, 4008 insertions(+), 3868 deletions(-) delete mode 100644 doc/Documentation/articles/plugins/ncalc.md create mode 100644 src/Core/DataDictionary/Models/Actions/UserCreated/HtmlTemplateAction.cs delete mode 100644 src/Core/DataManager/Models/FormContext.cs delete mode 100644 src/Core/DataManager/Services/FieldsService.cs create mode 100644 src/Core/DataManager/Services/HtmlTemplateService.cs rename src/Core/Extensions/{QueryStringExtensions.cs => HttpContextExtensions.cs} (55%) create mode 100644 src/Core/Tasks/ValueTaskHelper.cs delete mode 100644 src/Core/UI/Components/ComponentNameGenerator.cs create mode 100644 src/Core/UI/Events/GridDataLoadEventHandler.cs delete mode 100644 src/Core/UI/Html/HtmlBuilderTags.cs delete mode 100644 src/Plugins/NCalc/Configuration/MasterDataServiceBuilderExtensions.cs delete mode 100644 src/Plugins/NCalc/Configuration/NCalcExpressionProviderOptions.cs delete mode 100644 src/Plugins/NCalc/NCalc.csproj delete mode 100644 src/Plugins/NCalc/NCalcExpressionProvider.cs delete mode 100644 src/Plugins/NCalc/README.MD create mode 100644 src/Web/Areas/DataDictionary/Views/Actions/HtmlTemplateAction.cshtml delete mode 100644 src/Web/Areas/MasterData/Models/FormViewModel.cs create mode 100644 src/Web/wwwroot/js/codemirror/mode/liquid.js diff --git a/.editorconfig b/.editorconfig index 8f0432e49..c9665e79b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,16 @@ [*.cs] + +dotnet_diagnostic.CA1841.severity = error + # CS1591: Missing XML comment for publicly visible type or member dotnet_diagnostic.CS1591.severity = none + +dotnet_diagnostic.RCS1194.severity = none +dotnet_diagnostic.RCS1139.severity = none +dotnet_diagnostic.RCS1015.severity = error +dotnet_diagnostic.RCS1077.severity = error +dotnet_diagnostic.RCS1080.severity = error +dotnet_diagnostic.RCS1105.severity = error +dotnet_diagnostic.RCS1112.severity = error +dotnet_diagnostic.RCS1156.severity = error \ No newline at end of file diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 813864e1a..2cc92f811 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -33,7 +33,6 @@ jobs: - src/WebApi - src/Plugins/Hangfire - src/Plugins/Pdf - - src/Plugins/NCalc steps: - uses: actions/checkout@v2 - name: Build and publish NuGet package diff --git a/Directory.Build.props b/Directory.Build.props index 101fd93dd..995c64bb3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,11 +3,12 @@ https://www.github.com/JJConsulting/JJMasterData https://www.github.com/JJConsulting/JJMasterData JJMasterData.png - 4.2.6 + 4.3.0 README.NuGet.md - $(Version) + $(Version).0-rc $(Version) true + true CA1859;CS4014 snupkg $(Version) @@ -30,5 +31,11 @@ / + + + + + + diff --git a/JJMasterData.sln b/JJMasterData.sln index 0d197776d..100740528 100644 --- a/JJMasterData.sln +++ b/JJMasterData.sln @@ -33,8 +33,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{6DF8173F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{CAB8385E-033D-4D3B-A7F2-6F024AAB8527}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web.Test", "test\Web.Test\Web.Test.csproj", "{85B7C6A1-9901-4CB4-BCE9-D60F9C257737}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Commons.Test", "test\Commons.Test\Commons.Test.csproj", "{14C3076E-B624-4F3B-A4A6-D091577937A1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{86CDB147-0DAF-4493-BAAA-277D13A85DB8}" @@ -73,8 +71,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "src\Core\Core.cspro EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Brasil.Test", "test\Plugins\Brasil\Brasil.Test\Brasil.Test.csproj", "{475FAD96-EB34-4761-B551-8DC0100A592A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NCalc", "src\Plugins\NCalc\NCalc.csproj", "{FDF54236-3662-4C89-B49D-5700C64C4B8D}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SchemaGenerator", "src\ConsoleApps\SchemaGenerator\SchemaGenerator.csproj", "{29388C67-8C15-4EAE-836C-15BA812846F7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FormElementImporter", "src\ConsoleApps\FormElementImporter\FormElementImporter.csproj", "{8FB84AE3-4FBC-492F-B3BE-4B7000CEDFE1}" @@ -187,18 +183,6 @@ Global {CAB8385E-033D-4D3B-A7F2-6F024AAB8527}.Release|x64.Build.0 = Release|Any CPU {CAB8385E-033D-4D3B-A7F2-6F024AAB8527}.Release|x86.ActiveCfg = Release|Any CPU {CAB8385E-033D-4D3B-A7F2-6F024AAB8527}.Release|x86.Build.0 = Release|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Debug|x64.ActiveCfg = Debug|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Debug|x64.Build.0 = Debug|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Debug|x86.ActiveCfg = Debug|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Debug|x86.Build.0 = Debug|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Release|Any CPU.Build.0 = Release|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Release|x64.ActiveCfg = Release|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Release|x64.Build.0 = Release|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Release|x86.ActiveCfg = Release|Any CPU - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737}.Release|x86.Build.0 = Release|Any CPU {14C3076E-B624-4F3B-A4A6-D091577937A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {14C3076E-B624-4F3B-A4A6-D091577937A1}.Debug|Any CPU.Build.0 = Debug|Any CPU {14C3076E-B624-4F3B-A4A6-D091577937A1}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -307,18 +291,6 @@ Global {475FAD96-EB34-4761-B551-8DC0100A592A}.Release|x64.Build.0 = Release|Any CPU {475FAD96-EB34-4761-B551-8DC0100A592A}.Release|x86.ActiveCfg = Release|Any CPU {475FAD96-EB34-4761-B551-8DC0100A592A}.Release|x86.Build.0 = Release|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Debug|x64.ActiveCfg = Debug|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Debug|x64.Build.0 = Debug|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Debug|x86.ActiveCfg = Debug|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Debug|x86.Build.0 = Debug|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Release|Any CPU.Build.0 = Release|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Release|x64.ActiveCfg = Release|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Release|x64.Build.0 = Release|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Release|x86.ActiveCfg = Release|Any CPU - {FDF54236-3662-4C89-B49D-5700C64C4B8D}.Release|x86.Build.0 = Release|Any CPU {29388C67-8C15-4EAE-836C-15BA812846F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29388C67-8C15-4EAE-836C-15BA812846F7}.Debug|Any CPU.Build.0 = Debug|Any CPU {29388C67-8C15-4EAE-836C-15BA812846F7}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -368,14 +340,12 @@ Global {CD6DDE10-3DC3-4D42-BA45-9999E995F39A} = {59427CF1-D6B7-4779-9E62-4717C5763762} {40C11852-04ED-413B-9FED-C9666D62B0A2} = {59427CF1-D6B7-4779-9E62-4717C5763762} {CAB8385E-033D-4D3B-A7F2-6F024AAB8527} = {6DF8173F-DE7A-4110-B192-856C0CD48D37} - {85B7C6A1-9901-4CB4-BCE9-D60F9C257737} = {6DF8173F-DE7A-4110-B192-856C0CD48D37} {14C3076E-B624-4F3B-A4A6-D091577937A1} = {6DF8173F-DE7A-4110-B192-856C0CD48D37} {F3309B09-66B6-41DC-9624-DE50F6600CF1} = {59427CF1-D6B7-4779-9E62-4717C5763762} {98258DC9-8941-48AA-A5B8-4266CE09AA59} = {6DF8173F-DE7A-4110-B192-856C0CD48D37} {62CAAFCD-1856-417A-A23F-92A332B67236} = {59427CF1-D6B7-4779-9E62-4717C5763762} {13EC3E0B-2540-4210-B816-26F57865891B} = {6DF8173F-DE7A-4110-B192-856C0CD48D37} {475FAD96-EB34-4761-B551-8DC0100A592A} = {98258DC9-8941-48AA-A5B8-4266CE09AA59} - {FDF54236-3662-4C89-B49D-5700C64C4B8D} = {59427CF1-D6B7-4779-9E62-4717C5763762} {29388C67-8C15-4EAE-836C-15BA812846F7} = {8799DF37-58EB-4B95-A558-DD3D5EDD247A} {8FB84AE3-4FBC-492F-B3BE-4B7000CEDFE1} = {8799DF37-58EB-4B95-A558-DD3D5EDD247A} {5325D0BE-1C2F-4A5E-8A30-8A443DE17C53} = {8799DF37-58EB-4B95-A558-DD3D5EDD247A} diff --git a/doc/Documentation/articles/custom_rules.md b/doc/Documentation/articles/custom_rules.md index 302d525b8..a375580e1 100644 --- a/doc/Documentation/articles/custom_rules.md +++ b/doc/Documentation/articles/custom_rules.md @@ -32,7 +32,7 @@ public class AgendamentoStatusFormEventHandler : FormEventHandlerBase private const string IconeFieldName = "Icone"; public override string ElementName => "AgendamentoStatus"; - public override Task OnFormElementLoadAsync(object sender, FormElementLoadEventArgs args) + public override ValueTask OnFormElementLoadAsync(object sender, FormElementLoadEventArgs args) { var formElement = args.FormElement; var iconField = formElement.Fields[IconeFieldName]; @@ -54,7 +54,7 @@ public class AgendamentoStatusFormEventHandler : FormEventHandlerBase }); } - return Task.CompletedTask; + return ValueTask.CompletedTask; } } ``` diff --git a/doc/Documentation/articles/expressions.md b/doc/Documentation/articles/expressions.md index 2f27c0061..503ac30d8 100644 --- a/doc/Documentation/articles/expressions.md +++ b/doc/Documentation/articles/expressions.md @@ -25,7 +25,7 @@ In the Data Dictionary UI, the expression editor is simplified like this: ## What are the default expression providers? - Type [val:] returns a value; (1 or 0) (true or false) ("foo") etc.. -- Type [exp:] returns the result of the expression from `DataTable.Compute`; +- Type [exp:] returns the result of the expression from [NCalc](https://github.com/ncalc/ncalc); - Type [sql:] returns the result of a sql command; > [!TIP] @@ -105,4 +105,23 @@ At your `Program.cs` simply: builder.Services.AddJJMasterDataWeb().WithExpressionProvider(); ``` -[!include[Readme](../../../src/Plugins/NCalc/README.MD)] \ No newline at end of file +## Executing C# code at your expressions +You can execute C# code using the following example; + +Program.cs: +``` +builder.Services.PostConfigure(options => +{ + options.ExpressionsContext.Functions = new Dictionary + { + {"now", _ => DateTime.Now}, + {"myAwesomeFunction", args => MyCustomClass.Execute(args[0], args[1])} + }.ToFrozenDictionary() +}); + +``` + +At your expression: +``` +exp: myAwesomeFunction('{StringValue}',{IntValue}) +``` diff --git a/doc/Documentation/articles/plugins/ncalc.md b/doc/Documentation/articles/plugins/ncalc.md deleted file mode 100644 index b89366999..000000000 --- a/doc/Documentation/articles/plugins/ncalc.md +++ /dev/null @@ -1 +0,0 @@ -[!include[Readme](../../../../src/Plugins/NCalc/README.MD)] \ No newline at end of file diff --git a/doc/Documentation/articles/toc.yml b/doc/Documentation/articles/toc.yml index c6b3cedfc..552391d0c 100644 --- a/doc/Documentation/articles/toc.yml +++ b/doc/Documentation/articles/toc.yml @@ -65,8 +65,6 @@ href: plugins/mongodb.md - name: Python href: plugins/python.md - - name: NCalc - href: plugins/ncalc.md - name: Miscellaneous items: - name: Multiple Forms Support diff --git a/example/WebEntryPoint/Program.cs b/example/WebEntryPoint/Program.cs index d4f393804..15101a385 100644 --- a/example/WebEntryPoint/Program.cs +++ b/example/WebEntryPoint/Program.cs @@ -1,8 +1,5 @@ // This is a debug and example purposes Program.cs -using System.Globalization; -using JJMasterData.Core.Configuration; -using JJMasterData.NCalc.Configuration; using JJMasterData.Web.Configuration; using Microsoft.AspNetCore.ResponseCompression; using JJMasterData.Pdf; @@ -25,10 +22,7 @@ options.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.Ignore; }); -builder.Services.AddJJMasterDataWeb(builder.Configuration).WithNCalcExpressionProvider(new() -{ - ReplaceDefaultExpressionProvider = true -}).WithPdfExportation(); +builder.Services.AddJJMasterDataWeb(builder.Configuration).WithPdfExportation(); var app = builder.Build(); diff --git a/example/WebEntryPoint/WebEntryPoint.csproj b/example/WebEntryPoint/WebEntryPoint.csproj index c1f1581d6..b4af68c4b 100644 --- a/example/WebEntryPoint/WebEntryPoint.csproj +++ b/example/WebEntryPoint/WebEntryPoint.csproj @@ -13,7 +13,6 @@ - diff --git a/src/Commons/Commons.csproj b/src/Commons/Commons.csproj index 881a3e4ce..2e79cd7b2 100644 --- a/src/Commons/Commons.csproj +++ b/src/Commons/Commons.csproj @@ -12,22 +12,25 @@ true - - - - - - - - + + all + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/src/Commons/Configuration/MasterDataServiceBuilder.cs b/src/Commons/Configuration/MasterDataServiceBuilder.cs index 0b75b5cf1..cbfcfa8d1 100644 --- a/src/Commons/Configuration/MasterDataServiceBuilder.cs +++ b/src/Commons/Configuration/MasterDataServiceBuilder.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using JJMasterData.Commons.Configuration.Options; using JJMasterData.Commons.Data; -using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Data.Entity.Providers; using JJMasterData.Commons.Data.Entity.Repository.Abstractions; using JJMasterData.Commons.Tasks; diff --git a/src/Commons/Configuration/Options/ConnectionString.cs b/src/Commons/Configuration/Options/ConnectionString.cs index 6502e8354..2a33dcb65 100644 --- a/src/Commons/Configuration/Options/ConnectionString.cs +++ b/src/Commons/Configuration/Options/ConnectionString.cs @@ -12,7 +12,7 @@ public ConnectionString(string connectionString, string connectionProvider) : th } public Guid Guid { get; init; } = Guid.NewGuid(); - public string? Name { get; init; } + public string Name { get; init; } = null!; public string Connection { get; init; } = null!; public string ConnectionProvider { get; init; } = null!; } \ No newline at end of file diff --git a/src/Commons/Configuration/Options/MasterDataCommonsOptions.cs b/src/Commons/Configuration/Options/MasterDataCommonsOptions.cs index 4e8223f5f..2a436bf74 100644 --- a/src/Commons/Configuration/Options/MasterDataCommonsOptions.cs +++ b/src/Commons/Configuration/Options/MasterDataCommonsOptions.cs @@ -64,7 +64,7 @@ public ConnectionString GetConnectionString(Guid? guid) if (guid is null) return new ConnectionString(ConnectionString!, ConnectionProvider.GetAdoNetTypeName()); - var connectionString = AdditionalConnectionStrings.FirstOrDefault(c => c.Guid == guid); + var connectionString = AdditionalConnectionStrings.Find(c => c.Guid == guid); if (connectionString is null) throw new JJMasterDataException($"ConnectionString {guid} does not exist."); diff --git a/src/Commons/Configuration/Options/WritableJsonOptions.cs b/src/Commons/Configuration/Options/WritableJsonOptions.cs index 65fbb576e..f125de1de 100644 --- a/src/Commons/Configuration/Options/WritableJsonOptions.cs +++ b/src/Commons/Configuration/Options/WritableJsonOptions.cs @@ -76,9 +76,9 @@ public async Task UpdateAsync(Action applyChanges) private PropertyInfo[] GetCachedProperties(Type type) { - if (memoryCache.TryGetValue(type, out PropertyInfo[] cachedProperties)) + if (memoryCache.TryGetValue(type, out PropertyInfo[]? cachedProperties)) { - return cachedProperties; + return cachedProperties!; } var properties = type.GetProperties(); diff --git a/src/Commons/Configuration/Options/WritableOptionsExtensions.cs b/src/Commons/Configuration/Options/WritableOptionsExtensions.cs index 738c1fd95..53a742338 100644 --- a/src/Commons/Configuration/Options/WritableOptionsExtensions.cs +++ b/src/Commons/Configuration/Options/WritableOptionsExtensions.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Linq; using JJMasterData.Commons.Configuration.Options.Abstractions; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; diff --git a/src/Commons/Data/DataAccess.cs b/src/Commons/Data/DataAccess.cs index b7c90206d..67c2ac98d 100644 --- a/src/Commons/Data/DataAccess.cs +++ b/src/Commons/Data/DataAccess.cs @@ -7,6 +7,7 @@ using System.Data.Common; using System.Linq; using System.Text; +using JetBrains.Annotations; using JJMasterData.Commons.Configuration.Options; using JJMasterData.Commons.Exceptions; using Microsoft.Extensions.DependencyInjection; @@ -96,7 +97,7 @@ public DataAccess(string connectionString, DataAccessProvider dataAccessProvider ConnectionString = connectionString; ConnectionProvider = dataAccessProvider; } - + [ActivatorUtilitiesConstructor] public DataAccess(IOptionsSnapshot options) { @@ -104,7 +105,8 @@ public DataAccess(IOptionsSnapshot options) ConnectionString = optionsValue.ConnectionString ?? throw new ArgumentNullException(nameof(optionsValue.ConnectionString)); ConnectionProvider = optionsValue.ConnectionProvider; } - + + [MustDisposeResource] public DbConnection GetConnection() { var connection = Factory.CreateConnection(); @@ -139,8 +141,8 @@ public DataTable GetDataTable(DataAccessCommand cmd) ExecuteDataCommand(cmd, dataAdapter => dataAdapter.Fill(dataTable)); return dataTable; } - - + + /// /// Returns a DataSet object populated by a SQL string. Use a if you need parameters. /// @@ -148,7 +150,7 @@ public DataSet GetDataSet(string sql) { return GetDataSet(new DataAccessCommand(sql)); } - + /// /// Returns a DataSet object populated by a . /// @@ -158,7 +160,7 @@ public DataSet GetDataSet(DataAccessCommand cmd) ExecuteDataCommand(cmd, dataAdapter => dataAdapter.Fill(dataSet)); return dataSet; } - + private void ExecuteDataCommand(DataAccessCommand cmd, Action fillAction) { try @@ -171,7 +173,7 @@ private void ExecuteDataCommand(DataAccessCommand cmd, Action fil using var dataAdapter = Factory.CreateDataAdapter(); dataAdapter!.SelectCommand = dbCommand; fillAction(dataAdapter); - + foreach (var parameter in cmd.Parameters) { if (parameter.Direction is ParameterDirection.Output or ParameterDirection.InputOutput) @@ -184,7 +186,7 @@ private void ExecuteDataCommand(DataAccessCommand cmd, Action fil throw GetDataAccessException(ex, cmd); } } - + /// /// Returns a DataTable object populated from a sql. /// @@ -393,8 +395,6 @@ public int SetCommand(DataAccessCommand cmd, ref DbConnection sqlConn, ref DbTra using var dbCommand = CreateDbCommand(cmd); dbCommand.Connection = sqlConn; dbCommand.Transaction = trans; - - using (dbCommand.Connection) { numberOfRowsAffected += dbCommand.ExecuteNonQuery(); @@ -420,7 +420,6 @@ public int SetCommand(DataAccessCommand cmd, ref DbConnection sqlConn, ref DbTra public Hashtable? GetHashtable(string sql) => GetHashtable(new DataAccessCommand(sql)); public Dictionary? GetDictionary(string sql) => GetDictionary(new DataAccessCommand(sql)); - /// /// Retrieves the first record of the sql statement in a Hashtable object. @@ -638,14 +637,13 @@ private static DataAccessCommand GetTableExistsCommand(string table) return command; } - private static Exception GetDataAccessException(Exception ex, DataAccessCommand? cmd) { return GetDataAccessException(ex, cmd?.Sql ?? string.Empty, cmd?.Parameters); } private static Exception GetDataAccessException( - Exception ex, + Exception ex, string sql, List? parameters = null) { @@ -671,12 +669,14 @@ private static Exception GetDataAccessException( } + [MustDisposeResource] private DbCommand CreateDbCommand(DataAccessCommand command) { var dbCommand = Factory.CreateCommand(); if (dbCommand == null) throw new ArgumentNullException(nameof(dbCommand)); - + if (string.IsNullOrEmpty(command.Sql)) + throw new DataAccessException("Sql Command cannot be null or empty."); dbCommand.CommandType = command.Type; dbCommand.CommandText = command.Sql; dbCommand.CommandTimeout = TimeOut; @@ -712,8 +712,8 @@ private static DataAccessCommand GetColumnExistsCommand(string tableName, string @"SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName AND COLUMN_NAME = @ColumnName" }; - command.AddParameter("@TableName", tableName, DbType.String); - command.AddParameter("@ColumnName", columnName, DbType.String); + command.AddParameter("@TableName", tableName, DbType.AnsiString); + command.AddParameter("@ColumnName", columnName, DbType.AnsiString); return command; } @@ -724,14 +724,10 @@ private static DataAccessCommand GetColumnExistsCommand(string tableName, string try { - using var dbCommand = CreateDbCommand(cmd); - dbCommand.Connection = GetConnection(); - - + dbCommand.Connection = GetConnection(); using (dbCommand.Connection) { - using (var dataReader = dbCommand.ExecuteReader()) { var columnNames = Enumerable.Range(0, dataReader.FieldCount) @@ -758,7 +754,6 @@ private static DataAccessCommand GetColumnExistsCommand(string tableName, string { param.Value = dbCommand.Parameters[param.Name].Value; } - } } catch (Exception ex) diff --git a/src/Commons/Data/DataAccessAsync.cs b/src/Commons/Data/DataAccessAsync.cs index 7c7bd7589..bbfda2452 100644 --- a/src/Commons/Data/DataAccessAsync.cs +++ b/src/Commons/Data/DataAccessAsync.cs @@ -8,13 +8,14 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using JJMasterData.Commons.Data.Entity.Models; +using JetBrains.Annotations; using JJMasterData.Commons.Exceptions; namespace JJMasterData.Commons.Data; public partial class DataAccess { + [MustDisposeResource] public async Task GetConnectionAsync(CancellationToken cancellationToken = default) { var connection = Factory.CreateConnection(); @@ -31,7 +32,7 @@ public async Task GetConnectionAsync(CancellationToken cancellatio return connection; } - + /// public Task GetDataTableAsync(string sql, CancellationToken cancellationToken = default) { @@ -45,20 +46,20 @@ public async Task GetDataTableAsync(DataAccessCommand command, Cancel { using var dbCommand = CreateDbCommand(command); dbCommand.Connection = await GetConnectionAsync(cancellationToken); - + using (dbCommand.Connection) { using (var reader = await dbCommand.ExecuteReaderAsync(cancellationToken)) { var dataTable = new DataTable(); dataTable.Load(reader); - + foreach (var parameter in command.Parameters) { if (parameter.Direction is ParameterDirection.Output or ParameterDirection.InputOutput) parameter.Value = dbCommand.Parameters[parameter.Name].Value; } - + return dataTable; } } @@ -68,13 +69,13 @@ public async Task GetDataTableAsync(DataAccessCommand command, Cancel throw GetDataAccessException(ex, command); } } - + /// public Task GetDataSetAsync(string sql) { return GetDataSetAsync(new DataAccessCommand(sql)); } - + /// public async Task GetDataSetAsync(DataAccessCommand command, CancellationToken cancellationToken = default) { @@ -82,7 +83,7 @@ public async Task GetDataSetAsync(DataAccessCommand command, Cancellati { using var dbCommand = CreateDbCommand(command); dbCommand.Connection = await GetConnectionAsync(cancellationToken); - + using (dbCommand.Connection) { using (var reader = await dbCommand.ExecuteReaderAsync( cancellationToken)) @@ -98,13 +99,13 @@ public async Task GetDataSetAsync(DataAccessCommand command, Cancellati dataSet.Tables.Add(dataTable); index++; } while(!reader.IsClosed); - + foreach (var parameter in command.Parameters) { if (parameter.Direction is ParameterDirection.Output or ParameterDirection.InputOutput) parameter.Value = dbCommand.Parameters[parameter.Name].Value; } - + return dataSet; } } @@ -127,15 +128,8 @@ public async Task GetDataSetAsync(DataAccessCommand command, Cancellati object? scalarResult; try { -#if NET - await -#endif using var dbCommand = CreateDbCommand(cmd); dbCommand.Connection = await GetConnectionAsync(cancellationToken); - -#if NET - await -#endif using (dbCommand.Connection) { scalarResult = await dbCommand.ExecuteScalarAsync(cancellationToken); @@ -154,21 +148,15 @@ public async Task GetDataSetAsync(DataAccessCommand command, Cancellati return scalarResult; } - + /// public async Task SetCommandAsync(DataAccessCommand cmd, CancellationToken cancellationToken = default) { int rowsAffected; try { -#if NET - await -#endif using var dbCommand = CreateDbCommand(cmd); dbCommand.Connection = await GetConnectionAsync(cancellationToken); -#if NET - await -#endif using (dbCommand.Connection) { rowsAffected = await dbCommand.ExecuteNonQueryAsync(cancellationToken); @@ -187,7 +175,7 @@ public async Task SetCommandAsync(DataAccessCommand cmd, CancellationToken return rowsAffected; } - + /// public async Task SetCommandListAsync(IEnumerable commands, CancellationToken cancellationToken = default) { @@ -195,58 +183,42 @@ public async Task SetCommandListAsync(IEnumerable comman DataAccessCommand? currentCommand = null; var connection = await GetConnectionAsync(cancellationToken); -#if NET - await -#endif + using (connection) { -#if NET - await using var transaction = await connection.BeginTransactionAsync(cancellationToken); -#else using var transaction = connection.BeginTransaction(); -#endif try { foreach (var command in commands) { currentCommand = command; -#if NET - await -#endif using var dbCommand = CreateDbCommand(command); dbCommand.Connection = connection; dbCommand.Transaction = transaction; numberOfRowsAffected += await dbCommand.ExecuteNonQueryAsync(cancellationToken); } -#if NET - await transaction.CommitAsync(cancellationToken); - -#else + transaction.Commit(); -#endif } catch (Exception ex) { -#if NET - await transaction.RollbackAsync(cancellationToken); -#else transaction.Rollback(); -#endif + throw GetDataAccessException(ex, currentCommand); } } return numberOfRowsAffected; } - + /// public Task SetCommandAsync(string sql, CancellationToken cancellationToken = default) { return SetCommandAsync(new DataAccessCommand(sql), cancellationToken); } - + /// public async Task SetCommandAsync(IEnumerable sqlList, CancellationToken cancellationToken = default) { @@ -255,31 +227,22 @@ public async Task SetCommandAsync(IEnumerable sqlList, Cancellation int numberOfRowsAffected = await SetCommandListAsync(commandList, cancellationToken); return numberOfRowsAffected; } - + /// public Task> GetDictionaryAsync(string sql, CancellationToken cancellationToken = default) => GetDictionaryAsync(new DataAccessCommand(sql), cancellationToken); - - + /// public async Task> GetDictionaryAsync(DataAccessCommand command, CancellationToken cancellationToken = default) { var result = new Dictionary(); try { -#if NET - await -#endif using var dbCommand = CreateDbCommand(command); dbCommand.Connection = await GetConnectionAsync(cancellationToken); -#if NET - await -#endif + using (dbCommand.Connection) { -#if NET - await -#endif using (var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken)) { @@ -294,7 +257,7 @@ await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken) throw new DataAccessException($"[{fieldName}] field duplicated in get procedure"); result.Add(fieldName, dataReader.GetValue(count)); - count += 1; + count++; } } } @@ -320,20 +283,11 @@ await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken) try { -#if NET - await -#endif using var dbCommand = CreateDbCommand(cmd); dbCommand.Connection = await GetConnectionAsync(cancellationToken); -#if NET - await -#endif using (dbCommand.Connection) { -#if NET - await -#endif using (var dataReader = await dbCommand.ExecuteReaderAsync(cancellationToken)) { var columnNames = Enumerable.Range(0, dataReader.FieldCount) @@ -360,7 +314,6 @@ await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken) { param.Value = dbCommand.Parameters[param.Name].Value; } - } } catch (Exception ex) @@ -370,7 +323,7 @@ await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken) return dictionaryList; } - + /// public async Task TableExistsAsync(string tableName, CancellationToken cancellationToken = default) { @@ -378,7 +331,7 @@ public async Task TableExistsAsync(string tableName, CancellationToken can var result = await GetResultAsync(command, cancellationToken); return (int)result! == 1; } - + /// public async Task TryConnectionAsync(CancellationToken cancellationToken = default) { @@ -408,11 +361,7 @@ public async Task TryConnectionAsync(CancellationToken cancell { if (connection.State == ConnectionState.Open) { -#if NET - await connection.CloseAsync(); -#else connection.Close(); -#endif } connection.Dispose(); @@ -421,8 +370,7 @@ public async Task TryConnectionAsync(CancellationToken cancell return new(result, errorMessage); } - - + /// public async Task ExecuteBatchAsync(string script, CancellationToken cancellationToken = default) { @@ -432,7 +380,7 @@ public async Task ExecuteBatchAsync(string script, CancellationToken cance markpar = "/"; } - if (script.Trim().Length <= 0) + if (script.Trim().Length == 0) return true; var sqlList = new List(); @@ -459,27 +407,18 @@ public async Task ExecuteBatchAsync(string script, CancellationToken cance await SetCommandAsync(sqlList, cancellationToken); return true; } - public async Task ColumnExistsAsync(string tableName, string columnName, CancellationToken cancellationToken = default) { var command = GetColumnExistsCommand(tableName, columnName); try { -#if NET - await -#endif using var dbCommand = CreateDbCommand(command); dbCommand.Connection = await GetConnectionAsync(cancellationToken); -#if NET - await -#endif using (dbCommand.Connection) { -#if NET - await -#endif - using (var reader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken)) + using (var reader = + await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow, cancellationToken)) { return await reader.ReadAsync(cancellationToken); } @@ -490,5 +429,4 @@ public async Task ColumnExistsAsync(string tableName, string columnName, C throw GetDataAccessException(ex, command); } } - } \ No newline at end of file diff --git a/src/Commons/Data/DataAccessGenerics.cs b/src/Commons/Data/DataAccessGenerics.cs index a0024d26b..0adf3adf1 100644 --- a/src/Commons/Data/DataAccessGenerics.cs +++ b/src/Commons/Data/DataAccessGenerics.cs @@ -16,7 +16,7 @@ public partial class DataAccess CancellationToken cancellationToken = default) { var fields = await GetDictionaryAsync(cmd, cancellationToken); - return (fields as Dictionary).ToModel(serializerSettings); + return fields.ToModel(serializerSettings); } public IList? GetModelList(DataAccessCommand cmd, JsonSerializerSettings? serializerSettings = null) @@ -31,13 +31,12 @@ public partial class DataAccess var dataTable = await GetDataTableAsync(cmd, cancellationToken); var result = dataTable.ToModelList(serializerSettings); - if (result != null) + if (result == null) + yield break; + foreach (var model in result) { - foreach (var model in result) - { - cancellationToken.ThrowIfCancellationRequested(); - yield return model; - } + cancellationToken.ThrowIfCancellationRequested(); + yield return model; } } } \ No newline at end of file diff --git a/src/Commons/Data/DataAccessParameter.cs b/src/Commons/Data/DataAccessParameter.cs index 50378b882..ba8ec61e5 100644 --- a/src/Commons/Data/DataAccessParameter.cs +++ b/src/Commons/Data/DataAccessParameter.cs @@ -49,7 +49,7 @@ public DataAccessParameter(string name, string value) { Name = name; Value = value; - Type = DbType.String; + Type = DbType.AnsiString; Direction = ParameterDirection.Input; } diff --git a/src/Commons/Data/DataAccessProviderFactory.cs b/src/Commons/Data/DataAccessProviderFactory.cs index 359b06dbb..0c8e226c6 100644 --- a/src/Commons/Data/DataAccessProviderFactory.cs +++ b/src/Commons/Data/DataAccessProviderFactory.cs @@ -2,7 +2,6 @@ using System.ComponentModel; using System.Data.Common; using JJMasterData.Commons.Exceptions; -using JJMasterData.Commons.Extensions; using JJMasterData.Commons.Util; using Microsoft.Data.SqlClient; diff --git a/src/Commons/Data/Entity/Models/Element.cs b/src/Commons/Data/Entity/Models/Element.cs index 9a83a5078..f2f8caf41 100644 --- a/src/Commons/Data/Entity/Models/Element.cs +++ b/src/Commons/Data/Entity/Models/Element.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics; -using System.Linq; -using JJMasterData.Commons.Configuration.Options; using Newtonsoft.Json; namespace JJMasterData.Commons.Data.Entity.Models; @@ -86,6 +84,6 @@ public Element(string name, string description) : this(name) public List GetPrimaryKeys() { - return Fields.Where(x => x.IsPk).ToList(); + return Fields.FindAll(x => x.IsPk); } } \ No newline at end of file diff --git a/src/Commons/Data/Entity/Models/ElementFieldList.cs b/src/Commons/Data/Entity/Models/ElementFieldList.cs index 3e9a6445e..4e3cb886f 100644 --- a/src/Commons/Data/Entity/Models/ElementFieldList.cs +++ b/src/Commons/Data/Entity/Models/ElementFieldList.cs @@ -39,7 +39,7 @@ public bool ContainsKey(string name) { foreach(ElementField field in _list) { - if (field.Name.ToLower().Equals(name.ToLower())) + if (field.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) return true; } @@ -206,7 +206,7 @@ public ElementField this[string fieldName] { foreach (ElementField val in _list) { - if (val.Name.ToLower().Equals(fieldName.ToLower())) + if (val.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase)) return val; } @@ -218,7 +218,7 @@ public ElementField this[string fieldName] for (int i = 0; i < _list.Count; i++) { ElementField e = _list[i]; - if (e.Name.ToLower().Equals(fieldName.ToLower())) + if (e.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase)) { _list[i] = value; isOk = true; @@ -237,6 +237,11 @@ public List GetAsList() { return _list; } + + public List FindAll(Predicate predicate) + { + return _list.FindAll(predicate); + } public ElementFieldList DeepCopy() { diff --git a/src/Commons/Data/Entity/Models/ElementRelationship.cs b/src/Commons/Data/Entity/Models/ElementRelationship.cs index 5e0edbf9c..135dd8695 100644 --- a/src/Commons/Data/Entity/Models/ElementRelationship.cs +++ b/src/Commons/Data/Entity/Models/ElementRelationship.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; using Newtonsoft.Json; namespace JJMasterData.Commons.Data.Entity.Models; diff --git a/src/Commons/Data/Entity/Providers/EntityProviderBase.cs b/src/Commons/Data/Entity/Providers/EntityProviderBase.cs index 3d0f67fbc..c88a06b69 100644 --- a/src/Commons/Data/Entity/Providers/EntityProviderBase.cs +++ b/src/Commons/Data/Entity/Providers/EntityProviderBase.cs @@ -94,10 +94,14 @@ public CommandOperation SetValues(Element element, Dictionary v return GetCommandOperation(element, values, command, commandType, newFields); } - private static CommandOperation GetCommandOperation(Element element, Dictionary values, DataAccessCommand command, - CommandOperation commandType, Dictionary? newFields) + private static CommandOperation GetCommandOperation( + Element element, + Dictionary values, + DataAccessCommand command, + CommandOperation commandType, + Dictionary? newFields) { - var resultParameter = command.Parameters.ToList().First(x => x.Name.Equals("@RET")); + var resultParameter = command.Parameters.First(x => x.Name.Equals("@RET")); if (resultParameter.Value != DBNull.Value) { @@ -105,7 +109,7 @@ private static CommandOperation GetCommandOperation(Element element, Dictionary< { var err = "Element"; err += $" {element.Name}"; - err += ": " + "Invalid return of @RET variable in procedure"; + err += ": Invalid return of @RET variable in procedure"; throw new JJMasterDataException(err); } @@ -115,7 +119,7 @@ private static CommandOperation GetCommandOperation(Element element, Dictionary< if (newFields == null) return commandType; - foreach (var entry in newFields.Where(entry => element.Fields.ContainsKey(entry.Key.ToString()))) + foreach (var entry in newFields.Where(entry => element.Fields.ContainsKey(entry.Key))) { values[entry.Key] = entry.Value; } @@ -264,14 +268,14 @@ private async Task SetValuesNoResultAsync(Element element, Dic private static CommandOperation GetCommandFromValuesNoResult(Element element, DataAccessCommand command, CommandOperation ret) { - var oret = command.Parameters.ToList().First(x => x.Name.Equals("@RET")); - if (oret.Value != DBNull.Value) + var retParameter = command.Parameters.First(x => x.Name.Equals("@RET")); + if (retParameter.Value != DBNull.Value) { - if (!int.TryParse(oret.Value.ToString(), out var result)) + if (!int.TryParse(retParameter.Value.ToString(), out var result)) { string err = "Element"; err += $" {element.Name}"; - err += ": " + "Invalid return of @RET variable in procedure"; + err += ": Invalid return of @RET variable in procedure"; throw new JJMasterDataException(err); } diff --git a/src/Commons/Data/Entity/Providers/OracleProvider.cs b/src/Commons/Data/Entity/Providers/OracleProvider.cs index 434ad3417..d3188a6ec 100644 --- a/src/Commons/Data/Entity/Providers/OracleProvider.cs +++ b/src/Commons/Data/Entity/Providers/OracleProvider.cs @@ -21,7 +21,7 @@ public class OracleProvider( private const string InsertKeyword = "I"; private const string UpdateKeyword = "A"; private const string DeleteKeyword = "E"; - private const string Tab = "\t"; + private const char Tab = '\t'; public override string VariablePrefix => "p_"; public override string GetCreateTableScript(Element element, List? relationships = null) @@ -53,15 +53,14 @@ public override string GetCreateTableScript(Element element, List 0) - sKeys.Append(","); + sKeys.Append(','); sKeys.Append(f.Name); - sKeys.Append(" "); + sKeys.Append(' '); } } @@ -85,7 +84,7 @@ public override string GetCreateTableScript(Element element, List 0) sql.Append(", "); - sql.Append("["); + sql.Append('['); sql.Append(r.Columns[rc].FkColumn); - sql.Append("]"); + sql.Append(']'); } sql.AppendLine(")"); sql.Append(Tab); @@ -210,7 +209,7 @@ private static string GetRelationshipsScript(Element element) sql.Append(r.Columns[rc].PkColumn); } - sql.Append(")"); + sql.Append(')'); if (r.UpdateOnCascade) { @@ -298,15 +297,14 @@ public override string GetWriteProcedureScript(Element element) if (f.IsPk) { sql.AppendLine(""); + sql.Append(Tab).Append(Tab); if (isFirst) { - sql.Append(Tab).Append(Tab); sql.Append("WHERE "); isFirst = false; } else { - sql.Append(Tab).Append(Tab); sql.Append("AND "); } @@ -376,11 +374,11 @@ public override string GetWriteProcedureScript(Element element) //SCRIPT UPDATE isFirst = true; + sql.Append(Tab); + sql.AppendLine($"ELSIF v_TYPEACTION = '{UpdateKeyword}' THEN "); + sql.Append(Tab).Append(Tab); if (hasUpd) { - sql.Append(Tab); - sql.AppendLine($"ELSIF v_TYPEACTION = '{UpdateKeyword}' THEN "); - sql.Append(Tab).Append(Tab); sql.Append("UPDATE "); sql.Append(element.TableName); sql.AppendLine(" SET "); @@ -407,15 +405,14 @@ public override string GetWriteProcedureScript(Element element) if (f.IsPk) { sql.AppendLine(""); + sql.Append(Tab).Append(Tab); if (isFirst) { - sql.Append(Tab).Append(Tab); sql.Append("WHERE "); isFirst = false; } else { - sql.Append(Tab).Append(Tab); sql.Append("AND "); } @@ -426,21 +423,16 @@ public override string GetWriteProcedureScript(Element element) } } sql.AppendLine(";"); - sql.Append(Tab).Append(Tab); - sql.Append(VariablePrefix); - sql.AppendLine("RET := 1; "); } else { - sql.Append(Tab); - sql.AppendLine($"ELSIF v_TYPEACTION = '{UpdateKeyword}' THEN "); - sql.Append(Tab).Append(Tab); sql.AppendLine("--NO UPDATABLED"); - sql.Append(Tab).Append(Tab); - sql.Append(VariablePrefix); - sql.AppendLine("RET := 1; "); } + sql.Append(Tab).Append(Tab); + sql.Append(VariablePrefix); + sql.AppendLine("RET := 1; "); + //SCRIPT DELETE sql.Append(Tab); sql.AppendLine($"ELSIF v_TYPEACTION = '{DeleteKeyword}' THEN "); @@ -454,15 +446,14 @@ public override string GetWriteProcedureScript(Element element) if (f.IsPk) { sql.AppendLine(""); + sql.Append(Tab).Append(Tab); if (isFirst) { - sql.Append(Tab).Append(Tab); sql.Append("WHERE "); isFirst = false; } else { - sql.Append(Tab).Append(Tab); sql.Append("AND "); } @@ -531,7 +522,7 @@ public override string GetReadProcedureScript(Element element) { sql.Append(VariablePrefix); sql.Append(f.Name); - sql.Append(" "); + sql.Append(' '); sql.Append(GetStrType(f.DataType)); sql.AppendLine(", "); } @@ -827,7 +818,7 @@ private DataAccessCommand GetCommandWrite(string action, Element element, Dictio Type = CommandType.StoredProcedure }; cmd.Sql = Options.GetWriteProcedureName(element); - cmd.Parameters.Add(new DataAccessParameter($"{VariablePrefix}action", action, DbType.String, 1)); + cmd.Parameters.Add(new DataAccessParameter($"{VariablePrefix}action", action, DbType.AnsiString, 1)); var fields = element.Fields .ToList() @@ -914,8 +905,7 @@ public override DataAccessCommand GetReadCommand(Element element, EntityParamete else if (field.Filter.Type != FilterMode.None || field.IsPk) { object? value = DBNull.Value; - if (filters != null && - filters.ContainsKey(field.Name) && + if (filters?.ContainsKey(field.Name) == true && filters[field.Name] != null) { value = filters[field.Name]; @@ -977,7 +967,7 @@ private static string GetStrType(FieldType dataType) private static DbType GetDbType(FieldType dataType) { - var t = DbType.String; + var t = DbType.AnsiString; switch (dataType) { case FieldType.Date: diff --git a/src/Commons/Data/Entity/Providers/PlainTextReader.cs b/src/Commons/Data/Entity/Providers/PlainTextReader.cs index c6827e46e..598590b33 100644 --- a/src/Commons/Data/Entity/Providers/PlainTextReader.cs +++ b/src/Commons/Data/Entity/Providers/PlainTextReader.cs @@ -86,13 +86,13 @@ public async Task GetFieldsListAsTextAsync(Element element, EntityParame foreach (ElementField field in element.Fields) { currentField = field.Name; - if (!columns.ContainsKey(field.Name)) + if (!columns.TryGetValue(field.Name, out var value)) throw new JJMasterDataException($"{field.Name} field not found"); if (col > 0) sRet.Append(Delimiter); - int ordinal = columns[field.Name]; + int ordinal = value; if (!dr.IsDBNull(ordinal)) { switch (field.DataType) @@ -146,10 +146,10 @@ public async Task GetFieldsListAsTextAsync(Element element, EntityParame var ts = DateTime.Now - dStart; var logMessage = new StringBuilder(); logMessage.Append("Synchronizing"); - logMessage.Append(" "); + logMessage.Append(' '); logMessage.AppendLine(element.Name); - if (filters.Any()) + if (filters.Count > 0) { logMessage.Append("- "); logMessage.Append("Filters"); @@ -159,7 +159,7 @@ public async Task GetFieldsListAsTextAsync(Element element, EntityParame { logMessage.Append(" "); logMessage.Append(filter.Key); - logMessage.Append("="); + logMessage.Append('='); logMessage.Append(filter.Value); } diff --git a/src/Commons/Data/Entity/Providers/SQLiteProvider.cs b/src/Commons/Data/Entity/Providers/SQLiteProvider.cs index a8e8353b2..e0a9be328 100644 --- a/src/Commons/Data/Entity/Providers/SQLiteProvider.cs +++ b/src/Commons/Data/Entity/Providers/SQLiteProvider.cs @@ -18,7 +18,7 @@ public class SQLiteProvider( ILoggerFactory loggerFactory) : EntityProviderBase(options, loggerFactory) { - private const string Tab = "\t"; + private const char Tab = '\t'; public override string VariablePrefix => "@"; public override string GetCreateTableScript(Element element, List? relationships = null) @@ -48,7 +48,7 @@ public override string GetCreateTableScript(Element element, List 0) sSql.Append(", "); - sSql.Append("["); + sSql.Append('['); sSql.Append(r.Columns[rc].FkColumn); - sSql.Append("]"); + sSql.Append(']'); } sSql.AppendLine(")"); @@ -209,12 +208,12 @@ private string GetRelationshipsScript(Element element) if (rc > 0) sSql.Append(", "); - sSql.Append("["); + sSql.Append('['); sSql.Append(r.Columns[rc].PkColumn); - sSql.Append("]"); + sSql.Append(']'); } - sSql.Append(")"); + sSql.Append(')'); if (r.UpdateOnCascade) { @@ -285,15 +284,14 @@ public override DataAccessCommand GetReadCommand(Element element, EntityParamete foreach (var filter in filters) { + sqlScript.Append(Tab).Append(Tab); if (isFirst) { - sqlScript.Append(Tab).Append(Tab); sqlScript.Append(" WHERE "); isFirst = false; } else { - sqlScript.Append(Tab).Append(Tab); sqlScript.Append("AND "); } @@ -365,7 +363,7 @@ private static DataAccessCommand GetScriptInsert(Element element, Dictionary values) { - if (!values.ContainsKey(f.Name)) + if (!values.TryGetValue(f.Name, out object? value)) return DBNull.Value; - object? val = values[f.Name]; - if (val == null) + if (value == null) { return DBNull.Value; } if (f.DataType is FieldType.Date or FieldType.DateTime or FieldType.Float or FieldType.Int && - string.IsNullOrEmpty(val.ToString())) + string.IsNullOrEmpty(value.ToString())) { return DBNull.Value; } - return val; + return value; } private static DbType GetDbType(FieldType dataType) { - DbType t = DbType.String; + DbType t = DbType.AnsiString; switch (dataType) { case FieldType.Date: @@ -576,7 +571,7 @@ private static DbType GetDbType(FieldType dataType) } // ReSharper disable once UnusedMember.Local - private DataAccessCommand GetScriptCount(Element element, Dictionary filters) + private static DataAccessCommand GetScriptCount(Element element, Dictionary filters) { var fields = element.Fields .ToList() @@ -592,15 +587,14 @@ private DataAccessCommand GetScriptCount(Element element, Dictionary x.DataBehavior == FieldBehavior.Real); bool isFirst = true; @@ -41,9 +40,9 @@ public static string GetCreateTableScript(Element element, List 0) - keys.Append(","); + keys.Append(','); - keys.Append("["); + keys.Append('['); keys.Append(f.Name); keys.Append("] ASC "); } @@ -56,7 +55,7 @@ public static string GetCreateTableScript(Element element, List 0) sql.Append(", "); - sql.Append("["); + sql.Append('['); sql.Append(relationship.Columns[rc].FkColumn); - sql.Append("]"); + sql.Append(']'); } sql.AppendLine(")"); @@ -170,12 +169,12 @@ private static string GetRelationshipsScript(Element element, List 0) sql.Append(", "); - sql.Append("["); + sql.Append('['); sql.Append(relationship.Columns[rc].PkColumn); - sql.Append("]"); + sql.Append(']'); } - sql.Append(")"); + sql.Append(')'); if (relationship.UpdateOnCascade) { diff --git a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerProvider.cs b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerProvider.cs index 8b727b7ac..eeb967748 100644 --- a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerProvider.cs +++ b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerProvider.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Linq; using System.Threading.Tasks; using JJMasterData.Commons.Configuration.Options; using JJMasterData.Commons.Data.Entity.Models; @@ -82,9 +81,9 @@ public override DataAccessCommand GetReadCommand(Element element, EntityParamete else { var cacheKey = $"{element.Name}_ReadScript"; - if (MemoryCache.TryGetValue(cacheKey, out string readScript)) + if (MemoryCache.TryGetValue(cacheKey, out string? readScript)) { - sql = readScript; + sql = readScript!; } else { @@ -177,9 +176,9 @@ private DataAccessCommand GetWriteCommand(string action, Element element, Dictio else { var cacheKey = $"{element.Name}_WriteScript"; - if (MemoryCache.TryGetValue(cacheKey, out string writeScript)) + if (MemoryCache.TryGetValue(cacheKey, out string? writeScript)) { - sql = writeScript; + sql = writeScript!; } else { @@ -192,10 +191,9 @@ private DataAccessCommand GetWriteCommand(string action, Element element, Dictio Type = element.UseWriteProcedure ? CommandType.StoredProcedure : CommandType.Text, Sql = sql }; - writeCommand.Parameters.Add(new DataAccessParameter("@action", action, DbType.String, 1)); + writeCommand.Parameters.Add(new DataAccessParameter("@action", action, DbType.AnsiString, 1)); var fields = element.Fields - .ToList() .FindAll(x => x.DataBehavior is FieldBehavior.Real or FieldBehavior.WriteOnly); foreach (var field in fields) @@ -265,7 +263,10 @@ private static DbType GetDbType(FieldType dataType) FieldType.Int => DbType.Int32, FieldType.Bit => DbType.Boolean, FieldType.UniqueIdentifier => DbType.Guid, - _ => DbType.String + FieldType.NVarchar => DbType.String, + FieldType.NText => DbType.String, + FieldType.Varchar => DbType.AnsiString, + _ => DbType.AnsiString }; } @@ -276,50 +277,22 @@ private static FieldType GetDataType(string databaseType) databaseType = databaseType.ToLower().Trim(); - if (databaseType.Equals("varchar") || - databaseType.Equals("char")) - return FieldType.Varchar; - - if (databaseType.Equals("bit")) - return FieldType.Bit; - - if (databaseType.Equals("nvarchar") || - databaseType.Equals("nchar")) - return FieldType.NVarchar; - - if (databaseType.Equals("int") || - databaseType.Equals("int identity") || - databaseType.Equals("bigint") || - databaseType.Equals("tinyint")) - return FieldType.Int; - - if (databaseType.Equals("float") || - databaseType.Equals("decimal") || - databaseType.Equals("numeric") || - databaseType.Equals("money") || - databaseType.Equals("smallmoney") || - databaseType.Equals("real")) - return FieldType.Float; - - if (databaseType.Equals("time")) - return FieldType.Time; - - if (databaseType.Equals("date")) - return FieldType.Date; - - if (databaseType.Equals("datetime")) - return FieldType.DateTime; - - if (databaseType.Equals("datetime2")) - return FieldType.DateTime2; - - if (databaseType.Equals("text")) - return FieldType.Text; - - if (databaseType.Equals("uniqueidentifier")) - return FieldType.UniqueIdentifier; - - return databaseType.Equals("ntext") ? FieldType.NText : FieldType.NVarchar; + return databaseType switch + { + "varchar" or "char" => FieldType.Varchar, + "bit" => FieldType.Bit, + "nvarchar" or "nchar" => FieldType.NVarchar, + "int" or "int identity" or "bigint" or "tinyint" => FieldType.Int, + "float" or "decimal" or "numeric" or "money" or "smallmoney" or "real" => FieldType.Float, + "time" => FieldType.Time, + "date" => FieldType.Date, + "datetime" => FieldType.DateTime, + "datetime2" => FieldType.DateTime2, + "text" => FieldType.Text, + "ntext" => FieldType.NText, + "uniqueidentifier" => FieldType.UniqueIdentifier, + _ => FieldType.Varchar + }; } public override async Task GetElementFromTableAsync(string tableName, Guid? connectionId = null) @@ -357,7 +330,7 @@ public override async Task GetElementFromTableAsync(string tableName, G Name = row["COLUMN_NAME"].ToString()!.Replace(" ","_"), Label = (string)row["COLUMN_NAME"], Size = (int)row["LENGTH"], - AutoNum = ((string)row["TYPE_NAME"]).ToUpper().Contains("IDENTITY"), + AutoNum = ((string)row["TYPE_NAME"]).IndexOf("IDENTITY", StringComparison.OrdinalIgnoreCase) >= 0, IsRequired = row["NULLABLE"].ToString()?.Equals("0") ?? false, DataType = GetDataType((string)row["TYPE_NAME"]) }; diff --git a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerReadProcedureScripts.cs b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerReadProcedureScripts.cs index 449bfa163..604318517 100644 --- a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerReadProcedureScripts.cs +++ b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerReadProcedureScripts.cs @@ -231,15 +231,15 @@ internal string GetReadScript(Element element, List fields) { sql.Append("SET @sqlOrderBy = ' ORDER BY "); sql.Append($"[{fields[0].Name}]"); - sql.AppendLine("'"); } else { sql.Append("SET @sqlOrderBy = ' ORDER BY "); sql.Append($"[{listPk[0].Name}]"); - sql.AppendLine("'"); } + sql.AppendLine("'"); + sql.Append(Tab); sql.AppendLine("IF @orderby IS NOT NULL AND @orderby <> ''"); sql.Append(Tab); @@ -313,8 +313,8 @@ internal string GetReadScript(Element element, List fields) sql.Append(Tab); sql.Append("N'"); sql.Append(GetParameters(fields, addMasterDataParameters: true, tabLevel: 1)); - sql.Append("'"); - sql.Append(","); + sql.Append('\''); + sql.Append(','); sql.AppendLine(); sql.Append(Tab); sql.AppendLine(); @@ -398,13 +398,13 @@ private static string GetFilterParametersScript(IEnumerable fields sql.AppendLine($"@{field.Name}_from,"); sql.Append(Tab, tabCount); sql.AppendLine($"@{field.Name}_to,"); - sql.Append(Tab, tabCount); } else { sql.AppendLine($"@{field.Name},"); - sql.Append(Tab, tabCount); } + + sql.Append(Tab, tabCount); } return sql.ToString(); @@ -425,26 +425,26 @@ private static string GetParameters(List fields, bool addMasterDat { case FilterMode.Range: { - sql.Append("@"); + sql.Append('@'); sql.Append(field.Name); sql.Append("_from "); sql.Append(field.DataType.ToString()); if (field.DataType is FieldType.Varchar or FieldType.NVarchar or FieldType.DateTime2) { - sql.Append("("); + sql.Append('('); sql.Append(field.Size == -1 ? "MAX" : field.Size); - sql.Append(")"); + sql.Append(')'); } sql.AppendLine(","); sql.Append(Tab, tabLevel); - sql.Append("@"); + sql.Append('@'); sql.Append(field.Name); sql.Append("_to "); sql.Append(field.DataType.ToString()); if (field.DataType is FieldType.Varchar or FieldType.NVarchar or FieldType.DateTime2) { - sql.Append("("); + sql.Append('('); sql.Append(field.Size == -1 ? "MAX" : field.Size); sql.Append(") "); } @@ -453,9 +453,9 @@ private static string GetParameters(List fields, bool addMasterDat break; } case FilterMode.MultValuesContain or FilterMode.MultValuesEqual: - sql.Append("@"); + sql.Append('@'); sql.Append(field.Name); - sql.Append(" "); + sql.Append(' '); sql.Append("VARCHAR"); sql.AppendLine("(MAX),"); sql.Append(Tab, tabLevel); @@ -464,22 +464,22 @@ private static string GetParameters(List fields, bool addMasterDat { if (IsFilter(field)) { - sql.Append("@"); + sql.Append('@'); sql.Append(field.Name); - sql.Append(" "); + sql.Append(' '); sql.Append(field.DataType.ToString()); if (field.DataType is FieldType.Varchar or FieldType.NVarchar or FieldType.DateTime2) { - sql.Append("("); + sql.Append('('); sql.Append(field.Size == -1 ? "MAX" : field.Size); sql.AppendLine("), "); - sql.Append(Tab, tabLevel); } else { sql.AppendLine(", "); - sql.Append(Tab, tabLevel); } + + sql.Append(Tab, tabLevel); } break; diff --git a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerScripts.cs b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerScripts.cs index 75263cd81..74879f765 100644 --- a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerScripts.cs +++ b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerScripts.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using JJMasterData.Commons.Data.Entity.Models; namespace JJMasterData.Commons.Data.Entity.Providers; @@ -24,17 +23,13 @@ public string GetWriteProcedureScript(Element element) public static string GetWriteScript(Element element) { - var fields = element.Fields - .ToList() - .FindAll(x => x.DataBehavior is FieldBehavior.Real); + var fields = element.Fields.FindAll(x => x.DataBehavior is FieldBehavior.Real); return SqlServerWriteProcedureScripts.GetWriteScript(element, fields); } public string GetReadScript(Element element) { - var fields = element.Fields - .ToList() - .FindAll(f => f.DataBehavior is FieldBehavior.Real); + var fields = element.Fields.FindAll(f => f.DataBehavior is FieldBehavior.Real); return ReadProcedureScripts.GetReadScript(element, fields); } diff --git a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerScriptsBase.cs b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerScriptsBase.cs index 2007b9301..4f934a5bc 100644 --- a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerScriptsBase.cs +++ b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerScriptsBase.cs @@ -21,7 +21,7 @@ public static string GetFieldDataTypeScript(ElementField field) { sql.Append(" ("); sql.Append(field.Size == -1 ? "MAX" : field.Size); - sql.Append(")"); + sql.Append(')'); } if (field.IsRequired) @@ -36,7 +36,7 @@ public static string GetFieldDataTypeScript(ElementField field) protected static string GetFieldDefinition(ElementField field) { var sql = new StringBuilder(); - sql.Append("["); + sql.Append('['); sql.Append(field.Name); sql.Append("] "); @@ -45,7 +45,7 @@ protected static string GetFieldDefinition(ElementField field) return sql.ToString(); } - public string GetSqlDropIfExists(string objname) + public static string GetSqlDropIfExists(string objname) { var sql = new StringBuilder(); sql.AppendLine("IF EXISTS (SELECT * "); diff --git a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerWriteProcedureScripts.cs b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerWriteProcedureScripts.cs index 1fc942354..9f8d6d11e 100644 --- a/src/Commons/Data/Entity/Providers/SqlServer/SqlServerWriteProcedureScripts.cs +++ b/src/Commons/Data/Entity/Providers/SqlServer/SqlServerWriteProcedureScripts.cs @@ -45,20 +45,19 @@ public string GetWriteProcedureScript(Element element) sql.AppendLine("@action varchar(1), "); var fields = element.Fields - .Where(f => f.DataBehavior is FieldBehavior.Real or FieldBehavior.WriteOnly) - .ToList(); + .FindAll(f => f.DataBehavior is FieldBehavior.Real or FieldBehavior.WriteOnly); foreach (var field in fields) { - sql.Append("@"); + sql.Append('@'); sql.Append(field.Name); - sql.Append(" "); + sql.Append(' '); sql.Append(field.DataType); if (field.DataType is FieldType.Varchar or FieldType.NVarchar or FieldType.DateTime2) { - sql.Append("("); + sql.Append('('); sql.Append(field.Size == -1 ? "MAX" : field.Size); - sql.Append(")"); + sql.Append(')'); } if (!field.IsRequired) @@ -112,15 +111,14 @@ internal static string GetWriteScript(Element element, IReadOnlyCollection x.AutoNum); - if (autonumericFields.Any()) + if (autonumericFields.Count > 0) { sql.Append(Tab, 2); sql.Append("OUTPUT "); @@ -185,7 +183,7 @@ internal static string GetWriteScript(Element element, IReadOnlyCollection 0) { - sql.Append(","); + sql.Append(','); } sql.Append("Inserted." + fieldName); @@ -204,7 +202,7 @@ internal static string GetWriteScript(Element element, IReadOnlyCollection f.IsPk)) { + sql.Append(Tab).Append(Tab); if (isFirst) { - sql.Append(Tab).Append(Tab); sql.Append("WHERE "); isFirst = false; } else { - sql.Append(Tab).Append(Tab); sql.Append("AND "); } @@ -262,26 +259,17 @@ internal static string GetWriteScript(Element element, IReadOnlyCollection f.IsPk && f.EnableOnDelete)) { + sql.Append(Tab).Append(Tab); if (isFirst) { - sql.Append(Tab).Append(Tab); sql.Append("WHERE "); isFirst = false; } else { - sql.Append(Tab).Append(Tab); sql.Append("AND "); } diff --git a/src/Commons/Data/Entity/Repository/EntityRepository.cs b/src/Commons/Data/Entity/Repository/EntityRepository.cs index 1d57dd4c0..eaa874f1e 100644 --- a/src/Commons/Data/Entity/Repository/EntityRepository.cs +++ b/src/Commons/Data/Entity/Repository/EntityRepository.cs @@ -106,7 +106,7 @@ public Task ExecuteBatchAsync(string script, Guid? connectionId = null) public Dictionary GetFields(Element element, Dictionary primaryKeys) { var dataAccess = GetDataAccess(element.ConnectionId); - if (!primaryKeys.Any()) + if (primaryKeys.Count == 0) throw new ArgumentException("Your need at least one value at your primary keys.", nameof(primaryKeys)); var totalOfRecords = @@ -125,16 +125,16 @@ public Task ExecuteBatchAsync(string script, Guid? connectionId = null) return dataAccess.GetDictionary(command) ?? new Dictionary(); } - public async Task> GetFieldsAsync(DataAccessCommand command, Guid? connectionId = null) + public Task> GetFieldsAsync(DataAccessCommand command, Guid? connectionId = null) { var dataAccess = GetDataAccess(connectionId); - return await dataAccess.GetDictionaryAsync(command); + return dataAccess.GetDictionaryAsync(command); } - public async Task> GetFieldsAsync(Element element, + public Task> GetFieldsAsync(Element element, Dictionary primaryKeys) { - if (!primaryKeys.Any()) + if (primaryKeys.Count == 0) throw new ArgumentException("Your need at least one value at your primary keys.", nameof(primaryKeys)); var totalOfRecords = @@ -146,7 +146,7 @@ public Task ExecuteBatchAsync(string script, Guid? connectionId = null) var dataAccess = GetDataAccess(element.ConnectionId); - return await dataAccess.GetDictionaryAsync(cmd); + return dataAccess.GetDictionaryAsync(cmd); } public Task CreateDataModelAsync(Element element, List? relationships = null) => diff --git a/src/Commons/Data/Entity/Repository/OrderByData.cs b/src/Commons/Data/Entity/Repository/OrderByData.cs index 3414657e0..e95c4a04b 100644 --- a/src/Commons/Data/Entity/Repository/OrderByData.cs +++ b/src/Commons/Data/Entity/Repository/OrderByData.cs @@ -1,7 +1,7 @@ #nullable enable using System; using System.Collections.Generic; -using System.Linq; +using System.Text; using JJMasterData.Commons.Util; namespace JJMasterData.Commons.Data.Entity.Repository; @@ -10,24 +10,23 @@ public class OrderByData { private Dictionary Fields { get; } = new(); - public bool Any() => Fields.Any(); + public bool Any() => Fields.Count > 0; - public string? ToQueryParameter() + public string ToQueryParameter() { - string? queryParameter = null; + var queryParameter = new StringBuilder(); foreach (var field in Fields) { - if (queryParameter != null) + if (queryParameter.Length > 0) { - queryParameter += ","; + queryParameter.Append(','); } - queryParameter += $"{field.Key} {field.Value.ToString().ToUpper()}"; - + queryParameter.Append($"{field.Key} {field.Value.GetSqlString()}"); } - return queryParameter; + return queryParameter.ToString(); } public OrderByData AddOrReplace(string fieldName, OrderByDirection direction) @@ -58,22 +57,28 @@ public void Set(string? orderBy) foreach (var field in fields) { - string[] fieldParts = field.Split(' '); + var fieldParts = field.Split(' '); - var directionStr = OrderByDirection.Asc.ToString(); + var directionStr = nameof(OrderByDirection.Asc); if(fieldParts.Length == 2) - directionStr = fieldParts[1].ToUpper(); + directionStr = fieldParts[1].ToUpperInvariant(); var fieldName = fieldParts[0]; - - if (!Enum.TryParse(directionStr.ToLower().FirstCharToUpper(), out OrderByDirection direction) || - !Enum.IsDefined(typeof(OrderByDirection), direction)) - { - throw new ArgumentException("Invalid value for Direction in orderBy."); - } + + var direction = ParseDirection(directionStr); Fields[fieldName] = direction; } } + + private static OrderByDirection ParseDirection(string directionStr) + { + return directionStr.ToLowerInvariant() switch + { + "asc" => OrderByDirection.Asc, + "desc" => OrderByDirection.Desc, + _ => throw new ArgumentException("Invalid OrderBy direction.") + }; + } } \ No newline at end of file diff --git a/src/Commons/Data/Entity/Repository/OrderByDirection.cs b/src/Commons/Data/Entity/Repository/OrderByDirection.cs index b5f0dc351..70f59b1cd 100644 --- a/src/Commons/Data/Entity/Repository/OrderByDirection.cs +++ b/src/Commons/Data/Entity/Repository/OrderByDirection.cs @@ -1,8 +1,22 @@ -#nullable enable +using System; + namespace JJMasterData.Commons.Data.Entity.Repository; public enum OrderByDirection { Asc, Desc +} + +public static class OrderByDirectionExtensions +{ + public static string GetSqlString(this OrderByDirection direction) + { + return direction switch + { + OrderByDirection.Asc => "ASC", + OrderByDirection.Desc => "DESC", + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } } \ No newline at end of file diff --git a/src/Commons/Extensions/DictionaryExtensions.cs b/src/Commons/Extensions/DictionaryExtensions.cs index 22ae068fe..51920c70c 100644 --- a/src/Commons/Extensions/DictionaryExtensions.cs +++ b/src/Commons/Extensions/DictionaryExtensions.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace JJMasterData.Commons.Extensions; internal static class DictionaryExtensions { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TValue GetValueOrDefault(this IDictionary dictionary, TKey key, TValue defaultValue) { return dictionary.TryGetValue(key, out var value) ? value : defaultValue; diff --git a/src/Commons/Extensions/EnumExtensions.cs b/src/Commons/Extensions/EnumExtensions.cs index dd62dd034..217acb4ce 100644 --- a/src/Commons/Extensions/EnumExtensions.cs +++ b/src/Commons/Extensions/EnumExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; diff --git a/src/Commons/Extensions/LinqExtensions.cs b/src/Commons/Extensions/LinqExtensions.cs index 54ce5bc22..f418b265e 100644 --- a/src/Commons/Extensions/LinqExtensions.cs +++ b/src/Commons/Extensions/LinqExtensions.cs @@ -11,7 +11,7 @@ public static class LinqExtensions private static PropertyInfo GetPropertyInfo(Type objType, string name) { var properties = objType.GetProperties(); - var matchedProperty = properties.FirstOrDefault (p => p.Name.ToLower().Equals(name.ToLower())); + var matchedProperty = properties.FirstOrDefault (p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); if (matchedProperty == null) throw new ArgumentException("name"); diff --git a/src/Commons/Localization/MasterDataResources.pt-BR.resx b/src/Commons/Localization/MasterDataResources.pt-BR.resx index 967769e4b..f1b0f3b2a 100644 --- a/src/Commons/Localization/MasterDataResources.pt-BR.resx +++ b/src/Commons/Localization/MasterDataResources.pt-BR.resx @@ -125,10 +125,6 @@ (Selecione) JJMasterDataWeb - - (Digite Ctrl + Espaço para AutoCompletar) - JJMasterDataWeb - ({0} caracteres restantes) JJMasterDataCore @@ -193,6 +189,10 @@ Um registro selecionado JJMasterDataApi + + Redireciona para esta URL após o comando ser executado com sucesso. + JJMasterDataWeb + Sobre JJMasterDataWeb @@ -205,6 +205,22 @@ Ação JJMasterDataCore + + Url de Redirecionamento + JJMasterDataWeb + + + Este campo oferece suporte a valores do runtime entre {}. Use {AppPath} para o caminho da raiz da aplicação. + JJMasterDataWeb + + + É Modal? + JJMasterDataWeb + + + É Iframe? + JJMasterDataWeb + Texto de confirmação antes de executar a ação JJMasterDataWeb @@ -4082,7 +4098,7 @@ Padrão da Aplicação - Operação Realizada! + Operação Realizada Mostra uma label flutuando dentro do campo. Nem todos os campos suportam esta opção. @@ -4090,10 +4106,22 @@ Template de Renderização - - Template HTML para renderizar o campo na grid. Valores do runtime são suportados entre chaves. + + Template HTML para renderizar o campo na grid. Valores do runtime são suportados usando um template Liquid. Editar Elemento + + Fonte de Dados + + + Template HTML + + + (Utilize Ctrl+Espaço para autocompletar) + + + Aqui usamos um template Liquid para renderizar o HTML. Use a propriedade DataSource para acessar suas tabelas e colunas. + diff --git a/src/Commons/Localization/MasterDataResources.zh-CN.resx b/src/Commons/Localization/MasterDataResources.zh-CN.resx index b977b08b8..b34209022 100644 --- a/src/Commons/Localization/MasterDataResources.zh-CN.resx +++ b/src/Commons/Localization/MasterDataResources.zh-CN.resx @@ -129,7 +129,7 @@ (选择) JJMasterDataWeb - + (按下 Ctrl+Space 以自动完成) JJMasterDataWeb diff --git a/src/Commons/Localization/MasterDataStringLocalizer.cs b/src/Commons/Localization/MasterDataStringLocalizer.cs index 3886c3dda..75ce07c53 100644 --- a/src/Commons/Localization/MasterDataStringLocalizer.cs +++ b/src/Commons/Localization/MasterDataStringLocalizer.cs @@ -13,158 +13,143 @@ using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; - namespace JJMasterData.Commons.Localization; -public sealed class MasterDataStringLocalizer : IStringLocalizer +public sealed class MasterDataStringLocalizer(IStringLocalizerFactory factory) + : IStringLocalizer { - private readonly IStringLocalizer _localizer; - - public MasterDataStringLocalizer(IStringLocalizerFactory factory) - { - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - _localizer = factory.Create(typeof(TResourceSource)); - } - - /// - public LocalizedString this[string? name] => _localizer[name!]; - - /// - public LocalizedString this[string? name, params object[] arguments] => _localizer[name!, arguments]; - - /// - public IEnumerable GetAllStrings(bool includeParentCultures) => - _localizer.GetAllStrings(includeParentCultures); -} + private readonly IStringLocalizer _localizer = factory.Create(typeof(TResourceSource)); + + /// + public LocalizedString this[string? name] => _localizer[name!]; + /// + public LocalizedString this[string? name, params object[] arguments] => _localizer[name!, arguments]; + + /// + public IEnumerable GetAllStrings(bool includeParentCultures) => + _localizer.GetAllStrings(includeParentCultures); +} public class MasterDataStringLocalizer( - string resourceName, - ResourceManagerStringLocalizer resourcesStringLocalizer, - IEntityRepository entityRepository, - IMemoryCache cache, - IOptionsMonitor options) - : IStringLocalizer + string resourceName, + ResourceManagerStringLocalizer resourcesStringLocalizer, + IEntityRepository entityRepository, + IMemoryCache cache, + IOptionsMonitor options) + : IStringLocalizer { - private ResourceManagerStringLocalizer ResourceManagerStringLocalizer { get; } = resourcesStringLocalizer; - private IEntityRepository EntityRepository { get; } = entityRepository; - private IMemoryCache Cache { get; } = cache; - private IOptionsMonitor Options { get; } = options; - - private string ResourceName { get; } = resourceName; - - public IEnumerable GetAllStrings(bool includeParentCultures) - { - return GetAllStringsAsDictionary().Select(e=>new LocalizedString(e.Key, e.Value)); - } - - public LocalizedString this[string? name] - { - get - { - if (name == null) - return new LocalizedString(string.Empty,string.Empty, true); - - var value = GetString(name); - return new LocalizedString(name, value, false); - } - } - - public LocalizedString this[string? name, params object[] arguments] - { - get - { - if (name == null) - return new LocalizedString(string.Empty,string.Empty); - - return new LocalizedString(name, string.Format(this[name], arguments)); - } - } - - - private string GetString(string key) - { - if (string.IsNullOrEmpty(key)) - return key; - - var culture = Thread.CurrentThread.CurrentCulture.Name; - var cacheKey = $"{ResourceName}_localization_strings_{culture}"; - - if (Cache.TryGetValue>(cacheKey, out var cachedDictionary)) - { - return cachedDictionary.GetValueOrDefault(key, key); - } - - var localizedStrings = GetAllStringsAsDictionary(); - - Cache.Set(cacheKey, localizedStrings); - - return localizedStrings.GetValueOrDefault(key, key); - } - - private FrozenDictionary GetAllStringsAsDictionary() - { - string culture = Thread.CurrentThread.CurrentCulture.Name; - - var element = MasterDataStringLocalizerElement.GetElement(Options.CurrentValue); - - var hasConnectionString = !string.IsNullOrEmpty(Options.CurrentValue.ConnectionString); - - var tableExists = hasConnectionString && EntityRepository.TableExists(element.TableName); - - if (!tableExists && hasConnectionString) - EntityRepository.CreateDataModel(element,[]); - - var stringLocalizerValues = GetStringLocalizerValues(); - var databaseValues = hasConnectionString ? GetDatabaseValues(element, culture) : new Dictionary(); - - if (databaseValues.Count > 0) - { - foreach (var dbValue in databaseValues.ToList()) - { - stringLocalizerValues[dbValue.Key] = dbValue.Value?.ToString() ?? string.Empty; - } - } - - - return stringLocalizerValues.ToFrozenDictionary(); - } - - - private Dictionary GetStringLocalizerValues() - { - try - { - var values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - var localizedStrings = ResourceManagerStringLocalizer.GetAllStrings(); - - foreach (var localizedString in localizedStrings) - { - values.Add(localizedString.Name, localizedString.Value); - } - - return values; - } - catch - { - return new Dictionary(); - } - } - - private Dictionary GetDatabaseValues(Element element, string culture) - { - var values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - var filter = new Dictionary { { "cultureCode", culture} }; - var result =EntityRepository.GetDictionaryListResult(element, new EntityParameters {Filters = filter},false); - foreach (var row in result.Data) - { - values.Add(row["resourceKey"]!.ToString()!, row["resourceValue"]?.ToString()); - } - - return values; - } + public IEnumerable GetAllStrings(bool includeParentCultures) + { + return GetAllStringsAsDictionary().Select(e => new LocalizedString(e.Key, e.Value)); + } + + public LocalizedString this[string? name] + { + get + { + if (name == null) + return new LocalizedString(string.Empty, string.Empty, true); + + var value = GetString(name); + return new LocalizedString(name, value, false); + } + } + + public LocalizedString this[string? name, params object[] arguments] + { + get + { + if (name == null) + return new LocalizedString(string.Empty, string.Empty); + + return new LocalizedString(name, string.Format(this[name], arguments)); + } + } + + + private string GetString(string key) + { + if (string.IsNullOrEmpty(key)) + return key; + + var culture = Thread.CurrentThread.CurrentCulture.Name; + var cacheKey = $"{resourceName}_localization_strings_{culture}"; + + if (cache.TryGetValue>(cacheKey, out var cachedDictionary)) + { + return cachedDictionary.GetValueOrDefault(key, key); + } + + var localizedStrings = GetAllStringsAsDictionary(); + + cache.Set(cacheKey, localizedStrings); + + return localizedStrings.GetValueOrDefault(key, key); + } + + private FrozenDictionary GetAllStringsAsDictionary() + { + var culture = Thread.CurrentThread.CurrentCulture.Name; + + var element = MasterDataStringLocalizerElement.GetElement(options.CurrentValue); + + var hasConnectionString = !string.IsNullOrEmpty(options.CurrentValue.ConnectionString); + + var tableExists = hasConnectionString && entityRepository.TableExists(element.TableName); + + if (!tableExists && hasConnectionString) + entityRepository.CreateDataModel(element, []); + + var stringLocalizerValues = GetStringLocalizerValues(); + var databaseValues = + hasConnectionString ? GetDatabaseValues(element, culture) : new Dictionary(); + + if (databaseValues.Count > 0) + { + foreach (var dbValue in databaseValues.ToList()) + { + stringLocalizerValues[dbValue.Key] = dbValue.Value?.ToString() ?? string.Empty; + } + } + + + return stringLocalizerValues.ToFrozenDictionary(); + } + + + private Dictionary GetStringLocalizerValues() + { + try + { + var values = new Dictionary(StringComparer.Ordinal); + var localizedStrings = resourcesStringLocalizer.GetAllStrings(); + + foreach (var localizedString in localizedStrings) + { + if(!values.ContainsKey(localizedString.Name)) + values.Add(localizedString.Name, localizedString.Value); + } + + return values; + } + catch + { + return new Dictionary(); + } + } + + private Dictionary GetDatabaseValues(Element element, string culture) + { + var values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + var filter = new Dictionary { { "cultureCode", culture } }; + var result = + entityRepository.GetDictionaryListResult(element, new EntityParameters { Filters = filter }, false); + foreach (var row in result.Data) + { + values.Add(row["resourceKey"]!.ToString()!, row["resourceValue"]?.ToString()); + } + + return values; + } } \ No newline at end of file diff --git a/src/Commons/Localization/MasterDataStringLocalizerFactory.cs b/src/Commons/Localization/MasterDataStringLocalizerFactory.cs index 735ec7748..b316ed943 100644 --- a/src/Commons/Localization/MasterDataStringLocalizerFactory.cs +++ b/src/Commons/Localization/MasterDataStringLocalizerFactory.cs @@ -8,13 +8,13 @@ namespace JJMasterData.Commons.Localization; -public class MasterDataStringLocalizerFactory : IStringLocalizerFactory, IDisposable +public sealed class MasterDataStringLocalizerFactory : IStringLocalizerFactory { private ResourceManagerStringLocalizerFactory ResourceManagerStringLocalizerFactory { get; } private IEntityRepository EntityRepository { get; } private IMemoryCache Cache { get; } private IOptionsMonitor Options { get; } - + public MasterDataStringLocalizerFactory( ResourceManagerStringLocalizerFactory resourceManagerStringLocalizerFactory, IMemoryCache cache, @@ -39,6 +39,4 @@ public IStringLocalizer Create(string baseName, string location) var resourceLocalizer = (ResourceManagerStringLocalizer)ResourceManagerStringLocalizerFactory.Create(baseName,location); return new MasterDataStringLocalizer($"{baseName}_{location}", resourceLocalizer, EntityRepository, Cache, Options); } - - public void Dispose() => Cache?.Dispose(); } \ No newline at end of file diff --git a/src/Commons/Logging/Db/DbLogger.cs b/src/Commons/Logging/Db/DbLogger.cs index d18b9dd76..1585804f9 100644 --- a/src/Commons/Logging/Db/DbLogger.cs +++ b/src/Commons/Logging/Db/DbLogger.cs @@ -6,9 +6,9 @@ namespace JJMasterData.Commons.Logging.Db; -internal class DbLogger(string categoryName, LoggerBuffer loggerBuffer) : ILogger +internal sealed class DbLogger(string categoryName, LoggerBuffer loggerBuffer) : ILogger { - public IDisposable BeginScope(TState state) => default!; + IDisposable ILogger.BeginScope(TState state1) => default!; public bool IsEnabled(LogLevel logLevel) { diff --git a/src/Commons/Logging/Db/DbLoggerBackgroundService.cs b/src/Commons/Logging/Db/DbLoggerBackgroundService.cs index 5b0a7b867..2b653aad9 100644 --- a/src/Commons/Logging/Db/DbLoggerBackgroundService.cs +++ b/src/Commons/Logging/Db/DbLoggerBackgroundService.cs @@ -26,7 +26,7 @@ protected override async Task LogAsync(LogMessage entry, CancellationToken cance var entityRepository = scope.ServiceProvider.GetRequiredService(); if (!TableExists) { - if (!await entityRepository.TableExistsAsync(options.TableName, null)) + if (!await entityRepository.TableExistsAsync(options.TableName, options.ConnectionStringId)) { await entityRepository.CreateDataModelAsync(element,[]); } diff --git a/src/Commons/Logging/Db/DbLoggerOptions.cs b/src/Commons/Logging/Db/DbLoggerOptions.cs index 7440fcbff..11979adcd 100644 --- a/src/Commons/Logging/Db/DbLoggerOptions.cs +++ b/src/Commons/Logging/Db/DbLoggerOptions.cs @@ -1,12 +1,14 @@ -namespace JJMasterData.Commons.Logging.Db; +using System; + +namespace JJMasterData.Commons.Logging.Db; public class DbLoggerOptions { + public Guid? ConnectionStringId { get; set; } public string TableName { get; set; } = "tb_masterdata_log"; public string IdColumnName { get; set; } = "Id"; - public string ConnectionStringName { get; set; } = "ConnectionString"; public string CreatedColumnName { get; set; } = "log_dat_evento"; public string LevelColumnName { get; set; } = "log_txt_tipo"; public string MessageColumnName { get; set; } = "log_txt_message"; diff --git a/src/Commons/Logging/Db/DbLoggerProvider.cs b/src/Commons/Logging/Db/DbLoggerProvider.cs index eee6dcd52..ef781270a 100644 --- a/src/Commons/Logging/Db/DbLoggerProvider.cs +++ b/src/Commons/Logging/Db/DbLoggerProvider.cs @@ -5,7 +5,7 @@ namespace JJMasterData.Commons.Logging.Db; [ProviderAlias(ProviderName)] -internal class DbLoggerProvider(DbLoggerBuffer buffer) : ILoggerProvider +internal sealed class DbLoggerProvider(DbLoggerBuffer buffer) : ILoggerProvider { private const string ProviderName = "Database"; diff --git a/src/Commons/Logging/File/FileLogger.cs b/src/Commons/Logging/File/FileLogger.cs index 8ac49742c..e0ceb1715 100644 --- a/src/Commons/Logging/File/FileLogger.cs +++ b/src/Commons/Logging/File/FileLogger.cs @@ -7,7 +7,7 @@ namespace JJMasterData.Commons.Logging.File; -internal class FileLogger( +internal sealed class FileLogger( string categoryName, LoggerBuffer buffer, IOptionsMonitor options) : ILogger @@ -61,9 +61,9 @@ private string GetMessage(LogLevel logLevel, EventId eventId, Exception exceptio case FileLoggerFormatting.Default: log.Append(DateTime.Now); - log.Append(" "); + log.Append(' '); log.Append($" [{categoryName}] "); - log.Append("("); + log.Append('('); log.Append(logLevel.ToString()); log.AppendLine(")"); log.Append(formatterMessage); diff --git a/src/Commons/Logging/File/FileLoggerProvider.cs b/src/Commons/Logging/File/FileLoggerProvider.cs index 8fae33632..5256f111b 100644 --- a/src/Commons/Logging/File/FileLoggerProvider.cs +++ b/src/Commons/Logging/File/FileLoggerProvider.cs @@ -6,7 +6,7 @@ namespace JJMasterData.Commons.Logging.File; [ProviderAlias(ProviderName)] -internal class FileLoggerProvider(FileLoggerBuffer buffer, IOptionsMonitor options) +internal sealed class FileLoggerProvider(FileLoggerBuffer buffer, IOptionsMonitor options) : ILoggerProvider { private const string ProviderName = "File"; diff --git a/src/Commons/Logging/LoggerDecoration.cs b/src/Commons/Logging/LoggerDecoration.cs index cc9440aa7..f48a02b8b 100644 --- a/src/Commons/Logging/LoggerDecoration.cs +++ b/src/Commons/Logging/LoggerDecoration.cs @@ -8,38 +8,42 @@ namespace JJMasterData.Commons.Logging; internal static class LoggerDecoration { - public static string GetMessageException(Exception exception) { var message = new StringBuilder(); - message.Append("Message: "); - message.AppendLine(); - message.AppendLine(exception.Message); + if (!string.IsNullOrEmpty(exception.Message)) + { + message.AppendLine("Message:"); + message.AppendLine(exception.Message); + } + if (exception.InnerException != null) { - message.AppendLine(); - message.Append("InnerException: "); + message.AppendLine("Inner Exception:"); message.AppendLine(exception.InnerException.Message); } - if (exception.Data?.Count > 0) + if (exception.Data.Count > 0) { message.Append(GetDataAccessMessage(exception)); } - message.AppendLine(); - message.AppendLine("StackTrace:"); - message.AppendLine(exception.StackTrace); - - message.AppendLine(); - message.AppendLine("Source:"); - message.AppendLine(exception.Source); + if (!string.IsNullOrEmpty(exception.StackTrace)) + { + message.AppendLine("StackTrace:"); + message.AppendLine(exception.StackTrace); + } + if (!string.IsNullOrEmpty(exception.Source)) + { + message.AppendLine("Source:"); + message.AppendLine(exception.Source); + } + return message.ToString(); } - - + private static string GetDataAccessMessage(Exception exception) { var message = new StringBuilder(); @@ -54,12 +58,10 @@ private static string GetDataAccessMessage(Exception exception) message.AppendLine(); message.Append(key); - message.AppendLine(": "); + message.AppendLine(":"); message.Append(data.Value); } return message.ToString(); } - } - diff --git a/src/Commons/Security/Cryptography/AesEncryptionAlgorithm.cs b/src/Commons/Security/Cryptography/AesEncryptionAlgorithm.cs index 4ef7695ed..9b3b9be29 100644 --- a/src/Commons/Security/Cryptography/AesEncryptionAlgorithm.cs +++ b/src/Commons/Security/Cryptography/AesEncryptionAlgorithm.cs @@ -57,9 +57,9 @@ private Aes CreateAes(string secretKey) Aes? aes = null; try { - if (memoryCache != null && memoryCache.TryGetValue(secretKey, out AesEntry aesEntry)) + if (memoryCache != null && memoryCache.TryGetValue(secretKey, out AesEntry? aesEntry)) { - aes = CreateAes(aesEntry); + aes = CreateAes(aesEntry!); return aes; } diff --git a/src/Commons/Tasks/AsyncEventHandler.cs b/src/Commons/Tasks/AsyncEventHandler.cs index 036a6e20a..6aad00d1d 100644 --- a/src/Commons/Tasks/AsyncEventHandler.cs +++ b/src/Commons/Tasks/AsyncEventHandler.cs @@ -3,4 +3,4 @@ namespace JJMasterData.Commons.Tasks; -public delegate Task AsyncEventHandler(object sender, TEventArgs e); +public delegate ValueTask AsyncEventHandler(object sender, TEventArgs e); diff --git a/src/Commons/Tasks/TaskWrapper.cs b/src/Commons/Tasks/TaskWrapper.cs index d3dbc73b9..0cc015f0c 100644 --- a/src/Commons/Tasks/TaskWrapper.cs +++ b/src/Commons/Tasks/TaskWrapper.cs @@ -4,7 +4,7 @@ namespace JJMasterData.Commons.Tasks; -internal class TaskWrapper +internal sealed class TaskWrapper { public string Key { get; internal set; } diff --git a/src/Commons/Util/Email.cs b/src/Commons/Util/Email.cs index 2fe33234d..c3681eb1c 100644 --- a/src/Commons/Util/Email.cs +++ b/src/Commons/Util/Email.cs @@ -7,7 +7,7 @@ namespace JJMasterData.Commons.Util; -public class Email +public static class Email { public static bool SendMail(string toEmail, string ccEmail, string ccoEmail, string subject, string body, bool IsBodyHtml) @@ -77,59 +77,62 @@ public static bool SendMailConfig(string toEmail, string ccEmail, string ccoEmai try { - msgMail = new MailMessage(); - if (!string.IsNullOrEmpty(toEmail)) + using (msgMail = new MailMessage()) { - msgMail.To.Add(toEmail.Replace(";", ",").Replace("\r\n", "").ToLower()); - } - - if (!string.IsNullOrEmpty(ccEmail)) - { - msgMail.CC.Add(ccEmail.Replace(";", ",").ToLower()); - } - - if (!string.IsNullOrEmpty(ccoEmail)) - { - msgMail.Bcc.Add(ccoEmail.Replace(";", ",").ToLower()); - } - - msgMail.Attachments.Clear(); - if (listAttach != null) - { - foreach (var atach in listAttach) - msgMail.Attachments.Add(atach); - } - - msgMail.Subject = subject; - msgMail.IsBodyHtml = IsBodyHtml; - msgMail.Body = body; - msgMail.SubjectEncoding = Encoding.GetEncoding("ISO-8859-1"); - - //msgMail.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure; - - oSmtp = new SmtpClient(); - - if (smtpconfig != null) - { - oSmtp.Host = smtpconfig.Server; - oSmtp.Port = smtpconfig.Port; - oSmtp.UseDefaultCredentials = false; - - NetworkCredential cred = new NetworkCredential(smtpconfig.User, smtpconfig.Password); - oSmtp.Credentials = cred; - oSmtp.DeliveryMethod = SmtpDeliveryMethod.Network; - oSmtp.EnableSsl = smtpconfig.EnableSSL; - - MailAddress mailfrom = new MailAddress(smtpconfig.Email); - msgMail.From = mailfrom; - } - - oSmtp.Send(msgMail); - - if (listAttach != null) - { - foreach (Attachment attachment in msgMail.Attachments) - attachment.Dispose(); + if (!string.IsNullOrEmpty(toEmail)) + { + msgMail.To.Add(toEmail.Replace(";", ",").Replace("\r\n", "").ToLower()); + } + + if (!string.IsNullOrEmpty(ccEmail)) + { + msgMail.CC.Add(ccEmail.Replace(";", ",").ToLower()); + } + + if (!string.IsNullOrEmpty(ccoEmail)) + { + msgMail.Bcc.Add(ccoEmail.Replace(";", ",").ToLower()); + } + + msgMail.Attachments.Clear(); + if (listAttach != null) + { + foreach (var atach in listAttach) + msgMail.Attachments.Add(atach); + } + + msgMail.Subject = subject; + msgMail.IsBodyHtml = IsBodyHtml; + msgMail.Body = body; + msgMail.SubjectEncoding = Encoding.GetEncoding("ISO-8859-1"); + + //msgMail.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure; + + using (oSmtp = new SmtpClient()) + { + if (smtpconfig != null) + { + oSmtp.Host = smtpconfig.Server; + oSmtp.Port = smtpconfig.Port; + oSmtp.UseDefaultCredentials = false; + + NetworkCredential cred = new NetworkCredential(smtpconfig.User, smtpconfig.Password); + oSmtp.Credentials = cred; + oSmtp.DeliveryMethod = SmtpDeliveryMethod.Network; + oSmtp.EnableSsl = smtpconfig.EnableSSL; + + MailAddress mailfrom = new MailAddress(smtpconfig.Email); + msgMail.From = mailfrom; + } + + oSmtp.Send(msgMail); + + if (listAttach != null) + { + foreach (Attachment attachment in msgMail.Attachments) + attachment.Dispose(); + } + } } } catch (Exception ex) diff --git a/src/Commons/Util/EnumerableHelper.cs b/src/Commons/Util/EnumerableHelper.cs index a8cef2d5b..6edd3f7ce 100644 --- a/src/Commons/Util/EnumerableHelper.cs +++ b/src/Commons/Util/EnumerableHelper.cs @@ -10,7 +10,19 @@ namespace JJMasterData.Commons.Util; public static class EnumerableHelper { + public static List>[] ConvertDataSetToArray(DataSet dataSet) + { + var result = new List>>(); + + foreach (DataTable table in dataSet.Tables) + { + result.Add(ConvertToDictionaryList(table)); + } + + return result.ToArray(); + } + public static List> ConvertToDictionaryList(DataTable dataTable) { var list = new List>(); diff --git a/src/Commons/Util/Format.cs b/src/Commons/Util/Format.cs index 68894ee30..bcd1e1728 100644 --- a/src/Commons/Util/Format.cs +++ b/src/Commons/Util/Format.cs @@ -9,9 +9,9 @@ public static class Format public static string DateFormat => DateTimeFormatInfo.CurrentInfo.ShortDatePattern; - public static string TimeFormat => DateTimeFormatInfo.CurrentInfo.LongTimePattern; + public static string TimeFormat => DateTimeFormatInfo.CurrentInfo.ShortTimePattern; - public static string DateTimeFormat => $"{DateFormat} {DateTimeFormatInfo.CurrentInfo.LongTimePattern}"; + public static string DateTimeFormat => $"{DateFormat} {DateTimeFormatInfo.CurrentInfo.ShortTimePattern}"; public static string FormatTimeSpan(DateTime dtFrom, DateTime dtTo) { var ts = dtTo - dtFrom; diff --git a/src/Commons/Util/ReflectionUtils.cs b/src/Commons/Util/ReflectionUtils.cs index e860eca8c..d842a4de6 100644 --- a/src/Commons/Util/ReflectionUtils.cs +++ b/src/Commons/Util/ReflectionUtils.cs @@ -6,7 +6,7 @@ namespace JJMasterData.Commons.Util; -public class ReflectionUtils +public static class ReflectionUtils { /// diff --git a/src/Commons/Util/StringManager.cs b/src/Commons/Util/StringManager.cs index a4fbc1676..58fe947d5 100644 --- a/src/Commons/Util/StringManager.cs +++ b/src/Commons/Util/StringManager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using JJMasterData.Commons.Localization; using Microsoft.Extensions.Localization; @@ -13,13 +14,16 @@ namespace JJMasterData.Commons.Util; public static class StringManager { public static bool ParseBool(object? value) + { + return ParseBool(value?.ToString()); + } + + public static bool ParseBool(string? value) { if (value == null) return false; - - var stringValue = value.ToString()?.ToLower(); - return stringValue switch + return value.ToLowerInvariant() switch { "true" => true, "1" => true, @@ -529,7 +533,7 @@ public static string FirstCharToUpper(this string input) }; #else if (!string.IsNullOrEmpty(input)) - return input.First().ToString().ToUpper() + input[1..].ToLower(); + return input[0].ToString().ToUpper() + input[1..].ToLower(); return input; #endif diff --git a/src/Commons/Util/XmlHelper.cs b/src/Commons/Util/XmlHelper.cs index ba88b3fb3..5fe14fbe1 100644 --- a/src/Commons/Util/XmlHelper.cs +++ b/src/Commons/Util/XmlHelper.cs @@ -6,7 +6,7 @@ namespace JJMasterData.Commons.Util; -public class XmlHelper +public static class XmlHelper { private static string RemoveText(Match m) { return ""; } @@ -32,7 +32,7 @@ public static string Serialize(object value) if (value == null) throw new ArgumentNullException(nameof(value), "Serialize"); - var stringwriter = new Utf8StringWriter(); + using var stringwriter = new Utf8StringWriter(); var serializer = new XmlSerializer(value.GetType()); serializer.Serialize(stringwriter, value); return stringwriter.ToString(); diff --git a/src/Commons/Validations/ValidateBrazil.cs b/src/Commons/Validations/ValidateBrazil.cs index 3f2511f65..e9ebec7ca 100644 --- a/src/Commons/Validations/ValidateBrazil.cs +++ b/src/Commons/Validations/ValidateBrazil.cs @@ -28,12 +28,12 @@ public static bool ValidIE(string pUF, string pInscr) return false; } - if ((pInscr.Trim().ToUpper() == "ISENTO")) + if (string.Equals(pInscr.Trim(), "ISENTO", StringComparison.OrdinalIgnoreCase)) return true; for (intPos = 1; intPos <= pInscr.Trim().Length; intPos++) { - if ((("0123456789P".IndexOf(pInscr.Substring((intPos - 1), 1), 0, StringComparison.OrdinalIgnoreCase) + 1) > 0)) + if (("0123456789P".IndexOf(pInscr.Substring((intPos - 1), 1), 0, StringComparison.OrdinalIgnoreCase) + 1) > 0) strOrigem = (strOrigem + pInscr.Substring((intPos - 1), 1)); } diff --git a/src/Core/Configuration/ActionsServiceExtensions.cs b/src/Core/Configuration/ActionsServiceExtensions.cs index 6c52147c8..3cf383e32 100644 --- a/src/Core/Configuration/ActionsServiceExtensions.cs +++ b/src/Core/Configuration/ActionsServiceExtensions.cs @@ -1,3 +1,5 @@ +using Fluid; +using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.UI.Components; using Microsoft.Extensions.DependencyInjection; @@ -7,6 +9,12 @@ public static class ActionsServiceExtensions { public static IServiceCollection AddActionServices(this IServiceCollection services) { + services.AddSingleton(_=>new FluidParser(new FluidParserOptions + { + AllowFunctions = true + })); + + services.AddScoped(); services.AddScoped(); return services; } diff --git a/src/Core/Configuration/DataManagerServiceExtensions.cs b/src/Core/Configuration/DataManagerServiceExtensions.cs index 2ef2c45c3..24884133b 100644 --- a/src/Core/Configuration/DataManagerServiceExtensions.cs +++ b/src/Core/Configuration/DataManagerServiceExtensions.cs @@ -15,7 +15,6 @@ public static IServiceCollection AddDataManagerServices(this IServiceCollection services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/src/Core/Configuration/ExpressionsServiceExtensions.cs b/src/Core/Configuration/ExpressionsServiceExtensions.cs index f9772a01f..e17a5e71d 100644 --- a/src/Core/Configuration/ExpressionsServiceExtensions.cs +++ b/src/Core/Configuration/ExpressionsServiceExtensions.cs @@ -2,14 +2,17 @@ using JJMasterData.Core.DataManager.Expressions.Abstractions; using JJMasterData.Core.DataManager.Expressions.Providers; using Microsoft.Extensions.DependencyInjection; +using NCalc.Cache.Configuration; +using NCalc.DependencyInjection; namespace JJMasterData.Core.Configuration; - public static class ExpressionsServiceExtensions { public static IServiceCollection AddExpressionServices(this IServiceCollection services) { + services.AddNCalc().WithMemoryCache(); + services.AddTransient(); services.AddTransient(); @@ -19,4 +22,4 @@ public static IServiceCollection AddExpressionServices(this IServiceCollection s return services; } -} +} \ No newline at end of file diff --git a/src/Core/Configuration/Options/MasterDataCoreOptions.cs b/src/Core/Configuration/Options/MasterDataCoreOptions.cs index bc0744fde..f303e6948 100644 --- a/src/Core/Configuration/Options/MasterDataCoreOptions.cs +++ b/src/Core/Configuration/Options/MasterDataCoreOptions.cs @@ -1,8 +1,12 @@ #nullable enable +using System; +using System.Collections.Frozen; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using JJMasterData.Commons.Util; +using NCalc; namespace JJMasterData.Core.Configuration.Options; @@ -37,4 +41,35 @@ public sealed class MasterDataCoreOptions /// [Display(Name = "Exportation Folder Path")] public string ExportationFolderPath { get; set; } = Path.Combine(FileIO.GetApplicationPath(), "JJExportationFiles"); + + /// + /// Context of expressions starting with "exp:". Declare here custom parameters and functions. + /// + public ExpressionContext ExpressionContext { get; set; } = new() + { + Options = ExpressionOptions.IgnoreCaseAtBuiltInFunctions + | ExpressionOptions.AllowNullParameter + | ExpressionOptions.OrdinalStringComparer + | ExpressionOptions.CaseInsensitiveStringComparer, + Functions = new Dictionary + { + { "now", _ => DateTime.Now } + }.ToFrozenDictionary() + }; + + /// + /// Context of async expressions starting with "exp:". Declare here custom parameters and functions. + /// + public AsyncExpressionContext AsyncExpressionsContext { get; set; } = new() + { + Options = ExpressionOptions.IgnoreCaseAtBuiltInFunctions + | ExpressionOptions.AllowNullParameter + | ExpressionOptions.OrdinalStringComparer + | ExpressionOptions.CaseInsensitiveStringComparer, + Functions = new Dictionary + { + { "now", _ => new(DateTime.Now) } + }.ToFrozenDictionary() + }; + } \ No newline at end of file diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 48ce6f13b..99eaa9f63 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -26,12 +26,18 @@ + + + + + + - + all diff --git a/src/Core/DataDictionary/Models/Actions/BasicAction.cs b/src/Core/DataDictionary/Models/Actions/BasicAction.cs index 806b87d8b..9a6550c83 100644 --- a/src/Core/DataDictionary/Models/Actions/BasicAction.cs +++ b/src/Core/DataDictionary/Models/Actions/BasicAction.cs @@ -78,6 +78,7 @@ public string Name /// Display grid title /// [JsonProperty("showTitle")] + [Display(Name = "Show Title")] public bool ShowTitle { get; set; } /// @@ -86,6 +87,7 @@ public string Name /// Sim = Action will be executed /// [JsonProperty("confirmationMessage")] + [Display(Name = "Confirmation Message")] public string ConfirmationMessage { get; set; } /// @@ -189,7 +191,5 @@ public void SetOptions(BasicAction action) ShowAsButton = action.ShowAsButton; CssClass = action.CssClass; } - - protected BasicAction CopyAction() => (BasicAction)MemberwiseClone(); public abstract BasicAction DeepCopy(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/FormElementActionList.cs b/src/Core/DataDictionary/Models/Actions/FormElementActionList.cs index da978bcb2..6172f020a 100644 --- a/src/Core/DataDictionary/Models/Actions/FormElementActionList.cs +++ b/src/Core/DataDictionary/Models/Actions/FormElementActionList.cs @@ -37,9 +37,9 @@ public BasicAction Get(string actionName) return List.First(a => a.Name == actionName); } - public BasicAction? GetOrDefault(string actionName) + public TAction? GetOrDefault(string actionName) where TAction : BasicAction { - return List.FirstOrDefault(a => a.Name == actionName); + return List.OfType().FirstOrDefault(a => a.Name == actionName); } public List GetAllSorted() @@ -68,7 +68,7 @@ public void AddRange(IEnumerable items) public void Set(BasicAction item) { - var existingAction = List.FirstOrDefault(a => a.Name == item.Name); + var existingAction = List.Find(a => a.Name == item.Name); if (existingAction != null) { var index = List.IndexOf(existingAction); @@ -123,17 +123,27 @@ public void RemoveAll(Predicate match) List.RemoveAll(match); } + public List FindAll(Predicate match) + { + return List.FindAll(match); + } + public BasicAction this[int index] { get => List[index]; set => List[index] = value; } - protected void EnsureActionExists() where T : BasicAction, new() - { - if (List.OfType().FirstOrDefault() == null) - { - List.Add(new T()); - } + protected T EnsureActionExists() where T : BasicAction, new() + { + var action = List.OfType().FirstOrDefault(); + + if (action != null) + return action; + + action = new T(); + List.Add(action); + + return action; } } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/FormElementFieldActionList.cs b/src/Core/DataDictionary/Models/Actions/FormElementFieldActionList.cs index e18896d53..c1b44a512 100644 --- a/src/Core/DataDictionary/Models/Actions/FormElementFieldActionList.cs +++ b/src/Core/DataDictionary/Models/Actions/FormElementFieldActionList.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json; namespace JJMasterData.Core.DataDictionary.Models.Actions; public sealed class FormElementFieldActionList : FormElementActionList { - public FormElementFieldActionList() : base() + public FormElementFieldActionList() { } diff --git a/src/Core/DataDictionary/Models/Actions/FormToolbar/AuditLogFormToolbarAction.cs b/src/Core/DataDictionary/Models/Actions/FormToolbar/AuditLogFormToolbarAction.cs index 1a54e89eb..288ca2248 100644 --- a/src/Core/DataDictionary/Models/Actions/FormToolbar/AuditLogFormToolbarAction.cs +++ b/src/Core/DataDictionary/Models/Actions/FormToolbar/AuditLogFormToolbarAction.cs @@ -18,5 +18,5 @@ public AuditLogFormToolbarAction() Location = FormToolbarActionLocation.Panel; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/FormToolbar/BackAction.cs b/src/Core/DataDictionary/Models/Actions/FormToolbar/BackAction.cs index 971586459..75737952a 100644 --- a/src/Core/DataDictionary/Models/Actions/FormToolbar/BackAction.cs +++ b/src/Core/DataDictionary/Models/Actions/FormToolbar/BackAction.cs @@ -20,5 +20,5 @@ public BackAction() Order = 0; Text = "Back"; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/FormToolbar/CancelAction.cs b/src/Core/DataDictionary/Models/Actions/FormToolbar/CancelAction.cs index 77996e34d..3524a3b50 100644 --- a/src/Core/DataDictionary/Models/Actions/FormToolbar/CancelAction.cs +++ b/src/Core/DataDictionary/Models/Actions/FormToolbar/CancelAction.cs @@ -20,5 +20,5 @@ public CancelAction() ShowAsButton = true; Text = "Cancel"; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/FormToolbar/FormEditAction.cs b/src/Core/DataDictionary/Models/Actions/FormToolbar/FormEditAction.cs index eefaf510b..0576ec795 100644 --- a/src/Core/DataDictionary/Models/Actions/FormToolbar/FormEditAction.cs +++ b/src/Core/DataDictionary/Models/Actions/FormToolbar/FormEditAction.cs @@ -20,5 +20,5 @@ public FormEditAction() Order = 0; Text = "Edit"; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/FormToolbar/SaveAction.cs b/src/Core/DataDictionary/Models/Actions/FormToolbar/SaveAction.cs index dd2fadc82..4078a1d37 100644 --- a/src/Core/DataDictionary/Models/Actions/FormToolbar/SaveAction.cs +++ b/src/Core/DataDictionary/Models/Actions/FormToolbar/SaveAction.cs @@ -29,5 +29,5 @@ public SaveAction() ShowAsButton = true; VisibleExpression = "exp: '{PageState}' <> 'View'"; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/FormToolbarActionList.cs b/src/Core/DataDictionary/Models/Actions/FormToolbarActionList.cs index b079d1c25..d4a31ba85 100644 --- a/src/Core/DataDictionary/Models/Actions/FormToolbarActionList.cs +++ b/src/Core/DataDictionary/Models/Actions/FormToolbarActionList.cs @@ -1,38 +1,44 @@ using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json; namespace JJMasterData.Core.DataDictionary.Models.Actions; public sealed class FormToolbarActionList : FormElementActionList { - public SaveAction SaveAction => List.First(a => a is SaveAction) as SaveAction; - - public BackAction BackAction => List.First(a => a is BackAction) as BackAction; - - public CancelAction CancelAction => List.First(a => a is CancelAction) as CancelAction; - - public FormEditAction FormEditAction => List.First(a => a is FormEditAction) as FormEditAction; - public AuditLogFormToolbarAction AuditLogFormToolbarAction => List.First(a => a is AuditLogFormToolbarAction) as AuditLogFormToolbarAction; - public FormToolbarActionList() + public SaveAction SaveAction { get; } + public BackAction BackAction { get; } + public CancelAction CancelAction { get; } + public FormEditAction FormEditAction { get; } + public AuditLogFormToolbarAction AuditLogFormToolbarAction { get; } + + public FormToolbarActionList() { - List.Add(new SaveAction()); - List.Add(new CancelAction()); - List.Add(new BackAction()); - List.Add(new FormEditAction()); + SaveAction = new SaveAction(); + CancelAction = new CancelAction(); + BackAction = new BackAction(); + FormEditAction = new FormEditAction(); + AuditLogFormToolbarAction = new AuditLogFormToolbarAction(); + + List.AddRange([ + SaveAction, + CancelAction, + BackAction, + FormEditAction, + AuditLogFormToolbarAction + ]); } - + [JsonConstructor] private FormToolbarActionList(List list) { List = list; - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); + SaveAction = EnsureActionExists(); + CancelAction = EnsureActionExists(); + BackAction = EnsureActionExists(); + FormEditAction = EnsureActionExists(); + AuditLogFormToolbarAction = EnsureActionExists(); } - public FormToolbarActionList DeepCopy() { return new FormToolbarActionList(List.ConvertAll(a=>a.DeepCopy())); diff --git a/src/Core/DataDictionary/Models/Actions/GridTable/DeleteAction.cs b/src/Core/DataDictionary/Models/Actions/GridTable/DeleteAction.cs index ab49e5754..bf1951bf3 100644 --- a/src/Core/DataDictionary/Models/Actions/GridTable/DeleteAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridTable/DeleteAction.cs @@ -23,5 +23,5 @@ public DeleteAction() Icon = IconType.SolidTrashCan; Order = 3; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridTable/EditAction.cs b/src/Core/DataDictionary/Models/Actions/GridTable/EditAction.cs index b383a8dc4..fa2a980c2 100644 --- a/src/Core/DataDictionary/Models/Actions/GridTable/EditAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridTable/EditAction.cs @@ -24,6 +24,6 @@ public EditAction() Icon = IconType.Pencil; Order = 2; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridTable/InsertSelectionAction.cs b/src/Core/DataDictionary/Models/Actions/GridTable/InsertSelectionAction.cs index 97fc6e39e..258308950 100644 --- a/src/Core/DataDictionary/Models/Actions/GridTable/InsertSelectionAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridTable/InsertSelectionAction.cs @@ -14,5 +14,5 @@ internal InsertSelectionAction() Icon = IconType.CaretRight; Order = -1; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridTable/ViewAction.cs b/src/Core/DataDictionary/Models/Actions/GridTable/ViewAction.cs index 1b5cf252f..865fba009 100644 --- a/src/Core/DataDictionary/Models/Actions/GridTable/ViewAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridTable/ViewAction.cs @@ -25,7 +25,7 @@ public ViewAction() Icon = IconType.Eye; Order = 1; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); diff --git a/src/Core/DataDictionary/Models/Actions/GridTableActionList.cs b/src/Core/DataDictionary/Models/Actions/GridTableActionList.cs index af8d54668..947147924 100644 --- a/src/Core/DataDictionary/Models/Actions/GridTableActionList.cs +++ b/src/Core/DataDictionary/Models/Actions/GridTableActionList.cs @@ -1,31 +1,35 @@ using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json; namespace JJMasterData.Core.DataDictionary.Models.Actions; - public sealed class GridTableActionList : FormElementActionList { - public DeleteAction DeleteAction => List.First(a => a is DeleteAction) as DeleteAction; - public EditAction EditAction => List.First(a => a is EditAction) as EditAction; - public ViewAction ViewAction => List.First(a => a is ViewAction) as ViewAction; - + public DeleteAction DeleteAction { get; } + public EditAction EditAction { get; } + public ViewAction ViewAction { get; } + public GridTableActionList() { - List.Add(new DeleteAction()); - List.Add(new EditAction()); - List.Add(new ViewAction()); + DeleteAction = new DeleteAction(); + EditAction = new EditAction(); + ViewAction = new ViewAction(); + + List.AddRange([ + DeleteAction, + EditAction, + ViewAction + ]); } - + [JsonConstructor] private GridTableActionList(List list) { List = list; - - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); + + DeleteAction = EnsureActionExists(); + EditAction = EnsureActionExists(); + ViewAction = EnsureActionExists(); } public GridTableActionList DeepCopy() diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/AuditLogGridToolbarAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/AuditLogGridToolbarAction.cs index 0b82c59bc..3c9633031 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/AuditLogGridToolbarAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/AuditLogGridToolbarAction.cs @@ -13,5 +13,5 @@ public AuditLogGridToolbarAction() Order = 20; SetVisible(false); } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/ConfigAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/ConfigAction.cs index 24c240a83..431f54374 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/ConfigAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/ConfigAction.cs @@ -16,5 +16,5 @@ public ConfigAction() CssClass = "float-end"; Order = 2; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/ExportAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/ExportAction.cs index 1d2d4861b..2b2e4a399 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/ExportAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/ExportAction.cs @@ -32,7 +32,7 @@ public ExportAction() public override BasicAction DeepCopy() { - var newAction = (ExportAction)CopyAction(); + var newAction = (ExportAction)MemberwiseClone(); newAction.FileName = FileName; newAction.ProcessOptions = ProcessOptions.DeepCopy(); return newAction; diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/FilterAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/FilterAction.cs index 0d15696ef..ce3014f8d 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/FilterAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/FilterAction.cs @@ -65,5 +65,5 @@ public FilterAction() ExpandedByDefault = false; EnableScreenSearch = false; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/ImportAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/ImportAction.cs index d0592fe3b..40e795a6e 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/ImportAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/ImportAction.cs @@ -27,7 +27,7 @@ public ImportAction() } public override BasicAction DeepCopy() { - var newAction = (ImportAction)CopyAction(); + var newAction = (ImportAction)MemberwiseClone(); newAction.HelpText = HelpText; newAction.ProcessOptions = ProcessOptions.DeepCopy(); return newAction; diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/InsertAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/InsertAction.cs index a607ae344..2173b04cf 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/InsertAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/InsertAction.cs @@ -49,5 +49,5 @@ public InsertAction() ShowAsButton = true; Order = 1; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/LegendAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/LegendAction.cs index 228bdf618..74db4b7f4 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/LegendAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/LegendAction.cs @@ -13,5 +13,5 @@ public LegendAction() Order = 7; SetVisible(false); } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/RefreshAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/RefreshAction.cs index 9df29ce0c..84d98aa9d 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/RefreshAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/RefreshAction.cs @@ -12,5 +12,5 @@ public RefreshAction() CssClass = "float-end"; Order = 6; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbar/SortAction.cs b/src/Core/DataDictionary/Models/Actions/GridToolbar/SortAction.cs index 3ccb5c925..b6bf954d7 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbar/SortAction.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbar/SortAction.cs @@ -14,5 +14,5 @@ public SortAction() Order = 7; SetVisible(false); } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/GridToolbarActionList.cs b/src/Core/DataDictionary/Models/Actions/GridToolbarActionList.cs index 897517e18..0089fe1df 100644 --- a/src/Core/DataDictionary/Models/Actions/GridToolbarActionList.cs +++ b/src/Core/DataDictionary/Models/Actions/GridToolbarActionList.cs @@ -1,52 +1,63 @@ using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json; namespace JJMasterData.Core.DataDictionary.Models.Actions; public sealed class GridToolbarActionList : FormElementActionList { - public InsertAction InsertAction => List.FirstOrDefault(a => a is InsertAction) as InsertAction; - public LegendAction LegendAction => List.FirstOrDefault(a => a is LegendAction) as LegendAction; - public RefreshAction RefreshAction => List.FirstOrDefault(a => a is RefreshAction) as RefreshAction; - public FilterAction FilterAction => List.FirstOrDefault(a => a is FilterAction) as FilterAction; - public ImportAction ImportAction => List.FirstOrDefault(a=>a is ImportAction) as ImportAction; - public ExportAction ExportAction => List.FirstOrDefault(a =>a is ExportAction) as ExportAction; - public ConfigAction ConfigAction => List.FirstOrDefault(a =>a is ConfigAction) as ConfigAction; - public SortAction SortAction => List.FirstOrDefault(a => a is SortAction) as SortAction; - public AuditLogGridToolbarAction AuditLogGridToolbarAction => List.FirstOrDefault(a => a is AuditLogGridToolbarAction) as AuditLogGridToolbarAction; + public InsertAction InsertAction { get; } + public LegendAction LegendAction { get; } + public RefreshAction RefreshAction { get; } + public FilterAction FilterAction { get; } + public ImportAction ImportAction { get; } + public ExportAction ExportAction { get; } + public ConfigAction ConfigAction { get; } + public SortAction SortAction { get; } + public AuditLogGridToolbarAction AuditLogGridToolbarAction { get; } public GridToolbarActionList() { - List.Add(new InsertAction()); - List.Add(new LegendAction()); - List.Add(new RefreshAction()); - List.Add(new FilterAction()); - List.Add(new ImportAction()); - List.Add(new ExportAction()); - List.Add(new ConfigAction()); - List.Add(new SortAction()); - List.Add(new AuditLogGridToolbarAction()); + InsertAction = new InsertAction(); + LegendAction = new LegendAction(); + RefreshAction = new RefreshAction(); + FilterAction = new FilterAction(); + ImportAction = new ImportAction(); + ExportAction = new ExportAction(); + ConfigAction = new ConfigAction(); + SortAction = new SortAction(); + AuditLogGridToolbarAction = new AuditLogGridToolbarAction(); + + List.AddRange([ + InsertAction, + LegendAction, + RefreshAction, + FilterAction, + ImportAction, + ExportAction, + ConfigAction, + SortAction, + AuditLogGridToolbarAction + ]); } - + [JsonConstructor] private GridToolbarActionList(List list) { List = list; - - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); - EnsureActionExists(); + + InsertAction = EnsureActionExists(); + LegendAction = EnsureActionExists(); + RefreshAction = EnsureActionExists(); + FilterAction = EnsureActionExists(); + ImportAction = EnsureActionExists(); + ExportAction = EnsureActionExists(); + ConfigAction = EnsureActionExists(); + SortAction = EnsureActionExists(); + AuditLogGridToolbarAction = EnsureActionExists(); } public GridToolbarActionList DeepCopy() { - return new GridToolbarActionList(List.ConvertAll(a=>a.DeepCopy())); + return new GridToolbarActionList(List.ConvertAll(a => a.DeepCopy())); } } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/Plugins/PluginAction.cs b/src/Core/DataDictionary/Models/Actions/Plugins/PluginAction.cs index 3174b0799..2565ccc93 100644 --- a/src/Core/DataDictionary/Models/Actions/Plugins/PluginAction.cs +++ b/src/Core/DataDictionary/Models/Actions/Plugins/PluginAction.cs @@ -20,7 +20,7 @@ public PluginAction() public override BasicAction DeepCopy() { - var newAction = (PluginAction)CopyAction(); + var newAction = (PluginAction)MemberwiseClone(); newAction.ConfigurationMap = new Dictionary(ConfigurationMap); return newAction; } diff --git a/src/Core/DataDictionary/Models/Actions/Plugins/PluginActionContext.cs b/src/Core/DataDictionary/Models/Actions/Plugins/PluginActionContext.cs index 45549d651..e67f219b6 100644 --- a/src/Core/DataDictionary/Models/Actions/Plugins/PluginActionContext.cs +++ b/src/Core/DataDictionary/Models/Actions/Plugins/PluginActionContext.cs @@ -9,4 +9,6 @@ public class PluginActionContext public required ActionContext ActionContext { get; init; } public required Dictionary ConfigurationMap { get; init; } public Dictionary Values => ActionContext.FormStateData.Values; + + public Dictionary SecretValues { get; } = new(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/Plugins/PluginFieldAction.cs b/src/Core/DataDictionary/Models/Actions/Plugins/PluginFieldAction.cs index e6de13659..9a66ea512 100644 --- a/src/Core/DataDictionary/Models/Actions/Plugins/PluginFieldAction.cs +++ b/src/Core/DataDictionary/Models/Actions/Plugins/PluginFieldAction.cs @@ -12,7 +12,7 @@ public class PluginFieldAction : PluginAction public override BasicAction DeepCopy() { - var newAction = (PluginFieldAction)CopyAction(); + var newAction = (PluginFieldAction)MemberwiseClone(); newAction.ConfigurationMap = new Dictionary(ConfigurationMap); newAction.FieldMap = new Dictionary(FieldMap); newAction.TriggerOnChange = TriggerOnChange; diff --git a/src/Core/DataDictionary/Models/Actions/UserCreated/HtmlTemplateAction.cs b/src/Core/DataDictionary/Models/Actions/UserCreated/HtmlTemplateAction.cs new file mode 100644 index 000000000..48e5961c4 --- /dev/null +++ b/src/Core/DataDictionary/Models/Actions/UserCreated/HtmlTemplateAction.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; + +namespace JJMasterData.Core.DataDictionary.Models.Actions; + +public sealed class HtmlTemplateAction : UserCreatedAction +{ + [JsonProperty("sqlCommand")] + [Display(Name = "SQL Command")] + public string SqlCommand { get; set; } + + [JsonProperty("htmlTemplate")] + [Display(Name = "HTML Template")] + public string HtmlTemplate { get; set; } + + public HtmlTemplateAction() + { + Name = "html-template"; + Icon = IconType.RegularFileLines; + Text = "Template"; + ShowAsButton = false; + } + + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); +} \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/UserCreated/InternalAction.cs b/src/Core/DataDictionary/Models/Actions/UserCreated/InternalAction.cs index 239fa60b2..0977a5855 100644 --- a/src/Core/DataDictionary/Models/Actions/UserCreated/InternalAction.cs +++ b/src/Core/DataDictionary/Models/Actions/UserCreated/InternalAction.cs @@ -14,7 +14,7 @@ public InternalAction() public override BasicAction DeepCopy() { - var newAction = (InternalAction)CopyAction(); + var newAction = (InternalAction)MemberwiseClone(); newAction.ElementRedirect = ElementRedirect.DeepCopy(); return newAction; } diff --git a/src/Core/DataDictionary/Models/Actions/UserCreated/ScriptAction.cs b/src/Core/DataDictionary/Models/Actions/UserCreated/ScriptAction.cs index ffb6b21b2..0dd32e189 100644 --- a/src/Core/DataDictionary/Models/Actions/UserCreated/ScriptAction.cs +++ b/src/Core/DataDictionary/Models/Actions/UserCreated/ScriptAction.cs @@ -15,5 +15,5 @@ public sealed class ScriptAction : UserCreatedAction // ReSharper disable once InconsistentNaming public string OnClientClick { get; set; } public override bool IsUserCreated => true; - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/UserCreated/SqlCommandAction.cs b/src/Core/DataDictionary/Models/Actions/UserCreated/SqlCommandAction.cs index c62fb77dc..ed88c0d95 100644 --- a/src/Core/DataDictionary/Models/Actions/UserCreated/SqlCommandAction.cs +++ b/src/Core/DataDictionary/Models/Actions/UserCreated/SqlCommandAction.cs @@ -1,4 +1,7 @@ -using System.ComponentModel.DataAnnotations; +#nullable enable +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Azure.Core; using Newtonsoft.Json; namespace JJMasterData.Core.DataDictionary.Models.Actions; @@ -8,12 +11,12 @@ public sealed class SqlCommandAction : UserCreatedAction, ISubmittableAction [JsonProperty("isSubmit")] [Display(Name = "Is Submit")] public bool IsSubmit { get; set; } - + /// /// Comando SQL a ser executado, aceita expression /// [JsonProperty("commandSQL")] - public string SqlCommand { get; set; } + public string SqlCommand { get; set; } = null!; /// /// Aplicar somenter as linhas selecionadas (default=false) @@ -26,9 +29,16 @@ public sealed class SqlCommandAction : UserCreatedAction, ISubmittableAction [Display(Name = "Apply only on selected lines")] public bool ApplyOnSelected { get; set; } + /// + /// Redirects to this URL after the command is executed successfully. + /// + [JsonProperty("redirectUrl")] + [Display(Name = "Redirect Url")] + public string? RedirectUrl { get; set; } + public SqlCommandAction() { Icon = IconType.Play; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/UserCreated/SubmitAction.cs b/src/Core/DataDictionary/Models/Actions/UserCreated/SubmitAction.cs index 42a8550b2..2c0fd5f00 100644 --- a/src/Core/DataDictionary/Models/Actions/UserCreated/SubmitAction.cs +++ b/src/Core/DataDictionary/Models/Actions/UserCreated/SubmitAction.cs @@ -7,5 +7,5 @@ public sealed class SubmitAction : UserCreatedAction { [JsonProperty("formAction")] public string FormAction { get; set; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/Actions/UserCreated/UrlRedirectAction.cs b/src/Core/DataDictionary/Models/Actions/UserCreated/UrlRedirectAction.cs index 2fe8e5a56..b4afd6e81 100644 --- a/src/Core/DataDictionary/Models/Actions/UserCreated/UrlRedirectAction.cs +++ b/src/Core/DataDictionary/Models/Actions/UserCreated/UrlRedirectAction.cs @@ -10,19 +10,21 @@ public sealed class UrlRedirectAction : UserCreatedAction public string UrlRedirect { get; set; } [JsonProperty("urlAsPopUp")] + [Display(Name = "Is Modal?")] public bool IsModal { get; set; } /// /// If the action is inside a modal, render as iFrame. If this is false, it will only add the resulting HTML inside the modal (recommended). /// [JsonProperty("isIframe")] - [Display(Name = "Iframe")] + [Display(Name = "Is Iframe?")] public bool IsIframe { get; set; } = true; [JsonProperty("popupSize")] public ModalSize ModalSize { get; set; } = ModalSize.Default; [JsonProperty("ModalTitle")] + [Display(Name = "Modal Title")] public string ModalTitle { get; set; } = "Title"; [JsonProperty("encryptParameters")] @@ -33,5 +35,5 @@ public UrlRedirectAction() { Icon = IconType.ExternalLink; } - public override BasicAction DeepCopy() => CopyAction(); + public override BasicAction DeepCopy() => (BasicAction)MemberwiseClone(); } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/BootstrapColor.cs b/src/Core/DataDictionary/Models/BootstrapColor.cs index ce784530e..ffaf8d929 100644 --- a/src/Core/DataDictionary/Models/BootstrapColor.cs +++ b/src/Core/DataDictionary/Models/BootstrapColor.cs @@ -25,7 +25,7 @@ public static class ColorExtensions public static string ToButtonColorString(this BootstrapColor color) { if (BootstrapHelper.Version >= 4 && color == BootstrapColor.Default) - return BootstrapColor.Secondary.ToColorString(); + return "secondary"; return color.ToColorString(); } @@ -33,7 +33,7 @@ public static string ToButtonColorString(this BootstrapColor color) public static string ToLinkColorString(this BootstrapColor color) { if (BootstrapHelper.Version >= 4 && color == BootstrapColor.Default) - return BootstrapColor.Default.ToString().ToLower(); + return "default"; return color.ToColorString(); } diff --git a/src/Core/DataDictionary/Models/DataElementMap.cs b/src/Core/DataDictionary/Models/DataElementMap.cs index 5eac243df..039c72ab2 100644 --- a/src/Core/DataDictionary/Models/DataElementMap.cs +++ b/src/Core/DataDictionary/Models/DataElementMap.cs @@ -1,7 +1,6 @@ #nullable enable using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; using Newtonsoft.Json; namespace JJMasterData.Core.DataDictionary.Models; diff --git a/src/Core/DataDictionary/Models/FormElementDataItem.cs b/src/Core/DataDictionary/Models/FormElementDataItem.cs index 042323ec6..e82626b10 100644 --- a/src/Core/DataDictionary/Models/FormElementDataItem.cs +++ b/src/Core/DataDictionary/Models/FormElementDataItem.cs @@ -1,7 +1,6 @@ #nullable enable using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; using JJMasterData.Commons.Data; using Newtonsoft.Json; diff --git a/src/Core/DataDictionary/Models/FormElementField.cs b/src/Core/DataDictionary/Models/FormElementField.cs index daba37608..616b5bc6c 100644 --- a/src/Core/DataDictionary/Models/FormElementField.cs +++ b/src/Core/DataDictionary/Models/FormElementField.cs @@ -140,6 +140,7 @@ public class FormElementField : ElementField [Display(Name = "Auto Reload")] public bool AutoPostBack { get; set; } + /// /// [See expressions](../articles/expressions.md) /// @@ -292,6 +293,7 @@ public bool SupportsFloatingLabel() => Component is FormComponent.Text or FormComponent.TextArea + or FormComponent.Hour or FormComponent.Date or FormComponent.DateTime or FormComponent.Number @@ -300,7 +302,7 @@ or FormComponent.Cpf or FormComponent.CnpjCpf or FormComponent.Tel or FormComponent.ComboBox - && (DataItem == null || (DataItem != null && DataItem.SupportsFloatingLabels())); + && DataItem?.SupportsFloatingLabels() != false; /// /// Set if the field is enabled. diff --git a/src/Core/DataDictionary/Models/FormElementFieldList.cs b/src/Core/DataDictionary/Models/FormElementFieldList.cs index 4c8564f74..6f9328cbb 100644 --- a/src/Core/DataDictionary/Models/FormElementFieldList.cs +++ b/src/Core/DataDictionary/Models/FormElementFieldList.cs @@ -135,7 +135,7 @@ public FormElementField this[string fieldName] foreach (var val in _formFields) { - if (val.Name.ToLower().Equals(fieldName.ToLower())) + if (val.Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase)) return val; } throw new KeyNotFoundException($"Field {fieldName} not found."); @@ -145,7 +145,7 @@ public FormElementField this[string fieldName] var isOk = false; for (var i = 0; i < _formFields.Count; i++) { - if (!_formFields[i].Name.ToLower().Equals(fieldName.ToLower())) + if (!_formFields[i].Name.Equals(fieldName, StringComparison.OrdinalIgnoreCase)) continue; _formFields[i] = value; @@ -189,6 +189,16 @@ public List GetElementFields() return _baseFields.GetAsList(); } + public List FindAll(Predicate predicate) + { + return _formFields.FindAll(predicate); + } + + public bool Exists(Predicate predicate) + { + return _formFields.Exists(predicate); + } + public FormElementFieldList DeepCopy() { return new FormElementFieldList( diff --git a/src/Core/DataDictionary/Models/FormElementOptions.cs b/src/Core/DataDictionary/Models/FormElementOptions.cs index 07b55b533..c7414c1ba 100644 --- a/src/Core/DataDictionary/Models/FormElementOptions.cs +++ b/src/Core/DataDictionary/Models/FormElementOptions.cs @@ -37,7 +37,7 @@ public FormElementOptions() Form = new FormUI(); GridToolbarActions = []; FormToolbarActions = []; - GridTableActions = new GridTableActionList(); + GridTableActions = []; } [JsonConstructor] diff --git a/src/Core/DataDictionary/Models/FormElementRelationshipList.cs b/src/Core/DataDictionary/Models/FormElementRelationshipList.cs index a35ee7a92..c34752dec 100644 --- a/src/Core/DataDictionary/Models/FormElementRelationshipList.cs +++ b/src/Core/DataDictionary/Models/FormElementRelationshipList.cs @@ -1,4 +1,5 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using JJMasterData.Commons.Data.Entity.Models; @@ -81,7 +82,7 @@ public void Add(FormElementRelationship item) private void SetId(FormElementRelationship item) { - var highestId = _formRelationships.Any() ? _formRelationships.Max(x => x.Id) : 1; + var highestId = _formRelationships.Count > 0 ? _formRelationships.Max(x => x.Id) : 1; item.Id = highestId + 1; } @@ -165,10 +166,15 @@ public int GetIndexById(int id) return _formRelationships.IndexOf(relationship); } + public List FindAll(Predicate predicate) + { + return _formRelationships.FindAll(predicate); + } + public FormElementRelationshipList DeepCopy() { return new FormElementRelationshipList( - _baseRelationships.Select(b=>b.DeepCopy()).ToList(), + _baseRelationships.ConvertAll(b=>b.DeepCopy()), _formRelationships.ConvertAll(r=>r.DeepCopy())); } } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/IconHelper.cs b/src/Core/DataDictionary/Models/IconHelper.cs index fafd1a9f5..38f8b4f51 100644 --- a/src/Core/DataDictionary/Models/IconHelper.cs +++ b/src/Core/DataDictionary/Models/IconHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Frozen; using System.Collections.Generic; using System.Reflection; +using System.Runtime.CompilerServices; using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Exceptions; @@ -10,72 +11,45 @@ namespace JJMasterData.Core.DataDictionary.Models; public static class IconHelper { - private static FrozenSet? _icons; - public static FrozenSet GetIconList() - { - if (_icons != null) - return _icons; - - _icons = new List((IconType[])Enum.GetValues(typeof(IconType))).ToFrozenSet(); - - return _icons; - } + private static readonly FrozenSet Icons; + private static readonly FrozenDictionary CssClasses; - private static FrozenDictionary? _cssClasses; - private static FrozenDictionary CssClasses + static IconHelper() { - get - { - if (_cssClasses is not null) - return _cssClasses; - - var cssClasses = new Dictionary(); - var icons = GetIconList(); - foreach (var icon in icons) - { - var enumField = typeof(IconType).GetField(icon.ToString()); - var attribute = enumField!.GetCustomAttribute(); - cssClasses[icon] = attribute!.CssClass; - } + Icons = new List((IconType[])Enum.GetValues(typeof(IconType))).ToFrozenSet(); - _cssClasses = cssClasses.ToFrozenDictionary(); - - return _cssClasses; + var cssClasses = new Dictionary(); + foreach (var icon in Icons) + { + var enumField = typeof(IconType).GetField(icon.ToString()); + var attribute = enumField!.GetCustomAttribute(); + cssClasses[icon] = attribute!.CssClass; } - } - - - public static int GetId(this IconType icon) - { - return (int)icon; + + CssClasses = cssClasses.ToFrozenDictionary(); } - public static string GetCssClass(this IconType icon) - { - return CssClasses[icon]; - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static FrozenSet GetIconList() => Icons; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetId(this IconType icon) => (int)icon; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetCssClass(this IconType icon) => CssClasses[icon]; + internal static IconType GetIconTypeFromField(ElementField field, object value) { - IconType iconType; if (value is int intValue) { - iconType = (IconType)intValue; + return (IconType)intValue; } - else + + if (int.TryParse(value.ToString(), out var parsedInt)) { - if (int.TryParse(value.ToString(), out var parsedInt)) - { - iconType = (IconType)parsedInt; - } - else - { - throw new JJMasterDataException($"Invalid IconType id at {field.LabelOrName}."); - } + return (IconType)parsedInt; } - return iconType; + throw new JJMasterDataException($"Invalid IconType id at {field.LabelOrName}."); } - - } \ No newline at end of file diff --git a/src/Core/DataDictionary/Models/ProcessScope.cs b/src/Core/DataDictionary/Models/ProcessScope.cs index b4b3910d2..908d866f7 100644 --- a/src/Core/DataDictionary/Models/ProcessScope.cs +++ b/src/Core/DataDictionary/Models/ProcessScope.cs @@ -3,7 +3,6 @@ /// /// Escopo de execução do pocesso /// - public enum ProcessScope { /// diff --git a/src/Core/DataDictionary/Repository/Abstractions/IDataDictionaryRepository.cs b/src/Core/DataDictionary/Repository/Abstractions/IDataDictionaryRepository.cs index 5f8e10b11..8b711b0e6 100644 --- a/src/Core/DataDictionary/Repository/Abstractions/IDataDictionaryRepository.cs +++ b/src/Core/DataDictionary/Repository/Abstractions/IDataDictionaryRepository.cs @@ -13,7 +13,7 @@ public interface IDataDictionaryRepository { Task CreateStructureIfNotExistsAsync(); FormElement GetFormElement(string elementName); - Task GetFormElementAsync(string elementName); + ValueTask GetFormElementAsync(string elementName); Task> GetFormElementListAsync(bool? apiSync = null); Task> GetNameListAsync(); List GetFormElementList(bool? apiSync = null); diff --git a/src/Core/DataDictionary/Repository/FileSystemDataDictionaryRepository.cs b/src/Core/DataDictionary/Repository/FileSystemDataDictionaryRepository.cs index 2556a803c..e442621af 100644 --- a/src/Core/DataDictionary/Repository/FileSystemDataDictionaryRepository.cs +++ b/src/Core/DataDictionary/Repository/FileSystemDataDictionaryRepository.cs @@ -102,10 +102,10 @@ public FormElement GetFormElement(string elementName) return GetMetadata(elementName); } - public Task GetFormElementAsync(string elementName) + public ValueTask GetFormElementAsync(string elementName) { var result = GetMetadata(elementName); - return Task.FromResult(result); + return new ValueTask(result); } /// @@ -197,15 +197,15 @@ public IEnumerable GetMetadataInfoList(DataDictionaryFilter fil if (filter != null) { - if (!string.IsNullOrEmpty(filter.Name) && !formElement.Name.ToLower().Contains(filter.Name.ToLower())) + if (!string.IsNullOrEmpty(filter.Name) && formElement.Name.IndexOf(filter.Name, StringComparison.OrdinalIgnoreCase) < 0) continue; if (filter.ContainsTableName != null) { - bool containsName = filter.ContainsTableName.Any(tableName => formElement.TableName.ToLower().Contains(tableName.ToLower())); + bool containsName = filter.ContainsTableName.Any(tableName => formElement.TableName.IndexOf(tableName, StringComparison.OrdinalIgnoreCase) >= 0); if (!containsName) continue; - } + } if (filter.LastModifiedFrom.HasValue && file.LastWriteTime < filter.LastModifiedFrom) continue; diff --git a/src/Core/DataDictionary/Repository/SqlDataDictionaryRepository.cs b/src/Core/DataDictionary/Repository/SqlDataDictionaryRepository.cs index a351d00d9..0c4073ffa 100644 --- a/src/Core/DataDictionary/Repository/SqlDataDictionaryRepository.cs +++ b/src/Core/DataDictionary/Repository/SqlDataDictionaryRepository.cs @@ -59,7 +59,7 @@ private static EntityParameters GetFormElementListParameters(bool? apiSync) return new EntityParameters{Filters = filters, OrderBy = orderBy}; } - private static IEnumerable ParseDictionaryList(IEnumerable> result) + private static IEnumerable ParseDictionaryList(List> result) { foreach (var row in result) { @@ -74,13 +74,13 @@ public async Task> GetNameListAsync() var dt = await entityRepository.GetDictionaryListResultAsync(_masterDataElement, new EntityParameters { Filters = filter }, false); - return dt.Data.Select(row => row[DataDictionaryStructure.Name]!.ToString()!).ToList(); + return dt.Data.ConvertAll(row => row[DataDictionaryStructure.Name]!.ToString()!); } public FormElement? GetFormElement(string elementName) { - if (_enableDataDictionaryCaching && memoryCache.TryGetValue(elementName, out FormElement formElement)) - return formElement.DeepCopy(); + if (_enableDataDictionaryCaching && memoryCache.TryGetValue(elementName, out FormElement? formElement)) + return formElement!.DeepCopy(); var filter = new Dictionary { { DataDictionaryStructure.Name, elementName } }; @@ -101,10 +101,10 @@ public async Task> GetNameListAsync() return null; } - public async Task GetFormElementAsync(string elementName) + public async ValueTask GetFormElementAsync(string elementName) { - if (_enableDataDictionaryCaching && memoryCache.TryGetValue(elementName, out FormElement formElement)) - return formElement.DeepCopy(); + if (_enableDataDictionaryCaching && memoryCache.TryGetValue(elementName, out FormElement? formElement)) + return formElement!.DeepCopy(); var filter = new Dictionary { { DataDictionaryStructure.Name, elementName } }; @@ -196,7 +196,7 @@ public async Task ExistsAsync(string elementName) { var filter = new Dictionary { { DataDictionaryStructure.Name, elementName } }; var fields = await entityRepository.GetFieldsAsync(_masterDataElement, filter); - return fields.Any(); + return fields.Count > 0; } public async Task CreateStructureIfNotExistsAsync() @@ -218,7 +218,7 @@ public async Task> GetFormElementInfoListAsync(DataD Filters = filters!, OrderBy = orderBy, CurrentPage = currentPage, RecordsPerPage = recordsPerPage }); - var formElementInfoList = result.Data.Select(FormElementInfo.FromDictionary).ToList(); + var formElementInfoList = result.Data.ConvertAll(FormElementInfo.FromDictionary); return new ListResult(formElementInfoList, result.TotalOfRecords); } diff --git a/src/Core/DataDictionary/Services/BaseService.cs b/src/Core/DataDictionary/Services/BaseService.cs index 9749fb7eb..92cde38c7 100644 --- a/src/Core/DataDictionary/Services/BaseService.cs +++ b/src/Core/DataDictionary/Services/BaseService.cs @@ -36,7 +36,7 @@ public JJValidationSummary GetValidationSummary() public bool IsValid => validationDictionary.IsValid; - public Task GetFormElementAsync(string elementName) + public ValueTask GetFormElementAsync(string elementName) { return DataDictionaryRepository.GetFormElementAsync(elementName); } diff --git a/src/Core/DataDictionary/Services/ClassGenerationService.cs b/src/Core/DataDictionary/Services/ClassGenerationService.cs index 5037a27a9..7c8ea79ea 100644 --- a/src/Core/DataDictionary/Services/ClassGenerationService.cs +++ b/src/Core/DataDictionary/Services/ClassGenerationService.cs @@ -1,4 +1,3 @@ -using System.Linq; using System.Text; using System.Threading.Tasks; using JJMasterData.Commons.Data.Entity.Models; @@ -10,15 +9,15 @@ namespace JJMasterData.Core.DataDictionary.Services; public class ClassGenerationService(IDataDictionaryRepository dataDictionaryRepository) { private IDataDictionaryRepository DataDictionaryRepository { get; } = dataDictionaryRepository; - - public async Task GetClassSourceCode(string elementName) + + public async ValueTask GetClassSourceCode(string elementName) { const string propertyTemplate = "public @PropertyType @PropertyName { get; set; } "; var formElement = await DataDictionaryRepository.GetFormElementAsync(elementName); var properties = new StringBuilder(); - foreach (var item in formElement.Fields.ToList()) + foreach (var item in formElement.Fields) { var propertyName = StringManager.ToPascalCase(item.Name); var propertyType = GetPropertyType(item.DataType, item.IsRequired); diff --git a/src/Core/DataDictionary/Services/ElementService.cs b/src/Core/DataDictionary/Services/ElementService.cs index d631abaeb..dab9e7b0f 100644 --- a/src/Core/DataDictionary/Services/ElementService.cs +++ b/src/Core/DataDictionary/Services/ElementService.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; -using System.Linq; using System.Text; using System.Threading.Tasks; using JJMasterData.Commons.Configuration.Options; @@ -14,6 +13,7 @@ using JJMasterData.Core.DataDictionary.Repository.Abstractions; using JJMasterData.Core.DataDictionary.Structure; using JJMasterData.Core.Http.Abstractions; +using JJMasterData.Core.Tasks; using JJMasterData.Core.UI.Components; using Microsoft.Extensions.Localization; using Newtonsoft.Json; @@ -30,11 +30,6 @@ public class ElementService( IMasterDataUrlHelper urlHelper) : BaseService(validationDictionary, dataDictionaryRepository, stringLocalizer) { - private IFormElementComponentFactory FormViewFactory { get; } = formViewFactory; - private DataDictionaryFormElementFactory DataDictionaryFormElementFactory { get; } = dataDictionaryFormElementFactory; - private IMasterDataUrlHelper UrlHelper { get; } = urlHelper; - private IEntityRepository EntityRepository { get; } = entityRepository; - #region Add Dictionary public async Task CreateEntityAsync(ElementBean elementBean) @@ -49,7 +44,7 @@ public class ElementService( FormElement formElement; if (importFields) { - var element = await EntityRepository.GetElementFromTableAsync(tableName, connectionId); + var element = await entityRepository.GetElementFromTableAsync(tableName, connectionId); element.Name = MasterDataCommonsOptions.RemoveTbPrefix(tableName); formElement = new FormElement(element); } @@ -84,7 +79,7 @@ public async Task ValidateEntityAsync(ElementBean elementBean) if (importFields & IsValid) { - var exists = await EntityRepository.TableExistsAsync(tableName, connectionId); + var exists = await entityRepository.TableExistsAsync(tableName, connectionId); if (!exists) AddError("Name", StringLocalizer["Table not found"]); } @@ -134,7 +129,7 @@ private async Task ValidateEntityAsync(string name) public JJFormView GetFormView() { - var formView = FormViewFactory.Create(DataDictionaryFormElementFactory.GetFormElement()); + var formView = formViewFactory.Create(dataDictionaryFormElementFactory.GetFormElement()); formView.GridView.SetCurrentFilter(DataDictionaryStructure.Type,"F"); @@ -155,7 +150,7 @@ public JJFormView GetFormView() await DataDictionaryRepository.GetFormElementInfoListAsync(filter, args.OrderBy, args.RecordsPerPage, args.CurrentPage); - var dictionaryList = result.Data.Select(info => info.ToDictionary()).ToList(); + var dictionaryList = result.Data.ConvertAll(info => info.ToDictionary()); args.DataSource = dictionaryList; args.TotalOfRecords = result.TotalOfRecords; @@ -175,7 +170,7 @@ await DataDictionaryRepository.GetFormElementInfoListAsync(filter, args.OrderBy, } } - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; }; formView.GridView.OnRenderActionAsync += (_, args) => @@ -186,21 +181,21 @@ await DataDictionaryRepository.GetFormElementInfoListAsync(filter, args.OrderBy, { case "render": args.LinkButton.OnClientClick = - $"window.open('{UrlHelper.Action("Render", "Form", new {Area="MasterData", elementName })}', '_blank').focus();"; + $"window.open('{urlHelper.Action("Render", "Form", new {Area="MasterData", elementName })}', '_blank').focus();"; break; case "tools": - args.LinkButton.UrlAction = UrlHelper.Action("Index", "Entity", + args.LinkButton.UrlAction = urlHelper.Action("Index", "Entity", new { Area="DataDictionary", elementName }); args.LinkButton.OnClientClick = ""; break; case "duplicate": - args.LinkButton.UrlAction = UrlHelper.Action("Duplicate", "Element", + args.LinkButton.UrlAction = urlHelper.Action("Duplicate", "Element", new { Area="DataDictionary", elementName }); args.LinkButton.OnClientClick = ""; break; } - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; }; return formView; @@ -261,7 +256,6 @@ public async Task Import(Stream file) await DataDictionaryRepository.InsertOrReplaceAsync(dicParser); - return IsValid; } diff --git a/src/Core/DataDictionary/Services/EntityService.cs b/src/Core/DataDictionary/Services/EntityService.cs index 527925414..008baa7f0 100644 --- a/src/Core/DataDictionary/Services/EntityService.cs +++ b/src/Core/DataDictionary/Services/EntityService.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Localization; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Repository.Abstractions; @@ -15,7 +14,7 @@ public class EntityService(IValidationDictionary validationDictionary, { private async Task ValidateEntity(Entity entity, string originName) { - if (ValidateName(entity.Name) && !originName.ToLower().Equals(entity.Name.ToLower())) + if (ValidateName(entity.Name) && !originName.Equals(entity.Name, StringComparison.OrdinalIgnoreCase)) { if (await DataDictionaryRepository.ExistsAsync(entity.Name)) AddError("Name", StringLocalizer["There is already a dictionary with the name {0}",entity.Name]); @@ -28,7 +27,7 @@ private async Task ValidateEntity(Entity entity, string originName) if (!string.IsNullOrEmpty(entity.ReadProcedureName) && !string.IsNullOrEmpty(entity.WriteProcedureName)) { - if (entity.ReadProcedureName.ToLower().Equals(entity.WriteProcedureName.ToLower())) + if (entity.ReadProcedureName.Equals(entity.WriteProcedureName, StringComparison.OrdinalIgnoreCase)) { AddError("CustomProcNameGet", StringLocalizer["Procedure names cannot be identical"]); } @@ -54,13 +53,10 @@ public async Task EditEntityAsync(Entity entity, string entityName) if (!entityName.Equals(formElement.Name)) { await DataDictionaryRepository.DeleteAsync(entityName); - await DataDictionaryRepository.InsertOrReplaceAsync(formElement); - } - else - { - await DataDictionaryRepository.InsertOrReplaceAsync(formElement); } + await DataDictionaryRepository.InsertOrReplaceAsync(formElement); + return formElement; } diff --git a/src/Core/DataDictionary/Services/FieldService.cs b/src/Core/DataDictionary/Services/FieldService.cs index f02f3c0a4..68ce9d178 100644 --- a/src/Core/DataDictionary/Services/FieldService.cs +++ b/src/Core/DataDictionary/Services/FieldService.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Localization; -using JJMasterData.Commons.Util; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Repository.Abstractions; using JJMasterData.Core.DataManager.Expressions.Abstractions; diff --git a/src/Core/DataDictionary/Services/PanelService.cs b/src/Core/DataDictionary/Services/PanelService.cs index ddee7ad33..4de692d9c 100644 --- a/src/Core/DataDictionary/Services/PanelService.cs +++ b/src/Core/DataDictionary/Services/PanelService.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading.Tasks; using JJMasterData.Commons.Localization; -using JJMasterData.Commons.Util; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Repository.Abstractions; using JJMasterData.Core.DataManager.Expressions.Abstractions; @@ -24,7 +23,7 @@ public async Task SavePanelAsync(string elementName, FormElementPanel panel, str { var formElement = await DataDictionaryRepository.GetFormElementAsync(elementName); - if (selectedFields is null || !selectedFields.Any()) + if (selectedFields is null || selectedFields.Length == 0) { AddError(nameof(selectedFields), StringLocalizer["No fields selected for this panel."]); } diff --git a/src/Core/DataDictionary/Services/UIOptionsService.cs b/src/Core/DataDictionary/Services/UIOptionsService.cs index 2c6d107e6..539d49dce 100644 --- a/src/Core/DataDictionary/Services/UIOptionsService.cs +++ b/src/Core/DataDictionary/Services/UIOptionsService.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading.Tasks; using JJMasterData.Commons.Localization; using JJMasterData.Core.DataDictionary.Models; @@ -18,7 +17,7 @@ private async Task ValidateOptions(FormElementOptions options, string elem if (options.Grid.EnableMultiSelect) { var formElement = await DataDictionaryRepository.GetFormElementAsync(elementName); - var pks = formElement.Fields.ToList().FindAll(x => x.IsPk); + var pks = formElement.Fields.FindAll(x => x.IsPk); if (pks.Count == 0) { AddError("EnableMultiSelect", StringLocalizer["You cannot enable MultiSelect without setting a primary key"]); diff --git a/src/Core/DataDictionary/Structure/DataDictionaryFilter.cs b/src/Core/DataDictionary/Structure/DataDictionaryFilter.cs index d9da958d7..b4367e31e 100644 --- a/src/Core/DataDictionary/Structure/DataDictionaryFilter.cs +++ b/src/Core/DataDictionary/Structure/DataDictionaryFilter.cs @@ -61,7 +61,7 @@ public Dictionary ToDictionary() result[DataDictionaryStructure.Name] = Name; } - if (ContainsTableName != null && ContainsTableName.Any()) + if (ContainsTableName?.Count > 0) { string tableNameFilter = string.Empty; for (int i = 0; i < ContainsTableName.Count; i++) diff --git a/src/Core/DataDictionary/Structure/DataDictionaryFormElementFactory.cs b/src/Core/DataDictionary/Structure/DataDictionaryFormElementFactory.cs index e90ea3698..99ae51ecb 100644 --- a/src/Core/DataDictionary/Structure/DataDictionaryFormElementFactory.cs +++ b/src/Core/DataDictionary/Structure/DataDictionaryFormElementFactory.cs @@ -6,7 +6,6 @@ using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.UI; -using JJMasterData.Core.UI.Components; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; @@ -17,8 +16,6 @@ public class DataDictionaryFormElementFactory( IStringLocalizer stringLocalizer, IMasterDataUrlHelper urlHelper) { - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private IMasterDataUrlHelper UrlHelper { get; } = urlHelper; private readonly MasterDataCoreOptions _options = options.Value; public FormElement GetFormElement() @@ -41,7 +38,7 @@ private FormElement GetFormElement(Element element) { var formElement = new FormElement(element); - formElement.SubTitle = $"val:{StringLocalizer[formElement.SubTitle!]}"; + formElement.SubTitle = $"val:{stringLocalizer[formElement.SubTitle!]}"; ConfigureFields(formElement); @@ -59,7 +56,7 @@ private void ConfigureFields(FormElement formElement) formElement.Fields[DataDictionaryStructure.Json].CssClass = "col-sm-4"; formElement.Fields[DataDictionaryStructure.Json].VisibleExpression = "val:{IsFilter}"; formElement.Fields[DataDictionaryStructure.Json].HelpDescription = - StringLocalizer["Filter by any data within the data dictionary structure."]; + stringLocalizer["Filter by any data within the data dictionary structure."]; formElement.Fields[DataDictionaryStructure.LastModified].Component = FormComponent.DateTime; formElement.Fields[DataDictionaryStructure.LastModified].GridAlignment = GridAlignment.Right; formElement.Fields[DataDictionaryStructure.EnableSynchronism].VisibleExpression = "val:0"; @@ -89,7 +86,7 @@ private void AddGridTableActions(FormElement formElement) { Icon = IconType.Pencil, Name = "tools", - Tooltip = StringLocalizer["Edit"], + Tooltip = stringLocalizer["Edit"], EnableExpression = "exp:'T' <> '{type}'", IsDefaultOption = true }, @@ -97,7 +94,7 @@ private void AddGridTableActions(FormElement formElement) { Icon = IconType.FilesO, Name = "duplicate", - Tooltip = StringLocalizer["Duplicate"], + Tooltip = stringLocalizer["Duplicate"], EnableExpression = "exp:'T' <> '{type}'", IsGroup = false, }, @@ -105,7 +102,7 @@ private void AddGridTableActions(FormElement formElement) { Icon = IconType.SolidCirclePlay, Name = "render", - Tooltip = StringLocalizer["Render"], + Tooltip = stringLocalizer["Render"], EnableExpression = "exp:'T' <> '{type}'", IsGroup = false } @@ -121,10 +118,10 @@ private void AddGridToolbarActions(FormElement formElement) new UrlRedirectAction { Name = "btnAdd", - Text = StringLocalizer["New"], + Text = stringLocalizer["New"], Icon = IconType.Plus, ShowAsButton = true, - UrlRedirect = UrlHelper.Action("Add", "Element", new {Area="DataDictionary"}) + UrlRedirect = urlHelper.Action("Add", "Element", new {Area="DataDictionary"}) }, new SubmitAction @@ -132,21 +129,21 @@ private void AddGridToolbarActions(FormElement formElement) Name = "btnDeleteSelected", Order = 0, Icon = IconType.Trash, - Text = StringLocalizer["Delete Selected"], + Text = stringLocalizer["Delete Selected"], IsGroup = false, - ConfirmationMessage = StringLocalizer["Do you want to delete ALL selected records?"], + ConfirmationMessage = stringLocalizer["Do you want to delete ALL selected records?"], ShowAsButton = true, - FormAction = UrlHelper.Action("Delete", "Element", new {Area="DataDictionary"}), + FormAction = urlHelper.Action("Delete", "Element", new {Area="DataDictionary"}), }, new ScriptAction() { Name = "btnAbout", - Text = StringLocalizer["About"], + Text = stringLocalizer["About"], Icon = IconType.InfoCircle, ShowAsButton = false, IsGroup = true, - OnClientClick = $"DataDictionaryUtils.showAbout('{UrlHelper.Action("Index", "About", new {Area="DataDictionary"})}')", + OnClientClick = $"DataDictionaryUtils.showAbout('{urlHelper.Action("Index", "About", new {Area="DataDictionary"})}')", Order = 14, CssClass = BootstrapHelper.PullRight }, @@ -154,7 +151,7 @@ private void AddGridToolbarActions(FormElement formElement) new ScriptAction { Name = "btnHelp", - Text = StringLocalizer["Help"], + Text = stringLocalizer["Help"], Icon = IconType.QuestionCircle, IsGroup = true, ShowAsButton = true, @@ -166,13 +163,13 @@ private void AddGridToolbarActions(FormElement formElement) new UrlRedirectAction { Name = "btnImport", - Tooltip = StringLocalizer["Import"], + Tooltip = stringLocalizer["Import"], Icon = IconType.Upload, ShowAsButton = true, IsModal = true, IsIframe = false, ModalTitle = "Import", - UrlRedirect = UrlHelper.Action("Import", "Element", new {Area="DataDictionary"}), + UrlRedirect = urlHelper.Action("Import", "Element", new {Area="DataDictionary"}), Order = 11, CssClass = BootstrapHelper.PullRight }, @@ -180,26 +177,26 @@ private void AddGridToolbarActions(FormElement formElement) new ScriptAction { Name = "btnExport", - Tooltip = StringLocalizer["Export Selected"], + Tooltip = stringLocalizer["Export Selected"], Icon = IconType.Download, ShowAsButton = true, Order = 10, CssClass = BootstrapHelper.PullRight, OnClientClick = - $"DataDictionaryUtils.exportElement('{ComponentNameGenerator.Create(_options.DataDictionaryTableName)}', '{UrlHelper.Action("Export", "Element", new{Area="DataDictionary"})}', '{StringLocalizer["Select one or more dictionaries"]}');" + $"DataDictionaryUtils.exportElement('{_options.DataDictionaryTableName.ToLowerInvariant()}', '{urlHelper.Action("Export", "Element", new{Area="DataDictionary"})}', '{stringLocalizer["Select one or more dictionaries"]}');" }, new UrlRedirectAction { Name = "btnLog", - Text = StringLocalizer["Log"], + Text = stringLocalizer["Log"], Icon = IconType.Film, ShowAsButton = true, IsModal = true, IsGroup = true, - ModalTitle = StringLocalizer["Log"], + ModalTitle = stringLocalizer["Log"], ModalSize = ModalSize.ExtraLarge, - UrlRedirect = UrlHelper.Action("Index", "Log", new {Area="DataDictionary", isModal = true}), + UrlRedirect = urlHelper.Action("Index", "Log", new {Area="DataDictionary", isModal = true}), Order = 11, CssClass = BootstrapHelper.PullRight }, @@ -207,11 +204,11 @@ private void AddGridToolbarActions(FormElement formElement) new UrlRedirectAction { Name = "btnAppSettings", - Text = StringLocalizer["Application Settings"], + Text = stringLocalizer["Application Settings"], Icon = IconType.SolidToolbox, ShowAsButton = true, IsGroup = true, - UrlRedirect = UrlHelper.Action("Index", "Settings", new {Area="DataDictionary"}), + UrlRedirect = urlHelper.Action("Index", "Settings", new {Area="DataDictionary"}), Order = 12, CssClass = BootstrapHelper.PullRight }, @@ -219,14 +216,14 @@ private void AddGridToolbarActions(FormElement formElement) new UrlRedirectAction { Name = "btnI18n", - Text = StringLocalizer["Internationalization"], + Text = stringLocalizer["Internationalization"], Icon = IconType.SolidEarthAmericas, ShowAsButton = true, IsModal = true, IsGroup = true, - ModalTitle = StringLocalizer["Internationalization"], + ModalTitle = stringLocalizer["Internationalization"], ModalSize = ModalSize.ExtraLarge, - UrlRedirect = UrlHelper.Action("Index", "Localization", new {Area="DataDictionary", isModal=true}), + UrlRedirect = urlHelper.Action("Index", "Localization", new {Area="DataDictionary", isModal=true}), Order = 11, CssClass = BootstrapHelper.PullRight } diff --git a/src/Core/DataDictionary/Structure/LocalizationFormElementFactory.cs b/src/Core/DataDictionary/Structure/LocalizationFormElementFactory.cs index f11ff2897..92711d377 100644 --- a/src/Core/DataDictionary/Structure/LocalizationFormElementFactory.cs +++ b/src/Core/DataDictionary/Structure/LocalizationFormElementFactory.cs @@ -19,10 +19,18 @@ public FormElement GetFormElement(CultureInfo[] supportedCultures) { var element = MasterDataStringLocalizerElement.GetElement(masterDataOptions.Value); - var formElement = new FormElement(element); - formElement.Title = stringLocalizer["Internationalization"]; - formElement.Icon = IconType.SolidEarthAmericas; - formElement.Options.Grid.ShowTitle = false; + var formElement = new FormElement(element) + { + Title = stringLocalizer["Internationalization"], + Icon = IconType.SolidEarthAmericas, + Options = + { + Grid = + { + ShowTitle = false + } + } + }; formElement.Fields["resourceKey"].IsRequired = true; formElement.Fields["resourceKey"].CssClass = "col-sm-6"; formElement.Fields["resourceOrigin"].VisibleExpression = "val:0"; @@ -58,7 +66,7 @@ public FormElement GetFormElement(CultureInfo[] supportedCultures) cultureField.Component = FormComponent.Search; cultureField.DataItem = new FormElementDataItem { - Items = new List(), + Items = [], GridBehavior = DataItemGridBehavior.Id }; foreach (var cultureInfo in supportedCultures) diff --git a/src/Core/DataDictionary/Structure/LoggerFormElementFactory.cs b/src/Core/DataDictionary/Structure/LoggerFormElementFactory.cs index 4c82636db..458abb5ee 100644 --- a/src/Core/DataDictionary/Structure/LoggerFormElementFactory.cs +++ b/src/Core/DataDictionary/Structure/LoggerFormElementFactory.cs @@ -14,45 +14,43 @@ public class LoggerFormElementFactory( IMasterDataUrlHelper urlHelper, IStringLocalizer stringLocalizer) { - private IMasterDataUrlHelper UrlHelper { get; } = urlHelper; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private DbLoggerOptions Options { get; } = options.Value; + private readonly DbLoggerOptions _options = options.Value; public FormElement GetFormElement(bool isModal) { - var formElement = new FormElement(DbLoggerElement.GetInstance(Options)) + var formElement = new FormElement(DbLoggerElement.GetInstance(_options)) { - Title = StringLocalizer["Application Log"], + Title = stringLocalizer["Application Log"], SubTitle = string.Empty, Icon = IconType.Film }; - formElement.Fields[Options.IdColumnName].VisibleExpression = "val:0"; + formElement.Fields[_options.IdColumnName].VisibleExpression = "val:0"; - formElement.Fields[Options.CategoryColumnName].VisibleExpression = "val:0"; - formElement.Fields[Options.CategoryColumnName].CssClass = "col-sm-6"; + formElement.Fields[_options.CategoryColumnName].VisibleExpression = "val:0"; + formElement.Fields[_options.CategoryColumnName].CssClass = "col-sm-6"; - formElement.Fields[Options.LevelColumnName].LineGroup = 1; - formElement.Fields[Options.LevelColumnName].CssClass = "col-sm-6"; + formElement.Fields[_options.LevelColumnName].LineGroup = 1; + formElement.Fields[_options.LevelColumnName].CssClass = "col-sm-6"; - formElement.Fields[Options.CreatedColumnName].LineGroup = 2; + formElement.Fields[_options.CreatedColumnName].LineGroup = 2; - formElement.Fields[Options.MessageColumnName].LineGroup = 3; - formElement.Fields[Options.MessageColumnName].CssClass = "col-sm-10"; + formElement.Fields[_options.MessageColumnName].LineGroup = 3; + formElement.Fields[_options.MessageColumnName].CssClass = "col-sm-10"; - var levelField = formElement.Fields[Options.LevelColumnName]; + var levelField = formElement.Fields[_options.LevelColumnName]; levelField.Component = FormComponent.ComboBox; levelField.DataItem = new FormElementDataItem { Items = [ - new("0", LogLevel.Trace.ToString(), IconType.SolidMapLocation, "#808080"), - new("1", LogLevel.Debug.ToString(), IconType.Bug, "#198754"), - new("2", LogLevel.Information.ToString(), IconType.InfoCircle, "#0d6efd"), - new("3", LogLevel.Warning.ToString(), IconType.SolidTriangleExclamation, "#ffc107"), - new("4", LogLevel.Error.ToString(), IconType.TimesCircle, "#dc3545"), - new("5", LogLevel.Critical.ToString(), IconType.Fire, "#FF5733"), - new("6", LogLevel.None.ToString(), IconType.CircleO, "#808080") + new("0", nameof(LogLevel.Trace), IconType.SolidMapLocation, "#808080"), + new("1", nameof(LogLevel.Debug), IconType.Bug, "#198754"), + new("2", nameof(LogLevel.Information), IconType.InfoCircle, "#0d6efd"), + new("3", nameof(LogLevel.Warning), IconType.SolidTriangleExclamation, "#ffc107"), + new("4", nameof(LogLevel.Error), IconType.TimesCircle, "#dc3545"), + new("5", nameof(LogLevel.Critical), IconType.Fire, "#FF5733"), + new("6", nameof(LogLevel.None), IconType.CircleO, "#808080") ], GridBehavior = DataItemGridBehavior.Icon, ShowIcon = true @@ -62,10 +60,10 @@ public FormElement GetFormElement(bool isModal) { Name = "btnClearLog", Icon = IconType.Trash, - Text = StringLocalizer["Clear Log"], + Text = stringLocalizer["Clear Log"], ShowAsButton = true, - ConfirmationMessage = StringLocalizer["Do you want to clear ALL logs?"], - UrlRedirect = UrlHelper.Action("ClearAll", "Log", new { Area = "DataDictionary", isModal }) + ConfirmationMessage = stringLocalizer["Do you want to clear ALL logs?"], + UrlRedirect = urlHelper.Action("ClearAll", "Log", new { Area = "DataDictionary", isModal }) }; formElement.Options.GridTableActions.Clear(); diff --git a/src/Core/DataManager/DataHelper.cs b/src/Core/DataManager/DataHelper.cs index c47dfde02..0b3690619 100644 --- a/src/Core/DataManager/DataHelper.cs +++ b/src/Core/DataManager/DataHelper.cs @@ -61,15 +61,10 @@ public static Dictionary GetPkValues(Element element, Dictionary foreach (var field in elementPks) { - if (!values.ContainsKey(field.Name)) + if (!values.TryGetValue(field.Name, out var value)) throw new JJMasterDataException($"Primary key from {field.Name} not entered"); - var value = values[field.Name]; - - if (value is null) - primaryKeys.Add(field.Name, DBNull.Value); - else - primaryKeys.Add(field.Name, value); + primaryKeys.Add(field.Name, value ?? DBNull.Value); } return primaryKeys; @@ -83,7 +78,7 @@ public static Dictionary GetPkValues(Element element, string par if (values == null || values.Length == 0) throw new ArgumentException("Invalid parameter or not found in values"); - var elementPks = element.Fields.ToList().FindAll(x => x.IsPk); + var elementPks = element.Fields.FindAll(x => x.IsPk); if (values.Length != elementPks.Count) throw new JJMasterDataException("Invalid primary key"); @@ -108,7 +103,7 @@ public static Dictionary GetRelationValues(Element element, Dict foreach (var entry in values) { - var matchingRelationship = relationships.FirstOrDefault(r => r.Columns.Any(c => c.FkColumn == entry.Key)); + var matchingRelationship = relationships.Find(r => r.Columns.Any(c => c.FkColumn == entry.Key)); if (matchingRelationship != null) { @@ -135,46 +130,46 @@ public static string ParsePkValues(FormElement formElement, Dictionary x.IsPk); + var elementPks = formElement.Fields.FindAll(x => x.IsPk); if (elementPks == null || elementPks.Count == 0) throw new JJMasterDataException($"Primary key not defined for dictionary {formElement.Name}"); - string name = string.Empty; + var name = string.Empty; foreach (var field in elementPks) { if (name.Length > 0) name += separator.ToString(); - if (!formValues.ContainsKey(field.Name)) + if (!formValues.TryGetValue(field.Name, out var formValue)) throw new JJMasterDataException($"Primary key {field.Name} not entered"); - string value; - + if (field.DataType is FieldType.DateTime or FieldType.Date) { - if (DateTime.TryParse(formValues[field.Name]?.ToString(), CultureInfo.CurrentCulture, DateTimeStyles.None,out DateTime dateValue)) + if (DateTime.TryParse(formValue?.ToString(), CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime dateValue)) { value = dateValue.ToString(CultureInfo.InvariantCulture); } - else if (DateTime.TryParse(formValues[field.Name]?.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.None,out DateTime invariantDateValue)) + else if (DateTime.TryParse(formValue?.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime invariantDateValue)) { value = invariantDateValue.ToString(CultureInfo.InvariantCulture); } else { - throw new JJMasterDataException($"Invalid DateTime for field {field.Name}: {formValues[field.Name]}"); + throw new JJMasterDataException($"Invalid DateTime for field {field.Name}: {formValue}"); } } else { - value = formValues[field.Name]?.ToString() ?? string.Empty; + value = formValue?.ToString() ?? string.Empty; } - + if (value.Contains(separator)) throw new JJMasterDataException($"Primary key value {value} contains invalid characters."); - + name += value; + } return name; @@ -202,7 +197,7 @@ public static string ParsePkValues(FormElement formElement, Dictionary valuesToBeReceived, Dictionary? valuesToBeCopied, bool replaceIfKeyExists = false) { - if (valuesToBeCopied == null || !valuesToBeCopied.Any()) + if (valuesToBeCopied == null || valuesToBeCopied.Count == 0) return; foreach (var entry in valuesToBeCopied) @@ -221,7 +216,7 @@ public static void CopyIntoDictionary(Dictionary valuesToBeRece public static void RemoveNullValues(Dictionary? values) { - if (values == null || !values.Any()) + if (values == null || values.Count == 0) return; var keysToRemove = new List(); diff --git a/src/Core/DataManager/Exportation/Abstractions/DataExportationWriterBase.cs b/src/Core/DataManager/Exportation/Abstractions/DataExportationWriterBase.cs index cb2f598df..f3285894b 100644 --- a/src/Core/DataManager/Exportation/Abstractions/DataExportationWriterBase.cs +++ b/src/Core/DataManager/Exportation/Abstractions/DataExportationWriterBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -67,7 +66,7 @@ protected List VisibleFields return _fields; if (Configuration.ExportAllFields) { - _fields = FormElement.Fields.ToList().FindAll(x => x.Export); + _fields = FormElement.Fields.FindAll(x => x.Export); } else @@ -157,7 +156,7 @@ private static void CreateFolderPathIfNotExits(string folderPath) } catch (Exception ex) { - string message = "Error on create directory, set a valid ExportationFolderPath on JJMasterData Options."; + const string message = "Error on create directory, set a valid ExportationFolderPath on JJMasterData Options."; throw new JJMasterDataException(message, ex); } } @@ -170,7 +169,6 @@ private static void CreateFolderPathIfNotExits(string folderPath) #endregion - public async Task RunWorkerAsync(CancellationToken token) { #if NETFRAMEWORK @@ -269,27 +267,35 @@ private string GetFilePath() var exportActionFileName = FormElement.Options.GridToolbarActions.ExportAction.FileName; if (!string.IsNullOrEmpty(exportActionFileName)) + { fileName = exportActionFileName; - + } else if (!string.IsNullOrEmpty(FormElement.Title)) + { fileName = ExpressionsService.GetExpressionValue(FormElement.Title, new FormStateData() { Values = new Dictionary(), UserValues = new Dictionary(), PageState = PageState.List })?.ToString() ?? string.Empty; - + } else if (!string.IsNullOrEmpty(FormElement.Name)) + { fileName = FormElement.Name.Trim().ToLower(); + } else + { fileName = "file"; + } fileName = StringManager.GetStringWithoutAccents(fileName); string[] escapeChars = ["/", "\\", "|", ":", "*", ">", "<", "+", "=", "&", "%", "$", "#", "@", " "]; foreach (var @char in escapeChars) + { fileName = fileName.Replace(@char, string.Empty); + } fileName = HttpUtility.UrlEncode(fileName, Encoding.UTF8); var extension = Configuration.FileExtension.ToString().ToLower(); diff --git a/src/Core/DataManager/Exportation/Abstractions/IExcelWriter.cs b/src/Core/DataManager/Exportation/Abstractions/IExcelWriter.cs index f468f08a3..14d021de3 100644 --- a/src/Core/DataManager/Exportation/Abstractions/IExcelWriter.cs +++ b/src/Core/DataManager/Exportation/Abstractions/IExcelWriter.cs @@ -7,6 +7,6 @@ public interface IExcelWriter : IExportationWriter { bool ShowBorder { get; set; } bool ShowRowStriped { get; set; } - + event AsyncEventHandler OnRenderCellAsync; } diff --git a/src/Core/DataManager/Exportation/ExcelWriter.cs b/src/Core/DataManager/Exportation/ExcelWriter.cs index c800e7c7b..cf5fa57fb 100644 --- a/src/Core/DataManager/Exportation/ExcelWriter.cs +++ b/src/Core/DataManager/Exportation/ExcelWriter.cs @@ -163,9 +163,9 @@ private async Task CreateCell(Dictionary row, FormElemen string value = string.Empty; if (field.DataBehavior is not FieldBehavior.Virtual && field.DataBehavior is not FieldBehavior.WriteOnly) { - if (row.Keys.Contains(field.Name)) + if (row.TryGetValue(field.Name, out var cellValue)) { - value = FieldFormattingService.FormatValue(field, row[field.Name]); + value = FieldFormattingService.FormatValue(field, cellValue); } } @@ -176,8 +176,7 @@ private async Task CreateCell(Dictionary row, FormElemen value = $"{value}"; else { - if (value != null) - value = value.Replace(",", "
"); + value = value?.Replace(",", "
"); } } diff --git a/src/Core/DataManager/Exportation/TextWriter.cs b/src/Core/DataManager/Exportation/TextWriter.cs index 41379b0b9..051063704 100644 --- a/src/Core/DataManager/Exportation/TextWriter.cs +++ b/src/Core/DataManager/Exportation/TextWriter.cs @@ -111,10 +111,10 @@ private async Task GenerateRows(StreamWriter sw, CancellationToken token) string value = string.Empty; if (field.DataBehavior is not FieldBehavior.Virtual && field.DataBehavior is not FieldBehavior.WriteOnly) { - if (row.Keys.Contains(field.Name)) - value = row[field.Name]?.ToString(); + if (row.TryGetValue(field.Name, out var cellValue)) + value = cellValue?.ToString(); } - + if (OnRenderCellAsync != null) { var args = new GridCellEventArgs @@ -123,9 +123,9 @@ private async Task GenerateRows(StreamWriter sw, CancellationToken token) DataRow = row, Sender = new JJText(value) }; - + await OnRenderCellAsync(this, args); - + if(args.HtmlResult != null) value = args.HtmlResult.ToString(); } diff --git a/src/Core/DataManager/Expressions/Abstractions/IAsyncExpressionProvider.cs b/src/Core/DataManager/Expressions/Abstractions/IAsyncExpressionProvider.cs index feb7b904c..1a7670545 100644 --- a/src/Core/DataManager/Expressions/Abstractions/IAsyncExpressionProvider.cs +++ b/src/Core/DataManager/Expressions/Abstractions/IAsyncExpressionProvider.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -7,5 +8,6 @@ namespace JJMasterData.Core.DataManager.Expressions.Abstractions; public interface IAsyncExpressionProvider : IExpressionProvider { - Task EvaluateAsync(string expression, Dictionary parsedValues); + Guid? ConnectionId { get; set; } + ValueTask EvaluateAsync(string expression, Dictionary parsedValues); } \ No newline at end of file diff --git a/src/Core/DataManager/Expressions/ExpressionDataAccessCommandFactory.cs b/src/Core/DataManager/Expressions/ExpressionDataAccessCommandFactory.cs index b19c03faf..2560e2b0b 100644 --- a/src/Core/DataManager/Expressions/ExpressionDataAccessCommandFactory.cs +++ b/src/Core/DataManager/Expressions/ExpressionDataAccessCommandFactory.cs @@ -22,7 +22,7 @@ public static DataAccessCommand Create(string expression, Dictionary DbType.Double, decimal => DbType.Decimal, float => DbType.Double, - string => DbType.String, + string => DbType.AnsiString, Guid => DbType.Guid, DateTime => DbType.DateTime, bool => DbType.Boolean, - _ => DbType.String + _ => DbType.AnsiString }; } } \ No newline at end of file diff --git a/src/Core/DataManager/Expressions/ExpressionParser.cs b/src/Core/DataManager/Expressions/ExpressionParser.cs index d66df49f5..a5048aab7 100644 --- a/src/Core/DataManager/Expressions/ExpressionParser.cs +++ b/src/Core/DataManager/Expressions/ExpressionParser.cs @@ -1,6 +1,5 @@ #nullable enable -using System; using System.Collections.Generic; using System.Linq; using JJMasterData.Commons.Util; @@ -14,11 +13,6 @@ namespace JJMasterData.Core.DataManager.Expressions; public class ExpressionParser(IHttpContext httpContext, ILogger logger) { - private IHttpContext HttpContext { get; } = httpContext; - private ILogger Logger { get; } = logger; - private IHttpRequest Request => HttpContext.Request; - private IHttpSession Session => HttpContext.Session; - public Dictionary ParseExpression( string? expression, FormStateData formStateData) @@ -34,7 +28,7 @@ public class ExpressionParser(IHttpContext httpContext, ILogger c.Type == field) ?? false) - parsedValue = HttpContext.User.Claims.First(c => c.Type == field).Value; + else if (httpContext.Session.HasSession() && httpContext.Session[field] != null) + parsedValue = httpContext.Session[field]; + else if (httpContext.User?.HasClaim(c => c.Type == field) ?? false) + parsedValue = httpContext.User.Claims.First(c => c.Type == field).Value; else parsedValue = string.Empty; break; diff --git a/src/Core/DataManager/Expressions/ExpressionsService.cs b/src/Core/DataManager/Expressions/ExpressionsService.cs index 51ba23ca2..f449367c7 100644 --- a/src/Core/DataManager/Expressions/ExpressionsService.cs +++ b/src/Core/DataManager/Expressions/ExpressionsService.cs @@ -23,34 +23,23 @@ public class ExpressionsService( IEncryptionService encryptionService, ILogger logger) { - private record struct Expression(string Prefix, string Content); + private readonly record struct Expression(string Prefix, string Content); - private string? _valueExpressionPrefix; - - private string ValueExpressionPrefix => _valueExpressionPrefix ??= - ExpressionProviders.First(p => p is ValueExpressionProvider).Prefix; - - private IEnumerable ExpressionProviders { get; } = expressionProviders; - private ExpressionParser ExpressionParser { get; } = expressionParser; - private IEncryptionService EncryptionService { get; } = encryptionService; - private ILogger Logger { get; } = logger; - public Dictionary ParseExpression(string expression, FormStateData formStateData) { - return ExpressionParser.ParseExpression(expression, formStateData); + return expressionParser.ParseExpression(expression, formStateData); } - + public string? ReplaceExpressionWithParsedValues( string? expression, FormStateData formStateData, - bool encryptValues = false - ) + bool encryptValues = false) { - var parsedValues = ExpressionParser.ParseExpression(expression, formStateData); + var parsedValues = expressionParser.ParseExpression(expression, formStateData); if (encryptValues) EncryptValues(parsedValues); - + if (expression != null) return ExpressionHelper.ReplaceExpression(expression, parsedValues); @@ -59,11 +48,11 @@ private record struct Expression(string Prefix, string Content); private void EncryptValues(Dictionary parsedValues) { - foreach(var kvp in parsedValues) + foreach (var kvp in parsedValues) { var value = parsedValues[kvp.Key]; - if(value is not null) - parsedValues[kvp.Key] = EncryptionService.EncryptStringWithUrlEscape(value.ToString()!); + if (value is not null) + parsedValues[kvp.Key] = encryptionService.EncryptStringWithUrlEscape(value.ToString()!); } } @@ -74,20 +63,21 @@ public bool GetBoolValue(string? expression, FormStateData formStateData) public object? GetExpressionValue(string? expression, FormStateData formStateData) { - var extractedExpression = GetExpressionFromString(expression); - var (expressionType, expressionValue) = extractedExpression; + var (expressionType, expressionValue) = GetExpressionFromString(expression); - if (ExpressionProviders.FirstOrDefault(p => p.Prefix == expressionType && p is ISyncExpressionProvider) is + if (expressionProviders.FirstOrDefault(p => p.Prefix == expressionType && p is ISyncExpressionProvider) is not ISyncExpressionProvider provider) + { throw new JJMasterDataException($"Expression type not supported: {expressionType}."); + } object? result; try { - Logger.LogExpression(expression); + logger.LogExpression(expression); - var parsedValues = ExpressionParser.ParseExpression(expression, formStateData); + var parsedValues = expressionParser.ParseExpression(expression, formStateData); result = provider.Evaluate(expressionValue, parsedValues); } @@ -95,7 +85,7 @@ public bool GetBoolValue(string? expression, FormStateData formStateData) { var exception = new ExpressionException("Unhandled exception at a expression provider.", ex); - Logger.LogExpressionError(exception, provider.Prefix, expression); + logger.LogExpressionError(exception, provider.Prefix, expression); throw exception; } @@ -103,41 +93,39 @@ public bool GetBoolValue(string? expression, FormStateData formStateData) return result; } - public Task GetTriggerValueAsync(FormElementFieldSelector fieldSelector, FormStateData formStateData) + public ValueTask GetTriggerValueAsync(FormElementFieldSelector fieldSelector, FormStateData formStateData) { var field = fieldSelector.Field; - return GetExpressionValueAsync(fieldSelector,field.TriggerExpression, formStateData); + return GetExpressionValueAsync(fieldSelector, field.TriggerExpression, formStateData); } - - public Task GetDefaultValueAsync(FormElementFieldSelector fieldSelector, FormStateData formStateData) + + public ValueTask GetDefaultValueAsync(FormElementFieldSelector fieldSelector, FormStateData formStateData) { var field = fieldSelector.Field; - return GetExpressionValueAsync(fieldSelector,field.DefaultValue, formStateData); + return GetExpressionValueAsync(fieldSelector, field.DefaultValue, formStateData); } - - private async Task GetExpressionValueAsync( + + private async ValueTask GetExpressionValueAsync( FormElementFieldSelector fieldSelector, string? expression, FormStateData formStateData) { - var extractedExpression = GetExpressionFromString(expression); - var (expressionType, expressionValue) = extractedExpression; + var (expressionType, expressionValue) = GetExpressionFromString(expression); - if (ExpressionProviders.FirstOrDefault(p => p.Prefix == expressionType && p is IAsyncExpressionProvider) is not + if (expressionProviders.FirstOrDefault(p => p.Prefix == expressionType && p is IAsyncExpressionProvider) is not IAsyncExpressionProvider provider) { throw new JJMasterDataException($"Expression type not supported: {expressionType}"); } var field = fieldSelector.Field; - + try { - var parsedValues = ExpressionParser.ParseExpression(expression, formStateData); + var parsedValues = expressionParser.ParseExpression(expression, formStateData); + + provider.ConnectionId = fieldSelector.FormElement.ConnectionId; - if(provider is SqlExpressionProvider sqlProvider) - sqlProvider.ConnectionId = fieldSelector.FormElement.ConnectionId; - var result = await provider.EvaluateAsync(expressionValue, parsedValues); if (result is string stringResult) { @@ -158,7 +146,7 @@ FieldType.Float when double.TryParse(stringResult.Trim(), var exception = new ExpressionException($"Unhandled exception at a expression provider.\nField: {field.Name}", ex); - Logger.LogExpressionErrorWithField(exception, provider.Prefix, expression, field.Name); + logger.LogExpressionErrorWithField(exception, provider.Prefix, expression, field.Name); throw exception; } @@ -166,16 +154,16 @@ FieldType.Float when double.TryParse(stringResult.Trim(), private Expression GetExpressionFromString(string? expression) { - var splitExpression = expression?.Split([':'], 2) ; + var splitExpression = expression?.Split([':'], 2); if (splitExpression?.Length < 2) - return new Expression(ValueExpressionPrefix, expression ?? string.Empty); - + return new Expression(ValueExpressionProvider.Prefix, expression ?? string.Empty); + var prefix = splitExpression?[0]; - if (!ExpressionProviders.GetProvidersPrefixes().Contains(prefix) || splitExpression is null) - return new Expression(ValueExpressionPrefix, expression ?? string.Empty); - + if (splitExpression is null || !expressionProviders.GetProvidersPrefixes().Contains(prefix)) + return new Expression(ValueExpressionProvider.Prefix, expression ?? string.Empty); + return new Expression(splitExpression[0], splitExpression[1]); } diff --git a/src/Core/DataManager/Expressions/Providers/DefaultExpressionProvider.cs b/src/Core/DataManager/Expressions/Providers/DefaultExpressionProvider.cs index fc42cf214..ce868616f 100644 --- a/src/Core/DataManager/Expressions/Providers/DefaultExpressionProvider.cs +++ b/src/Core/DataManager/Expressions/Providers/DefaultExpressionProvider.cs @@ -1,27 +1,50 @@ #nullable enable +using System; using System.Collections.Generic; -using System.Data; using System.Threading.Tasks; - +using JJMasterData.Core.Configuration.Options; using JJMasterData.Core.DataManager.Expressions.Abstractions; +using Microsoft.Extensions.Options; +using NCalc.Factories; namespace JJMasterData.Core.DataManager.Expressions.Providers; -public sealed class DefaultExpressionProvider : ISyncExpressionProvider, IAsyncExpressionProvider +public sealed class DefaultExpressionProvider( + IExpressionFactory expressionFactory, + IAsyncExpressionFactory asyncExpressionFactory, + IServiceProvider serviceProvider, + IOptions options) : ISyncExpressionProvider, IAsyncExpressionProvider { public string Prefix => "exp"; public string Title => "Expression"; - private static readonly DataTable ExpressionsDataTable = new(); + public Guid? ConnectionId { get; set; } - public object Evaluate(string expression, Dictionary parsedValues) + public object? Evaluate(string expression, Dictionary parsedValues) { var replacedExpression = ExpressionHelper.ReplaceExpression(expression, parsedValues); - var result = ExpressionsDataTable.Compute(replacedExpression, string.Empty); - return result!; + var ncalcExpression = expressionFactory.Create(replacedExpression, options.Value.ExpressionContext with + { + StaticParameters = new Dictionary + { + {"serviceProvider", serviceProvider} + } + }); + return ncalcExpression.Evaluate(); } - public Task EvaluateAsync(string expression, Dictionary parsedValues) - => Task.FromResult(Evaluate(expression,parsedValues)); + public ValueTask EvaluateAsync(string expression, Dictionary parsedValues) + { + var replacedExpression = ExpressionHelper.ReplaceExpression(expression, parsedValues); + var ncalcExpression = asyncExpressionFactory.Create(replacedExpression, options.Value.AsyncExpressionsContext with + { + StaticParameters = new Dictionary + { + {"serviceProvider", serviceProvider}, + {"connectionId", ConnectionId} + } + }); + return ncalcExpression.EvaluateAsync(); + } } \ No newline at end of file diff --git a/src/Core/DataManager/Expressions/Providers/SqlExpressionProvider.cs b/src/Core/DataManager/Expressions/Providers/SqlExpressionProvider.cs index 5a91b0973..d78f9d87e 100644 --- a/src/Core/DataManager/Expressions/Providers/SqlExpressionProvider.cs +++ b/src/Core/DataManager/Expressions/Providers/SqlExpressionProvider.cs @@ -2,9 +2,7 @@ using System; using System.Collections.Generic; -using System.Data; using System.Threading.Tasks; -using JJMasterData.Commons.Data; using JJMasterData.Commons.Data.Entity.Repository.Abstractions; using JJMasterData.Core.DataManager.Expressions.Abstractions; @@ -16,12 +14,12 @@ public sealed class SqlExpressionProvider(IEntityRepository entityRepository) : public string Title => "SQL"; public Guid? ConnectionId { get; set; } - public async Task EvaluateAsync(string expression, Dictionary parsedValues) + public async ValueTask EvaluateAsync(string expression, Dictionary parsedValues) { var command = ExpressionDataAccessCommandFactory.Create(expression, parsedValues); var result = await entityRepository.GetResultAsync(command,ConnectionId); - + return result; } } diff --git a/src/Core/DataManager/Expressions/Providers/ValueExpressionProvider.cs b/src/Core/DataManager/Expressions/Providers/ValueExpressionProvider.cs index f2e26bd52..32e338ae6 100644 --- a/src/Core/DataManager/Expressions/Providers/ValueExpressionProvider.cs +++ b/src/Core/DataManager/Expressions/Providers/ValueExpressionProvider.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using System.Collections.Generic; using System.Threading.Tasks; using JJMasterData.Core.DataManager.Expressions.Abstractions; @@ -7,9 +8,12 @@ namespace JJMasterData.Core.DataManager.Expressions.Providers; public sealed class ValueExpressionProvider : IAsyncExpressionProvider, ISyncExpressionProvider { - public string Prefix => "val"; + public const string Prefix = "val"; + + public Guid? ConnectionId { get; set; } + + string IExpressionProvider.Prefix => Prefix; public string Title => "Value"; - public object Evaluate(string expression, Dictionary parsedValues) { if (expression.Contains(ExpressionHelper.Begin.ToString())) @@ -18,6 +22,6 @@ public object Evaluate(string expression, Dictionary parsedValu return expression.Trim(); } - public Task EvaluateAsync(string expression, Dictionary parsedValues) - => Task.FromResult(Evaluate(expression, parsedValues)); + public ValueTask EvaluateAsync(string expression, Dictionary parsedValues) + => new(Evaluate(expression, parsedValues)); } \ No newline at end of file diff --git a/src/Core/DataManager/IO/FormFileManager.cs b/src/Core/DataManager/IO/FormFileManager.cs index 10590aa89..799c03c92 100644 --- a/src/Core/DataManager/IO/FormFileManager.cs +++ b/src/Core/DataManager/IO/FormFileManager.cs @@ -18,7 +18,6 @@ public class FormFileManager(string memoryFilesSessionName, IStringLocalizer stringLocalizer, ILogger logger) { - private IHttpContext HttpContext { get; } = httpContext; public event EventHandler OnBeforeCreateFile; public event EventHandler OnBeforeDeleteFile; public event EventHandler OnBeforeRenameFile; @@ -45,14 +44,10 @@ public class FormFileManager(string memoryFilesSessionName, /// public string FolderPath { get; set; } - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - - private ILogger Logger { get; } = logger; - public List MemoryFiles { - get => HttpContext.Session.GetSessionValue>(MemoryFilesSessionName); - set => HttpContext.Session.SetSessionValue(MemoryFilesSessionName, value); + get => httpContext.Session.GetSessionValue>(MemoryFilesSessionName); + set => httpContext.Session.SetSessionValue(MemoryFilesSessionName, value); } public List GetFiles() @@ -71,14 +66,14 @@ public void RenameFile(string currentName, string newName) throw new ArgumentNullException(nameof(currentName)); if (string.IsNullOrWhiteSpace(newName)) - throw new ArgumentNullException(StringLocalizer["Required file name"]); + throw new ArgumentNullException(stringLocalizer["Required file name"]); if (!FileIO.GetFileNameExtension(currentName).Equals(FileIO.GetFileNameExtension(newName))) - throw new JJMasterDataException(StringLocalizer["The file extension must remain the same"]); + throw new JJMasterDataException(stringLocalizer["The file extension must remain the same"]); var files = GetFiles(); if (files.Exists(x => x.Content.FileName.Equals(newName))) - throw new JJMasterDataException(StringLocalizer["A file with the name {0} already exists", newName]); + throw new JJMasterDataException(stringLocalizer["A file with the name {0} already exists", newName]); if (OnBeforeRenameFile != null) { @@ -97,7 +92,7 @@ public void RenameFile(string currentName, string newName) { var file = files.Find(x => x.Content.FileName.Equals(currentName)); if (file == null) - throw new JJMasterDataException(StringLocalizer["file {0} not found!", currentName]); + throw new JJMasterDataException(stringLocalizer["file {0} not found!", currentName]); files.Remove(file); @@ -114,7 +109,7 @@ public void RenameFile(string currentName, string newName) public FormFileInfo GetFile(string fileName) { var files = GetFiles(); - var file = files.FirstOrDefault(x => fileName.Equals(x.Content.FileName) || fileName.Equals(x.OldName)); + var file = files.Find(x => fileName.Equals(x.Content.FileName) || fileName.Equals(x.OldName)); return file; } @@ -140,7 +135,7 @@ public void CreateFile(FormFileContent fileContent, bool replaceIfExists) if (!string.IsNullOrEmpty(errorMessage)) { var exception = new JJMasterDataException(errorMessage); - Logger.LogError(exception,"Error OnBeforeCreateFile"); + logger.LogError(exception,"Error OnBeforeCreateFile"); throw exception; } @@ -185,12 +180,11 @@ public void DeleteFile(string fileName) { var args = new FormDeleteFileEventArgs(fileName); OnBeforeDeleteFile.Invoke(this, args); - if (!string.IsNullOrEmpty(args.ErrorMessage)) { var exception = new JJMasterDataException(args.ErrorMessage); - Logger.LogError(exception, "Error OnBeforeDeleteFile"); + logger.LogError(exception, "Error OnBeforeDeleteFile"); throw exception; } } @@ -304,9 +298,9 @@ private void SavePhysicalFile(FormFileContent file) if (!Directory.Exists(FolderPath)) Directory.CreateDirectory(FolderPath); - string fileFullName = Path.Combine(FolderPath, file.FileName); - var ms = new MemoryStream(file.Bytes); - var fileStream = File.Create(fileFullName); + var fileFullName = Path.Combine(FolderPath, file.FileName); + using var ms = new MemoryStream(file.Bytes); + using var fileStream = File.Create(fileFullName); ms.Seek(0, SeekOrigin.Begin); ms.CopyTo(fileStream); fileStream.Close(); diff --git a/src/Core/DataManager/IO/FormFileService.cs b/src/Core/DataManager/IO/FormFileService.cs index f5be209a3..b945d0644 100644 --- a/src/Core/DataManager/IO/FormFileService.cs +++ b/src/Core/DataManager/IO/FormFileService.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using JJMasterData.Core.DataDictionary.Models; namespace JJMasterData.Core.DataManager.IO; @@ -10,7 +9,7 @@ public class FormFileService(FormFileManagerFactory formFileManagerFactory) public void SaveFormMemoryFiles(FormElement formElement, Dictionary primaryKeys) { - var uploadFields = formElement.Fields.ToList().FindAll(x => x.Component == FormComponent.File); + var uploadFields = formElement.Fields.FindAll(x => x.Component == FormComponent.File); if (uploadFields.Count == 0) return; @@ -25,7 +24,7 @@ public void SaveFormMemoryFiles(FormElement formElement, Dictionary primaryKeys) { - var uploadFields = formElement.Fields.ToList().FindAll(x => x.Component == FormComponent.File); + var uploadFields = formElement.Fields.FindAll(x => x.Component == FormComponent.File); if (uploadFields.Count == 0) return; diff --git a/src/Core/DataManager/Importation/DataImportationWorker.cs b/src/Core/DataManager/Importation/DataImportationWorker.cs index e18650d5f..af9082b5a 100644 --- a/src/Core/DataManager/Importation/DataImportationWorker.cs +++ b/src/Core/DataManager/Importation/DataImportationWorker.cs @@ -45,7 +45,7 @@ public class DataImportationWorker( public ProcessOptions ProcessOptions { get; set; } - private CultureInfo Culture { get; set; } = Thread.CurrentThread.CurrentUICulture; + private CultureInfo Culture { get; } = Thread.CurrentThread.CurrentUICulture; public FormElement FormElement { get; } = context.FormElement; private DataContext DataContext { get; } = context.DataContext; diff --git a/src/Core/DataManager/Models/DataContext.cs b/src/Core/DataManager/Models/DataContext.cs index 6b03526d5..eb3619349 100644 --- a/src/Core/DataManager/Models/DataContext.cs +++ b/src/Core/DataManager/Models/DataContext.cs @@ -6,19 +6,18 @@ namespace JJMasterData.Core.DataManager.Models; public class DataContext { - public DataContextSource Source { get; private set; } + public DataContextSource Source { get; } - public string? UserId { get; private set; } + public string? UserId { get; } public string? IpAddress { get; internal set; } - + public string? BrowserInfo { get; internal set; } public DataContext() { - } - + public DataContext(IHttpRequest request, DataContextSource source, string? userId) { Source = source; @@ -26,5 +25,4 @@ public DataContext(IHttpRequest request, DataContextSource source, string? userI IpAddress = request.UserHostAddress; BrowserInfo = request.UserAgent; } - } \ No newline at end of file diff --git a/src/Core/DataManager/Models/DataQuery.cs b/src/Core/DataManager/Models/DataQuery.cs index 8d1ed09a1..cc780f1b9 100644 --- a/src/Core/DataManager/Models/DataQuery.cs +++ b/src/Core/DataManager/Models/DataQuery.cs @@ -1,7 +1,6 @@ #nullable enable using System; -using System.Diagnostics.CodeAnalysis; namespace JJMasterData.Core.DataManager.Models; diff --git a/src/Core/DataManager/Models/FormContext.cs b/src/Core/DataManager/Models/FormContext.cs deleted file mode 100644 index 5002cf4cf..000000000 --- a/src/Core/DataManager/Models/FormContext.cs +++ /dev/null @@ -1,28 +0,0 @@ -#nullable enable - -using System.Collections.Generic; -using System.Diagnostics; -using JJMasterData.Core.DataDictionary.Models; - -namespace JJMasterData.Core.DataManager.Models; - -[DebuggerStepThrough] -internal class FormContext(Dictionary values, Dictionary errors, PageState pageState) -{ - public FormContext(Dictionary values, PageState pageState) - : this(values, new Dictionary(), pageState) - { - } - - public Dictionary Values { get; } = values; - public Dictionary Errors { get; } = errors; - public PageState PageState { get; } = pageState; - - public void Deconstruct(out Dictionary values, out Dictionary errors, - out PageState pageState) - { - values = Values; - errors = Errors; - pageState = PageState; - } -} \ No newline at end of file diff --git a/src/Core/DataManager/Services/AuditLogService.cs b/src/Core/DataManager/Services/AuditLogService.cs index 49a9014f7..e7f548649 100644 --- a/src/Core/DataManager/Services/AuditLogService.cs +++ b/src/Core/DataManager/Services/AuditLogService.cs @@ -18,7 +18,10 @@ namespace JJMasterData.Core.DataManager.Services; -public class AuditLogService(IEntityRepository entityRepository, IOptionsSnapshot options, IStringLocalizer stringLocalizer) +public class AuditLogService( + IEntityRepository entityRepository, + IOptionsSnapshot options, + IStringLocalizer stringLocalizer) { public const string DicId = "id"; public const string DicName = "dictionary"; @@ -30,10 +33,6 @@ public class AuditLogService(IEntityRepository entityRepository, IOptionsSnapsho public const string DicIp = "ip"; public const string DicBrowser = "browser"; public const string DicJson = "json"; - - private IEntityRepository EntityRepository { get; } = entityRepository; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private MasterDataCoreOptions Options { get; } = options.Value; public async Task LogAsync(Element element,DataContext dataContext, Dictionary formValues, CommandOperation action) { @@ -52,14 +51,14 @@ public async Task LogAsync(Element element,DataContext dataContext, DictionaryformValues) @@ -74,7 +73,7 @@ private static string GetJsonFields(DictionaryformValues) public static string GetKey(Element element, Dictionaryvalues) { var key = new StringBuilder(); - var pks = element.Fields.ToList().FindAll(x => x.IsPk); + var pks = element.Fields.FindAll(x => x.IsPk); foreach (var field in pks) { if (key.Length > 0) @@ -88,8 +87,8 @@ public static string GetKey(Element element, Dictionaryvalues) public Element GetElement(Guid? connectionId) { - string tableName = Options.AuditLogTableName; - var element = new Element(tableName, StringLocalizer["Audit Log"]); + string tableName = options.Value.AuditLogTableName; + var element = new Element(tableName, stringLocalizer["Audit Log"]); element.Fields.AddPk(DicId, "Id", FieldType.Int, 1, true, FilterMode.Equal); element.Fields.Add(DicName, "Dictionary Name", FieldType.NVarchar, 64, true, FilterMode.Equal); element.Fields.Add(DicAction, "Action", FieldType.Int, 1, true, FilterMode.Equal); diff --git a/src/Core/DataManager/Services/DataItemService.cs b/src/Core/DataManager/Services/DataItemService.cs index 7da1d5288..272d4f1ca 100644 --- a/src/Core/DataManager/Services/DataItemService.cs +++ b/src/Core/DataManager/Services/DataItemService.cs @@ -12,7 +12,6 @@ using JJMasterData.Core.DataDictionary; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.DataManager.Expressions.Providers; using JJMasterData.Core.DataManager.Models; using Microsoft.Extensions.Logging; @@ -57,35 +56,37 @@ private static DataItemType GetDataItemType(FormElementDataItem dataItem) private static IEnumerable GetItemsValues(FormElementDataItem dataItem, string? searchId, string? searchText) { - if (dataItem.Items != null) - foreach (var item in dataItem.Items) + if (dataItem.Items == null) + yield break; + + foreach (var item in dataItem.Items) + { + if (searchId is not null) { - if (searchId is not null) - { - if (item.Id == searchId) - { - yield return item; - } - } - else if (searchText is not null) + if (item.Id == searchId) { - if (item.Description?.ToLower().Contains(searchText.ToLower()) ?? false) - { - yield return item; - } + yield return item; } - else + } + else if (searchText is not null) + { + if (item.Description?.ToLowerInvariant().Contains(searchText.ToLowerInvariant()) ?? false) { yield return item; } } + else + { + yield return item; + } + } } private async Task> GetElementMapValues(FormElementDataItem dataItem, DataQuery dataQuery) { - FormStateData formStateData = dataQuery.FormStateData; - string? searchId = dataQuery.SearchId; - string? searchText = dataQuery.SearchText; + var formStateData = dataQuery.FormStateData; + var searchId = dataQuery.SearchId; + var searchText = dataQuery.SearchText; var elementMap = dataItem.ElementMap; var values = await elementMapService.GetDictionaryList(elementMap!, searchId, formStateData); @@ -114,7 +115,7 @@ private async Task> GetElementMapValues(FormElementDataItem if (elementMap.GroupFieldName != null) item.Group = value[elementMap.GroupFieldName]?.ToString(); - if (searchText == null || item.Description!.ToLower().Contains(searchText.ToLower())) + if (searchText == null || item.Description!.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0) { result.Add(item); } @@ -127,10 +128,10 @@ private async Task> GetSqlCommandValues( FormElementDataItem dataItem, DataQuery dataQuery) { - FormStateData formStateData = dataQuery.FormStateData; - string? searchId = dataQuery.SearchId; - string? searchText = dataQuery.SearchText; - Guid? connectionId = dataQuery.ConnectionId; + var formStateData = dataQuery.FormStateData; + var searchId = dataQuery.SearchId; + var searchText = dataQuery.SearchText; + var connectionId = dataQuery.ConnectionId; var command = GetDataItemCommand(dataItem, formStateData, searchText, searchId); @@ -151,7 +152,7 @@ private async Task> GetSqlCommandValues( foreach (DataRow row in dataTable.Rows) { var item = new DataItemValue(); - item.Id = row[0].ToString()!; + item.Id = row[0].ToString(); if (row.Table.Columns.Count == 1) { diff --git a/src/Core/DataManager/Services/ElementMapService.cs b/src/Core/DataManager/Services/ElementMapService.cs index ebe4b49a8..bb5fb6203 100644 --- a/src/Core/DataManager/Services/ElementMapService.cs +++ b/src/Core/DataManager/Services/ElementMapService.cs @@ -10,24 +10,23 @@ namespace JJMasterData.Core.DataManager.Services; -public class ElementMapService(IDataDictionaryRepository dataDictionaryRepository,IEntityRepository entityRepository,ExpressionsService expressionsService) +public class ElementMapService( + IDataDictionaryRepository dataDictionaryRepository, + IEntityRepository entityRepository, + ExpressionsService expressionsService) { - private IDataDictionaryRepository DataDictionaryRepository { get; } = dataDictionaryRepository; - private IEntityRepository EntityRepository { get; } = entityRepository; - private ExpressionsService ExpressionsService { get; } = expressionsService; - public async Task> GetFieldsAsync(DataElementMap elementMap, object? value, FormStateData? formStateData) { - var childElement = await DataDictionaryRepository.GetFormElementAsync(elementMap.ElementName); + var childElement = await dataDictionaryRepository.GetFormElementAsync(elementMap.ElementName); var filters = GetFilters(elementMap, value, formStateData); - return await EntityRepository.GetFieldsAsync(childElement, filters); + return await entityRepository.GetFieldsAsync(childElement, filters); } public async Task>> GetDictionaryList(DataElementMap elementMap, object? value, FormStateData formStateData) { - var childElement = await DataDictionaryRepository.GetFormElementAsync(elementMap.ElementName); + var childElement = await dataDictionaryRepository.GetFormElementAsync(elementMap.ElementName); var filters = GetFilters(elementMap, value, formStateData); - return await EntityRepository.GetDictionaryListAsync(childElement, new EntityParameters + return await entityRepository.GetDictionaryListAsync(childElement, new EntityParameters { Filters = filters! }); @@ -48,7 +47,7 @@ private Dictionary GetFilters( { if (formStateData != null) { - var filterParsed = ExpressionsService.GetExpressionValue(filter.Value.ToString(), formStateData) ?? string.Empty; + var filterParsed = expressionsService.GetExpressionValue(filter.Value.ToString(), formStateData) ?? string.Empty; filters[filter.Key] = filterParsed; } } diff --git a/src/Core/DataManager/Services/FieldFormattingService.cs b/src/Core/DataManager/Services/FieldFormattingService.cs index 3d1965c38..8fb33939c 100644 --- a/src/Core/DataManager/Services/FieldFormattingService.cs +++ b/src/Core/DataManager/Services/FieldFormattingService.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Threading.Tasks; using System.Web; using JJMasterData.Commons.Data.Entity.Models; @@ -15,23 +13,17 @@ namespace JJMasterData.Core.DataManager.Services; public class FieldFormattingService(DataItemService dataItemService, LookupService lookupService) { - private DataItemService DataItemService { get; } = dataItemService; - private LookupService LookupService { get; } = lookupService; - - public async Task FormatGridValueAsync( - FormElementFieldSelector fieldSelector, + public async ValueTask FormatGridValueAsync( + FormElementFieldSelector fieldSelector, FormStateData formStateData) { var field = fieldSelector.Field; - + formStateData.Values.TryGetValue(field.Name, out var value); - + if (value == null || value == DBNull.Value) return string.Empty; - if (field.EncodeHtml) - value = HttpUtility.HtmlEncode(value); - string stringValue; switch (field.Component) { @@ -41,36 +33,39 @@ public async Task FormatGridValueAsync( break; case FormComponent.Currency: CultureInfo cultureInfo; - if (field.Attributes.TryGetValue(FormElementField.CultureInfoAttribute, out var cultureInfoName) + if (field.Attributes.TryGetValue(FormElementField.CultureInfoAttribute, out var cultureInfoName) && !string.IsNullOrEmpty(cultureInfoName?.ToString())) - cultureInfo = CultureInfo.GetCultureInfo(cultureInfoName.ToString()); + cultureInfo = CultureInfo.GetCultureInfo(cultureInfoName.ToString()!); else cultureInfo = CultureInfo.CurrentUICulture; - - if (double.TryParse(value?.ToString(),NumberStyles.Currency,cultureInfo, out var currencyValue)) - stringValue = currencyValue.ToString($"C{field.NumberOfDecimalPlaces}", cultureInfo); + + if (value is double doubleValue || double.TryParse(value.ToString(), NumberStyles.Currency, cultureInfo, out doubleValue)) + stringValue = doubleValue.ToString($"C{field.NumberOfDecimalPlaces}", cultureInfo); else stringValue = null; break; case FormComponent.Lookup - when field.DataItem is { GridBehavior: not DataItemGridBehavior.Id}: + when field.DataItem is { GridBehavior: not DataItemGridBehavior.Id }: var allowOnlyNumerics = field.DataType is FieldType.Int or FieldType.Float; - stringValue = await LookupService.GetDescriptionAsync(field.DataItem.ElementMap!, formStateData, value.ToString(), allowOnlyNumerics); + stringValue = await lookupService.GetDescriptionAsync(field.DataItem.ElementMap!, formStateData, + value.ToString(), allowOnlyNumerics); break; case FormComponent.CheckBox: stringValue = StringManager.ParseBool(value) ? "Sim" : "Não"; break; case FormComponent.Search or FormComponent.ComboBox or FormComponent.RadioButtonGroup - when field.DataItem is { GridBehavior: not DataItemGridBehavior.Id }: - + when field.DataItem is { GridBehavior: not DataItemGridBehavior.Id }: + + var searchId = value.ToString()?.Trim(); + var dataQuery = new DataQuery(formStateData, fieldSelector.FormElement.ConnectionId) { - SearchId = value?.ToString() + SearchId = searchId }; - - var searchBoxValues = await DataItemService.GetValuesAsync(field.DataItem, dataQuery); - var rowValue = searchBoxValues.FirstOrDefault(v => v.Id == value?.ToString()); - + + var searchBoxValues = await dataItemService.GetValuesAsync(field.DataItem, dataQuery); + var rowValue = searchBoxValues.Find(v => + string.Equals(v.Id.Trim(), searchId, StringComparison.InvariantCultureIgnoreCase)); return rowValue?.Description ?? rowValue?.Id ?? string.Empty; case FormComponent.Email: stringValue = GetEmailLink(value?.ToString()); @@ -79,7 +74,10 @@ public async Task FormatGridValueAsync( stringValue = FormatValue(field, value); break; } - + + if (field.EncodeHtml) + stringValue = HttpUtility.HtmlEncode(stringValue); + return stringValue ?? string.Empty; } @@ -87,18 +85,19 @@ private static string GetEmailLink(string value) { if (string.IsNullOrEmpty(value)) return string.Empty; - - var a = new A(); + + var a = new HtmlBuilder(HtmlTag.A); a.WithAttribute("href", $"mailto:{value}"); a.AppendText(value); - + return a.ToString(); } private static string GetCurrencyValueAsString(FormElementField field, object value) { CultureInfo cultureInfo; - if (field.Attributes.TryGetValue(FormElementField.CultureInfoAttribute, out var cultureInfoName) && !string.IsNullOrEmpty(cultureInfoName?.ToString())) + if (field.Attributes.TryGetValue(FormElementField.CultureInfoAttribute, out var cultureInfoName) && + !string.IsNullOrEmpty(cultureInfoName?.ToString())) cultureInfo = CultureInfo.GetCultureInfo(cultureInfoName.ToString()); else cultureInfo = CultureInfo.CurrentUICulture; @@ -111,7 +110,7 @@ private static string GetCurrencyValueAsString(FormElementField field, object va } else if (field.DataType == FieldType.Int) { - if (int.TryParse(value.ToString(), NumberStyles.Currency, cultureInfo,out var intVal)) + if (int.TryParse(value.ToString(), NumberStyles.Currency, cultureInfo, out var intVal)) stringValue = intVal.ToString("0", cultureInfo); } else @@ -127,13 +126,13 @@ private static string GetNumericValueAsString(FormElementField field, object val string stringValue = null; if (field.DataType == FieldType.Float) { - if (double.TryParse(value.ToString(), out var doubleValue)) + if (value is double doubleValue || double.TryParse(value.ToString(), out doubleValue)) stringValue = doubleValue.ToString($"N{field.NumberOfDecimalPlaces}"); } else if (field.DataType == FieldType.Int) { - if (int.TryParse(value.ToString(),out var intVal)) - stringValue = intVal.ToString("0"); + if (value is int intValue || int.TryParse(value.ToString(), out intValue)) + stringValue = intValue.ToString("0"); } else { @@ -142,7 +141,7 @@ private static string GetNumericValueAsString(FormElementField field, object val return stringValue; } - + public static string FormatValue(FormElementField field, object value) { if (value == null) @@ -169,6 +168,7 @@ public static string FormatValue(FormElementField field, object value) return GetCurrencyValueAsString(field, value); } } + break; case FormComponent.Slider: case FormComponent.Number: @@ -180,6 +180,7 @@ public static string FormatValue(FormElementField field, object value) return GetNumericValueAsString(field, value); } } + break; case FormComponent.Hour: if (TimeSpan.TryParse(stringValue, out var timeSpan)) @@ -191,20 +192,22 @@ public static string FormatValue(FormElementField field, object value) switch (type) { case FieldType.Date: - { - var dVal = DateTime.Parse(stringValue); - stringValue = dVal == DateTime.MinValue ? string.Empty : dVal.ToString(DateTimeFormatInfo.CurrentInfo.ShortDatePattern); - break; - } + { + if (DateTime.TryParse(stringValue, out var dateValue)) + stringValue = dateValue.ToString(DateTimeFormatInfo.CurrentInfo.ShortDatePattern); + break; + } case FieldType.DateTime or FieldType.DateTime2: + { + if (DateTime.TryParse(stringValue, out var dateValue)) { - var dateValue = DateTime.Parse(stringValue); - stringValue = dateValue == DateTime.MinValue - ? string.Empty - : dateValue.ToString( + stringValue = + dateValue.ToString( $"{DateTimeFormatInfo.CurrentInfo.ShortDatePattern} {DateTimeFormatInfo.CurrentInfo.ShortTimePattern}"); - break; } + + break; + } } break; diff --git a/src/Core/DataManager/Services/FieldValidationService.cs b/src/Core/DataManager/Services/FieldValidationService.cs index f8a0135cb..fc8b59aab 100644 --- a/src/Core/DataManager/Services/FieldValidationService.cs +++ b/src/Core/DataManager/Services/FieldValidationService.cs @@ -12,20 +12,19 @@ namespace JJMasterData.Core.DataManager.Services; -public class FieldValidationService(ExpressionsService expressionsService, IStringLocalizer localizer) +public class FieldValidationService( + ExpressionsService expressionsService, + IStringLocalizer localizer) { - private ExpressionsService ExpressionsService { get; } = expressionsService; - private IStringLocalizer Localizer { get; } = localizer; - public Dictionary ValidateFields( - FormElement formElement, - Dictionary formValues, - PageState pageState, + FormElement formElement, + Dictionary formValues, + PageState pageState, bool enableErrorLink) { if (formElement == null) throw new ArgumentNullException(nameof(formElement)); - + if (formValues == null) throw new ArgumentNullException(nameof(formValues)); @@ -33,24 +32,25 @@ public Dictionary ValidateFields( var formState = new FormStateData(formValues, pageState); foreach (var field in formElement.Fields) { - var isVisible = ExpressionsService.GetBoolValue(field.VisibleExpression, formState); + var isVisible = expressionsService.GetBoolValue(field.VisibleExpression, formState); if (!isVisible) continue; - var isEnabled = ExpressionsService.GetBoolValue(field.EnableExpression, formState); + var isEnabled = expressionsService.GetBoolValue(field.EnableExpression, formState); if (!isEnabled) continue; - object value; - if (formValues.ContainsKey(field.Name) && formValues[field.Name] != null) - value = formValues[field.Name]; - else + + if (!formValues.TryGetValue(field.Name, out var value) || value == null) + { value = ""; + } var error = ValidateField(field, field.Name, value, enableErrorLink); if (!string.IsNullOrEmpty(error)) errors.Add(field.Name, error); } + return errors; } @@ -67,7 +67,7 @@ public string ValidateField(FormElementField field, string fieldId, object value { if (field.IsRequired || field.IsPk) { - error = Localizer["{0} field is required", fieldName]; + error = localizer["{0} field is required", fieldName]; } } else @@ -87,7 +87,7 @@ private string ValidateComponent(FormElementField field, object value, string fi { case FormComponent.Email: if (!Validate.ValidEmail(valueString)) - return Localizer["{0} field has an invalid email", fieldName]; + return localizer["{0} field has an invalid email", fieldName]; break; case FormComponent.Hour: var hourFormat = valueString.Length == 5 ? "HH:mm" : "HH:mm:ss"; @@ -99,35 +99,35 @@ private string ValidateComponent(FormElementField field, object value, string fi out _); if (!valid) { - return Localizer["{0} field has an invalid time", fieldName]; + return localizer["{0} field has an invalid time", fieldName]; } break; case FormComponent.Cnpj: if (!Validate.ValidCnpj(valueString)) { - return Localizer["{0} field has an invalid value", fieldName]; + return localizer["{0} field has an invalid value", fieldName]; } break; case FormComponent.Cpf: if (!Validate.ValidCpf(valueString)) { - return Localizer["{0} field has an invalid value", fieldName]; + return localizer["{0} field has an invalid value", fieldName]; } break; case FormComponent.CnpjCpf: if (!Validate.ValidCpfCnpj(valueString)) { - return Localizer["{0} field has an invalid value", fieldName]; + return localizer["{0} field has an invalid value", fieldName]; } break; case FormComponent.Tel: if (!Validate.ValidTel(valueString)) { - return Localizer["{0} field has an invalid phone", fieldName]; + return localizer["{0} field has an invalid phone", fieldName]; } break; @@ -136,21 +136,22 @@ private string ValidateComponent(FormElementField field, object value, string fi if (field.Attributes.TryGetValue(FormElementField.MinValueAttribute, out var minValue)) { if (double.Parse(value?.ToString()) < (double?)minValue) - return Localizer["{0} field needs to be greater than {1}", fieldName, minValue]; + return localizer["{0} field needs to be greater than {1}", fieldName, minValue]; } if (field.Attributes.TryGetValue(FormElementField.MaxValueAttribute, out var maxValue)) { if (double.Parse(value?.ToString()) > (double?)maxValue) - return Localizer["{0} field needs to be less or equal than {1}", fieldName, maxValue]; + return localizer["{0} field needs to be less or equal than {1}", fieldName, maxValue]; } break; case FormComponent.Text: case FormComponent.TextArea: - if (field.ValidateRequest && (value?.ToString().ToLower().Contains("= 0) { - return Localizer["{0} field contains invalid or not allowed character", fieldName]; + return localizer["{0} field contains invalid or not allowed character", fieldName]; } break; @@ -166,45 +167,49 @@ private string ValidateDataType(ElementField field, object value, string fieldNa case FieldType.Date: case FieldType.DateTime: case FieldType.DateTime2: - if (!DateTime.TryParse(value?.ToString(), out var date) || date.Year < 1900) + if (!DateTime.TryParse(value?.ToString(), out _)) { - return Localizer["{0} field is an invalid date", + return localizer["{0} field is an invalid date", fieldName]; } + break; case FieldType.Time: if (!TimeSpan.TryParse(value?.ToString(), out _)) { - return Localizer["{0} field is an invalid time", + return localizer["{0} field is an invalid time", fieldName]; } + break; case FieldType.Int: - if (value is not bool && !int.TryParse(value?.ToString(), NumberStyles.Number, CultureInfo.CurrentCulture, out _)) + if (value is not bool && !int.TryParse(value?.ToString(), NumberStyles.Number, + CultureInfo.CurrentCulture, out _)) { - return Localizer["{0} field has an invalid number", + return localizer["{0} field has an invalid number", fieldName]; } break; case FieldType.Float: - if (!double.TryParse(value?.ToString(), out _)) + if (!double.TryParse(value?.ToString(), out _)) { - return Localizer["{0} field has an invalid number", + return localizer["{0} field has an invalid number", fieldName]; } break; default: - if (field.Size > 0 && - value?.ToString()?.Length > field.Size && + if (field.Size > 0 && + value?.ToString()?.Length > field.Size && value is not bool && - value is not TimeSpan && + value is not TimeSpan && value is not DateTime) { - return Localizer["{0} field cannot contain more than {1} characters", + return localizer["{0} field cannot contain more than {1} characters", fieldName, field.Size]; } + break; } @@ -215,7 +220,7 @@ private static string GetFieldLinkHtml(string fieldName, string label) { var link = new HtmlBuilder(HtmlTag.A); link.WithAttribute("href", "#void"); - link.WithOnClick( $"javascript:$('#{fieldName}').focus();"); + link.WithOnClick($"javascript:$('#{fieldName}').focus();"); link.WithCssClass("alert-link"); link.AppendText(label ?? fieldName); diff --git a/src/Core/DataManager/Services/FieldValuesService.cs b/src/Core/DataManager/Services/FieldValuesService.cs index edea33a55..f2fba78c9 100644 --- a/src/Core/DataManager/Services/FieldValuesService.cs +++ b/src/Core/DataManager/Services/FieldValuesService.cs @@ -12,9 +12,6 @@ namespace JJMasterData.Core.DataManager.Services; public class FieldValuesService(ExpressionsService expressionsService) { - private ExpressionsService ExpressionsService { get; } = expressionsService; - - /// /// Apply default and triggers expression values /// @@ -24,7 +21,7 @@ public class FieldValuesService(ExpressionsService expressionsService) /// /// Returns a new Dictionary with the updated values /// - public async Task> MergeWithExpressionValuesAsync(FormElement formElement, FormStateData formStateData, bool replaceNullValues = true) + public async ValueTask> MergeWithExpressionValuesAsync(FormElement formElement, FormStateData formStateData, bool replaceNullValues = true) { var formValues = formStateData.Values; foreach (var f in formElement.Fields) @@ -41,17 +38,19 @@ public class FieldValuesService(ExpressionsService expressionsService) return new Dictionary(formStateData.Values); } - public async Task> GetDefaultValuesAsync(FormElement formElement,FormStateData formStateData) + public async ValueTask> GetDefaultValuesAsync(FormElement formElement,FormStateData formStateData) { var defaultValues = new Dictionary(StringComparer.InvariantCultureIgnoreCase); var formStateDataCopy = formStateData.DeepCopy(); + var values = formStateData.Values; var fieldsWithDefaultValue = formElement.Fields - .Where(FieldNeedsDefaultValue(formStateData)); + .Where(f => !string.IsNullOrEmpty(f.DefaultValue) && + (!values.ContainsKey(f.Name) || string.IsNullOrEmpty(values[f.Name]?.ToString()))); foreach (var field in fieldsWithDefaultValue) { var fieldSelector = new FormElementFieldSelector(formElement, field.Name); - var defaultValue = await ExpressionsService.GetDefaultValueAsync(fieldSelector, formStateDataCopy); + var defaultValue = await expressionsService.GetDefaultValueAsync(fieldSelector, formStateDataCopy); if (!string.IsNullOrEmpty(defaultValue?.ToString())) { defaultValues.Add(field.Name, defaultValue); @@ -61,26 +60,19 @@ public class FieldValuesService(ExpressionsService expressionsService) return defaultValues; } - - private static Func FieldNeedsDefaultValue(FormStateData formStateData) - { - return f => !string.IsNullOrEmpty(f.DefaultValue) && - (!formStateData.Values.ContainsKey(f.Name) || string.IsNullOrEmpty(formStateData.Values[f.Name]?.ToString())); - } - public async Task> MergeWithDefaultValuesAsync(FormElement formElement,FormStateData formStateData) + public async ValueTask> MergeWithDefaultValuesAsync(FormElement formElement,FormStateData formStateData) { var values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach (var v in formStateData.Values) values.Add(v.Key, v.Value); - await ApplyDefaultValues(formElement, formStateData, false); return values; } - private async Task ApplyDefaultValues(FormElement formElement, FormStateData formStateData, bool replaceNullValues) + private async ValueTask ApplyDefaultValues(FormElement formElement, FormStateData formStateData, bool replaceNullValues) { var defaultValues = await GetDefaultValuesAsync(formElement,formStateData); @@ -102,7 +94,7 @@ private async Task ApplyDefaultValues(FormElement formElement, FormStateData for } } - private async Task ApplyTriggerValues(FormElement formElement, FormStateData formStateData) + private async ValueTask ApplyTriggerValues(FormElement formElement, FormStateData formStateData) { var fieldsWithTrigger = formElement.Fields .Where(x => !string.IsNullOrEmpty(x.TriggerExpression)); @@ -110,7 +102,7 @@ private async Task ApplyTriggerValues(FormElement formElement, FormStateData for foreach (var field in fieldsWithTrigger) { var fieldSelector = new FormElementFieldSelector(formElement, field.Name); - var value = await ExpressionsService.GetTriggerValueAsync(fieldSelector, formStateData); + var value = await expressionsService.GetTriggerValueAsync(fieldSelector, formStateData); if (value != null) { formStateData.Values[field.Name] = value; diff --git a/src/Core/DataManager/Services/FieldsService.cs b/src/Core/DataManager/Services/FieldsService.cs deleted file mode 100644 index aee64ec59..000000000 --- a/src/Core/DataManager/Services/FieldsService.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using JJMasterData.Core.DataDictionary.Models; -using JJMasterData.Core.DataManager.Models; - - -namespace JJMasterData.Core.DataManager.Services; - -public class FieldsService( - FieldFormattingService fieldFormattingService, - FieldValuesService fieldValuesService, - FieldValidationService fieldValidationService) -{ - private FieldFormattingService FieldFormattingService { get; } = fieldFormattingService; - private FieldValidationService FieldValidationService { get; } = fieldValidationService; - private FieldValuesService FieldValuesService { get; } = fieldValuesService; - - public Dictionary ValidateFields(FormElement formElement, Dictionary formValues, PageState pageState, bool enableErrorLink) - { - return FieldValidationService.ValidateFields(formElement, formValues, pageState, enableErrorLink); - } - - public string ValidateField(FormElementField field, string fieldId, string value, bool enableErrorLink = true) - { - return FieldValidationService.ValidateField(field, fieldId, value, enableErrorLink); - } - - public Task FormatGridValueAsync(FormElementFieldSelector fieldSelector, FormStateData formStateData) - { - return FieldFormattingService.FormatGridValueAsync(fieldSelector, formStateData); - } - - public static string FormatValue(FormElementField field, object value) - { - return FieldFormattingService.FormatValue(field, value); - } - - public Task> MergeWithExpressionValuesAsync(FormElement formElement, FormStateData formStateData, - bool replaceNullValues) - { - return FieldValuesService.MergeWithExpressionValuesAsync(formElement, formStateData, replaceNullValues); - } - - public Task> GetDefaultValuesAsync(FormElement formElement, FormStateData formStateData) - { - return FieldValuesService.GetDefaultValuesAsync(formElement, formStateData); - } - - public Task> MergeWithDefaultValuesAsync(FormElement formElement, FormStateData formStateData) - { - return FieldValuesService.MergeWithDefaultValuesAsync(formElement,formStateData); - } -} \ No newline at end of file diff --git a/src/Core/DataManager/Services/FormService.cs b/src/Core/DataManager/Services/FormService.cs index aad912440..36abc871a 100644 --- a/src/Core/DataManager/Services/FormService.cs +++ b/src/Core/DataManager/Services/FormService.cs @@ -7,7 +7,6 @@ using JJMasterData.Commons.Localization; using JJMasterData.Commons.Tasks; using JJMasterData.Core.DataDictionary.Models; -using JJMasterData.Core.DataManager.Expressions; using JJMasterData.Core.DataManager.IO; using JJMasterData.Core.DataManager.Models; using JJMasterData.Core.Events.Args; @@ -25,20 +24,7 @@ public class FormService( IStringLocalizer localizer, ILogger logger) { - #region Properties - private IEntityRepository EntityRepository { get; } = entityRepository; - private FormFileService FormFileService { get; } = formFileService; - - private FieldValidationService FieldValidationService { get; } = fieldValidationService; - - private AuditLogService AuditLogService { get; } = auditLogService; - private IStringLocalizer Localizer { get; } = localizer; - private ILogger Logger { get; } = logger; - - #endregion - #region Events - public event AsyncEventHandler OnBeforeDeleteAsync; public event AsyncEventHandler OnAfterDeleteAsync; public event AsyncEventHandler OnBeforeInsertAsync; @@ -46,9 +32,7 @@ public class FormService( public event AsyncEventHandler OnBeforeUpdateAsync; public event AsyncEventHandler OnAfterUpdateAsync; public event AsyncEventHandler OnBeforeImportAsync; - #endregion - #region Methods /// @@ -60,7 +44,7 @@ public class FormService( public async Task UpdateAsync(FormElement formElement, Dictionary values, DataContext dataContext) { var isForm = dataContext.Source is DataContextSource.Form; - var errors = FieldValidationService.ValidateFields(formElement, values, PageState.Update, isForm); + var errors = fieldValidationService.ValidateFields(formElement, values, PageState.Update, isForm); var result = new FormLetter(errors); if (OnBeforeUpdateAsync != null) @@ -72,16 +56,16 @@ public async Task UpdateAsync(FormElement formElement, Dictionary 0) return result; - int rowsAffected = 0; + var rowsAffected = 0; try { - rowsAffected = await EntityRepository.UpdateAsync(formElement, values); + rowsAffected = await entityRepository.UpdateAsync(formElement, values); } catch (Exception e) { - Logger.LogFormServiceError(e, nameof(UpdateAsync)); - errors.Add("DbException", Localizer[ExceptionManager.GetMessage(e)]); + logger.LogFormServiceError(e, nameof(UpdateAsync)); + errors.Add("DbException", localizer[ExceptionManager.GetMessage(e)]); } result.NumberOfRowsAffected = rowsAffected; @@ -90,10 +74,10 @@ public async Task UpdateAsync(FormElement formElement, Dictionary InsertAsync(FormElement formElement, Dictionary errors; if (validateFields) - errors = FieldValidationService.ValidateFields(formElement, values, PageState.Insert, isForm); + errors = fieldValidationService.ValidateFields(formElement, values, PageState.Insert, isForm); else errors = new Dictionary(); @@ -127,22 +111,22 @@ public async Task InsertAsync(FormElement formElement, Dictionary 0) return result; if (dataContext.Source == DataContextSource.Form) - FormFileService.SaveFormMemoryFiles(formElement, values); + formFileService.SaveFormMemoryFiles(formElement, values); if (formElement.Options.EnableAuditLog) - await AuditLogService.LogAsync(formElement, dataContext, values, CommandOperation.Insert); + await auditLogService.LogAsync(formElement, dataContext, values, CommandOperation.Insert); if (OnAfterInsertAsync != null) { @@ -163,7 +147,7 @@ public async Task InsertAsync(FormElement formElement, Dictionary> InsertOrReplaceAsync(FormElement formElement, Dictionary values, DataContext dataContext) { var isForm = dataContext.Source is DataContextSource.Form; - var errors = FieldValidationService.ValidateFields(formElement, values, PageState.Import, isForm); + var errors = fieldValidationService.ValidateFields(formElement, values, PageState.Import, isForm); var result = new FormLetter(errors); if (OnBeforeImportAsync != null) @@ -178,20 +162,22 @@ public async Task> InsertOrReplaceAsync(FormElement try { - result.Result = await EntityRepository.SetValuesAsync(formElement, values); + result.Result = await entityRepository.SetValuesAsync(formElement, values); } catch (Exception e) { - Logger.LogFormServiceError(e, nameof(InsertOrReplaceAsync)); - errors.Add("DbException", Localizer[ExceptionManager.GetMessage(e)]); + logger.LogFormServiceError(e, nameof(InsertOrReplaceAsync)); + errors.Add("DbException", localizer[ExceptionManager.GetMessage(e)]); } if (errors.Count > 0) return result; if (formElement.Options.EnableAuditLog) - await AuditLogService.LogAsync(formElement, dataContext, values, result.Result); + await auditLogService.LogAsync(formElement, dataContext, values, result.Result); + if (dataContext.Source == DataContextSource.Form) + formFileService.SaveFormMemoryFiles(formElement, values); if (result.Result == CommandOperation.Insert && OnAfterInsertAsync != null) { @@ -241,23 +227,23 @@ public async Task DeleteAsync(FormElement formElement, Dictionary 0) return result; if (dataContext.Source == DataContextSource.Form) - FormFileService.DeleteFiles(formElement, primaryKeys); + formFileService.DeleteFiles(formElement, primaryKeys); if (formElement.Options.EnableAuditLog) - await AuditLogService.LogAsync(formElement, dataContext, primaryKeys, CommandOperation.Delete); + await auditLogService.LogAsync(formElement, dataContext, primaryKeys, CommandOperation.Delete); if (OnAfterDeleteAsync != null) { diff --git a/src/Core/DataManager/Services/FormValuesService.cs b/src/Core/DataManager/Services/FormValuesService.cs index 943546496..b523a14e0 100644 --- a/src/Core/DataManager/Services/FormValuesService.cs +++ b/src/Core/DataManager/Services/FormValuesService.cs @@ -23,11 +23,6 @@ public class FormValuesService( IEncryptionService encryptionService, IHttpRequest httpRequest) { - private IEntityRepository EntityRepository { get; } = entityRepository; - private FieldValuesService FieldValuesService { get; } = fieldValuesService; - private IEncryptionService EncryptionService { get; } = encryptionService; - private IFormValues FormValues { get; } = httpRequest.Form; - private Dictionary GetFormValues(FormElement formElement, string? fieldPrefix = null) { @@ -41,10 +36,10 @@ public class FormValuesService( #if NET48 var value = field.ValidateRequest - ? FormValues[fieldName] - : FormValues.GetUnvalidated(fieldName); + ? httpRequest.Form[fieldName] + : httpRequest.Form.GetUnvalidated(fieldName); #else - var value = FormValues[fieldName]; + var value = httpRequest.Form[fieldName]; #endif HandleFieldValue(field, values, value); } @@ -60,7 +55,7 @@ internal static void HandleFieldValue(FormElementField field, Dictionary> GetFormValuesWithMergedValuesAsync( + public async ValueTask> GetFormValuesWithMergedValuesAsync( FormElement formElement, FormStateData formStateData, bool autoReloadFormFields, @@ -181,45 +186,45 @@ internal static void HandleFieldValue(FormElementField field, Dictionary> GetDbValues(Element element) { - string encryptedPkValues = FormValues[ + string encryptedPkValues = httpRequest.Form[ $"data-panel-pk-values-{element.Name}"]; if (string.IsNullOrEmpty(encryptedPkValues)) { - var encryptedFkValues = FormValues[ + var encryptedFkValues = httpRequest.Form[ $"form-view-relation-values-{element.Name}"]; if (!string.IsNullOrEmpty(encryptedFkValues)) { - return EncryptionService.DecryptDictionary(encryptedFkValues)!; + return encryptionService.DecryptDictionary(encryptedFkValues)!; } } if (encryptedPkValues is null) return new Dictionary(); - string pkValues = EncryptionService.DecryptStringWithUrlUnescape(encryptedPkValues)!; + string pkValues = encryptionService.DecryptStringWithUrlUnescape(encryptedPkValues)!; var filters = DataHelper.GetPkValues(element, pkValues, '|'); - return await EntityRepository.GetFieldsAsync(element, filters); + return await entityRepository.GetFieldsAsync(element, filters); } } \ No newline at end of file diff --git a/src/Core/DataManager/Services/HtmlTemplateService.cs b/src/Core/DataManager/Services/HtmlTemplateService.cs new file mode 100644 index 000000000..4e79c893e --- /dev/null +++ b/src/Core/DataManager/Services/HtmlTemplateService.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Web; +using Fluid; +using Fluid.Values; +using JJMasterData.Commons.Data.Entity.Repository.Abstractions; +using JJMasterData.Commons.Localization; +using JJMasterData.Commons.Util; +using JJMasterData.Core.DataDictionary; +using JJMasterData.Core.DataDictionary.Models.Actions; +using JJMasterData.Core.DataManager.Expressions; +using JJMasterData.Core.UI.Components; +using JJMasterData.Core.UI.Html; +using Microsoft.Extensions.Localization; + +namespace JJMasterData.Core.DataManager.Services; + +public class HtmlTemplateService( + IEntityRepository entityRepository, + IStringLocalizer stringLocalizer, + FluidParser fluidParser) +{ + public async Task RenderTemplate(HtmlTemplateAction action, Dictionary pkValues) + { + var command = ExpressionDataAccessCommandFactory.Create(action.SqlCommand, pkValues); + var dataSource = await entityRepository.GetDataSetAsync(command); + + var renderedTemplate = await RenderTemplate(action.HtmlTemplate, new Dictionary + { + { "DataSource", EnumerableHelper.ConvertDataSetToArray(dataSource) } + }); + + var html = new HtmlBuilder(); + html.AppendDiv(div => + { + div.WithCssClass("text-end").AppendComponent(new JJLinkButton(stringLocalizer) + { + Icon = IconType.Print, + ShowAsButton = true, + Tooltip = stringLocalizer["Print"], + OnClientClick = "printTemplateIframe()" + }); + }); + html.Append(HtmlTag.Iframe, iframe => + { + iframe.WithCssClass("modal-iframe"); + iframe.WithId("jjmasterdata-template-iframe"); + iframe.WithAttribute("srcdoc", HttpUtility.HtmlAttributeEncode(renderedTemplate)); + }); + + return html; + } + + public async ValueTask RenderTemplate(string templateString, Dictionary values) + { + if (!fluidParser.TryParse(templateString, out var template, out var error)) + { + return error; + } + + var context = new TemplateContext(values); + + var localize = new FunctionValue((args, _) => + { + var firstArg = args.At(0).ToStringValue(); + var localizerArgs = args.Values.Skip(1).Select(v => v.ToStringValue()).ToArray(); + + var localizedString = stringLocalizer[firstArg, localizerArgs.ToArray()]; + + return new ValueTask(new StringValue(localizedString)); + }); + + context.SetValue("isNullOrWhiteSpace", new FunctionValue(IsNullOrWhiteSpace)); + context.SetValue("isNullOrEmpty", new FunctionValue(IsNullOrEmpty)); + context.SetValue("substring", new FunctionValue(Substring)); + context.SetValue("localize", localize); + + return await template.RenderAsync(context); + } + + private static BooleanValue IsNullOrEmpty(FunctionArguments args, TemplateContext _) + { + var str = args.At(0).ToStringValue(); + + return BooleanValue.Create(string.IsNullOrEmpty(str)); + } + + private static BooleanValue IsNullOrWhiteSpace(FunctionArguments args, TemplateContext _) + { + var str = args.At(0).ToStringValue(); + + return BooleanValue.Create(string.IsNullOrWhiteSpace(str)); + } + + private static StringValue Substring(FunctionArguments args, TemplateContext _) + { + if (args.Count < 2) + { + return new StringValue("Error: Not enough arguments"); + } + + var str = args.At(0).ToObjectValue().ToString()!; + if (!int.TryParse(args.At(1).ToStringValue(), out var startIndex)) + { + return new StringValue("Error: Invalid start index"); + } + + int length = 0; + + if (args.Count > 2 && !int.TryParse(args.At(2).ToStringValue(), out length)) + { + return new StringValue("Error: Invalid length"); + } + + // If length is not provided, use the length of the remaining string from the start index + var substring = args.Count > 2 + ? str.Substring(startIndex, length) + : str.Substring(startIndex); + + return new StringValue(substring); + } +} \ No newline at end of file diff --git a/src/Core/DataManager/Services/LookupService.cs b/src/Core/DataManager/Services/LookupService.cs index de933336c..06a57dba0 100644 --- a/src/Core/DataManager/Services/LookupService.cs +++ b/src/Core/DataManager/Services/LookupService.cs @@ -11,28 +11,25 @@ namespace JJMasterData.Core.DataManager.Services; -public class LookupService(IFormValues formValues, +public class LookupService( + IFormValues formValues, ExpressionsService expressionsService, IEncryptionService encryptionService, ElementMapService elementMapService, IMasterDataUrlHelper urlHelper) { - private IFormValues FormValues { get; } = formValues; - private ExpressionsService ExpressionsService { get; } = expressionsService; - private IEncryptionService EncryptionService { get; } = encryptionService; - private ElementMapService ElementMapService { get; } = elementMapService; - private IMasterDataUrlHelper UrlHelper { get; } = urlHelper; - - public string GetFormViewUrl(DataElementMap elementMap, FormStateData? formStateData, string componentName) { - var lookupParameters = new LookupParameters(elementMap.ElementName, componentName, elementMap.IdFieldName,elementMap.DescriptionFieldName, + var lookupParameters = new LookupParameters(elementMap.ElementName, componentName, elementMap.IdFieldName, + elementMap.DescriptionFieldName, elementMap.EnableElementActions, elementMap.Filters); var encryptedLookupParameters = - EncryptionService.EncryptStringWithUrlEscape(lookupParameters.ToQueryString(ExpressionsService, formStateData)); - - return UrlHelper.Action("Index", "Lookup", new { Area = "MasterData", lookupParameters = encryptedLookupParameters }); + encryptionService.EncryptStringWithUrlEscape( + lookupParameters.ToQueryString(expressionsService, formStateData)); + + return urlHelper.Action("Index", "Lookup", + new { Area = "MasterData", lookupParameters = encryptedLookupParameters }); } public async Task GetDescriptionAsync( @@ -55,10 +52,10 @@ public string GetFormViewUrl(DataElementMap elementMap, FormStateData? formState } Dictionary values; - + try { - values = await ElementMapService.GetFieldsAsync(elementMap, value, formStateData); + values = await elementMapService.GetFieldsAsync(elementMap, value, formStateData); } catch { @@ -66,17 +63,19 @@ public string GetFormViewUrl(DataElementMap elementMap, FormStateData? formState } - if (string.IsNullOrEmpty(elementMap.DescriptionFieldName) && values.TryGetValue(elementMap.IdFieldName, out var id)) + if (string.IsNullOrEmpty(elementMap.DescriptionFieldName) && + values.TryGetValue(elementMap.IdFieldName, out var id)) return id?.ToString(); - if (elementMap.DescriptionFieldName != null && values.TryGetValue(elementMap.DescriptionFieldName, out var description)) + if (elementMap.DescriptionFieldName != null && + values.TryGetValue(elementMap.DescriptionFieldName, out var description)) return description?.ToString(); return null; } - + public string? GetSelectedValue(string componentName) { - return FormValues[componentName]; + return formValues[componentName]; } } \ No newline at end of file diff --git a/src/Core/DataManager/Services/UploadAreaService.cs b/src/Core/DataManager/Services/UploadAreaService.cs index fbf8cdcd8..9f90b1852 100644 --- a/src/Core/DataManager/Services/UploadAreaService.cs +++ b/src/Core/DataManager/Services/UploadAreaService.cs @@ -18,21 +18,19 @@ namespace JJMasterData.Core.DataManager.Services; public class UploadAreaService(IHttpContext currentContext, IStringLocalizer stringLocalizer) { - private IHttpContext CurrentContext { get; } = currentContext; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; public event EventHandler? OnFileUploaded; public event AsyncEventHandler? OnFileUploadedAsync; public async Task UploadFileAsync(FormFileContent formFile, string? allowedTypes = null) { UploadAreaResultDto dto = new(); - + try { - string message = string.Empty; - + var message = string.Empty; + ValidateAllowedExtensions(formFile.FileName, allowedTypes); - + var args = new FormUploadFileEventArgs(formFile); OnFileUploaded?.Invoke(this, args); @@ -40,7 +38,7 @@ public async Task UploadFileAsync(FormFileContent formFile, { await OnFileUploadedAsync.Invoke(this, args); } - + var errorMessage = args.ErrorMessage; if (args.SuccessMessage != null) { @@ -49,9 +47,8 @@ public async Task UploadFileAsync(FormFileContent formFile, if (!string.IsNullOrEmpty(errorMessage)) throw new JJMasterDataException(errorMessage); - - dto.SuccessMessage = message; + dto.SuccessMessage = message; } catch (JJMasterDataException ex) { @@ -60,20 +57,20 @@ public async Task UploadFileAsync(FormFileContent formFile, return dto; } - + /// /// Recovers the file after the POST /// private FormFileContent? GetFile(string fileName) { - var fileData = CurrentContext.Request.Form.GetFile(fileName); + var fileData = currentContext.Request.Form.GetFile(fileName); if (fileData is null) return null; - + using var stream = new MemoryStream(); string filename = fileData.FileName; - + #if NETFRAMEWORK fileData.InputStream.CopyTo(stream); #else @@ -90,7 +87,7 @@ public async Task UploadFileAsync(FormFileContent formFile, return content; } - + public bool TryGetFile(string fileName, out FormFileContent? formFile) { formFile = GetFile(fileName); @@ -158,7 +155,6 @@ private void ValidateAllowedExtensions(string filename, string? allowedTypes) string ext = FileIO.GetFileNameExtension(filename); if (list.Contains(ext)) - throw new JJMasterDataException(StringLocalizer["You cannot upload system files"]); - + throw new JJMasterDataException(stringLocalizer["You cannot upload system files"]); } } \ No newline at end of file diff --git a/src/Core/DataManager/Services/UrlRedirectService.cs b/src/Core/DataManager/Services/UrlRedirectService.cs index 8c716a8d0..99cfe0b11 100644 --- a/src/Core/DataManager/Services/UrlRedirectService.cs +++ b/src/Core/DataManager/Services/UrlRedirectService.cs @@ -1,27 +1,24 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using System.Web; using JJMasterData.Commons.Data.Entity.Repository.Abstractions; -using JJMasterData.Commons.Security.Cryptography.Abstractions; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.DataManager.Expressions; using JJMasterData.Core.DataManager.Models; -using JJMasterData.Core.Extensions; +using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.UI.Components; namespace JJMasterData.Core.DataManager.Services; public class UrlRedirectService( + IHttpRequest httpRequest, IEntityRepository entityRepository, FormValuesService formValuesService, ExpressionsService expressionsService) { - private IEntityRepository EntityRepository { get; } = entityRepository; - private FormValuesService FormValuesService { get; } = formValuesService; - private ExpressionsService ExpressionsService { get; } = expressionsService; - public async Task GetUrlRedirectResult( JJDataPanel dataPanel, ActionMap actionMap) @@ -30,16 +27,16 @@ public async Task GetUrlRedirectResult( Dictionary dbValues; - if (actionMap.PkFieldValues.Any()) + if (actionMap.PkFieldValues.Count > 0) { - dbValues = await EntityRepository.GetFieldsAsync(dataPanel.FormElement, actionMap.PkFieldValues); + dbValues = await entityRepository.GetFieldsAsync(dataPanel.FormElement, actionMap.PkFieldValues); } else { dbValues = new Dictionary(); } - var values = await FormValuesService.GetFormValuesWithMergedValuesAsync(dataPanel.FormElement,new FormStateData(dbValues,dataPanel.UserValues,dataPanel.PageState), true, dataPanel.FieldNamePrefix); + var values = await formValuesService.GetFormValuesWithMergedValuesAsync(dataPanel.FormElement,new FormStateData(dbValues,dataPanel.UserValues,dataPanel.PageState), true, dataPanel.FieldNamePrefix); DataHelper.CopyIntoDictionary(values, actionMap.PkFieldValues); @@ -52,7 +49,7 @@ public async Task GetUrlRedirectResult( { var urlRedirectAction = actionMap.GetAction(gridView.FormElement); - var values = await FormValuesService.GetFormValuesWithMergedValuesAsync(gridView.FormElement,new FormStateData(new Dictionary(),gridView.UserValues,PageState.List), true); + var values = await formValuesService.GetFormValuesWithMergedValuesAsync(gridView.FormElement,new FormStateData(new Dictionary(),gridView.UserValues,PageState.List), true); DataHelper.CopyIntoDictionary(values, actionMap.PkFieldValues); @@ -62,8 +59,8 @@ public async Task GetUrlRedirectResult( private JsonComponentResult GetJsonResult(Dictionary values, UrlRedirectAction action) { var formStateData = new FormStateData(values, PageState.List); - var parsedUrl = ExpressionsService.ReplaceExpressionWithParsedValues(System.Web.HttpUtility.UrlDecode(action.UrlRedirect), formStateData, action.EncryptParameters); - var parsedTitle = ExpressionsService.ReplaceExpressionWithParsedValues(action.ModalTitle, formStateData); + var parsedUrl = GetParsedUrl(action, formStateData); + var parsedTitle = expressionsService.ReplaceExpressionWithParsedValues(action.ModalTitle, formStateData); var model = new UrlRedirectModel { @@ -76,4 +73,15 @@ private JsonComponentResult GetJsonResult(Dictionary values, Url return new JsonComponentResult(model); } + + public string GetParsedUrl(UrlRedirectAction action, FormStateData formStateData) + { + var formStateDataCopy = formStateData.DeepCopy(); + + formStateDataCopy.Values.Add("AppPath", httpRequest.ApplicationPath); + + var decodedUrl = HttpUtility.UrlDecode(action.UrlRedirect); + + return expressionsService.ReplaceExpressionWithParsedValues(decodedUrl, formStateDataCopy, action.EncryptParameters); + } } \ No newline at end of file diff --git a/src/Core/Events/Abstractions/FormEventHandlerBase.cs b/src/Core/Events/Abstractions/FormEventHandlerBase.cs index 1f245f3ab..5f8597739 100644 --- a/src/Core/Events/Abstractions/FormEventHandlerBase.cs +++ b/src/Core/Events/Abstractions/FormEventHandlerBase.cs @@ -1,18 +1,19 @@ using System.Threading.Tasks; using JJMasterData.Core.Events.Args; +using JJMasterData.Core.Tasks; namespace JJMasterData.Core.Events.Abstractions; public abstract class FormEventHandlerBase : IFormEventHandler { public abstract string ElementName { get; } - public virtual Task OnBeforeInsertAsync(object sender, FormBeforeActionEventArgs args) => Task.CompletedTask; - public virtual Task OnBeforeUpdateAsync(object sender, FormBeforeActionEventArgs args) => Task.CompletedTask; - public virtual Task OnBeforeDeleteAsync(object sender, FormBeforeActionEventArgs args) => Task.CompletedTask; - public virtual Task OnBeforeImportAsync(object sender, FormBeforeActionEventArgs args) => Task.CompletedTask; - public virtual Task OnAfterInsertAsync(object sender, FormAfterActionEventArgs args) => Task.CompletedTask; - public virtual Task OnAfterUpdateAsync(object sender, FormAfterActionEventArgs args) => Task.CompletedTask; - public virtual Task OnAfterDeleteAsync(object sender, FormAfterActionEventArgs args) => Task.CompletedTask; - public virtual Task OnFormElementLoadAsync(object sender, FormElementLoadEventArgs args) => Task.CompletedTask; + public virtual ValueTask OnBeforeInsertAsync(object sender, FormBeforeActionEventArgs args) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnBeforeUpdateAsync(object sender, FormBeforeActionEventArgs args) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnBeforeDeleteAsync(object sender, FormBeforeActionEventArgs args) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnBeforeImportAsync(object sender, FormBeforeActionEventArgs args) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnAfterInsertAsync(object sender, FormAfterActionEventArgs args) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnAfterUpdateAsync(object sender, FormAfterActionEventArgs args) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnAfterDeleteAsync(object sender, FormAfterActionEventArgs args) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnFormElementLoadAsync(object sender, FormElementLoadEventArgs args) => ValueTaskHelper.CompletedTask; } diff --git a/src/Core/Events/Abstractions/IFormEventHandler.cs b/src/Core/Events/Abstractions/IFormEventHandler.cs index b465c21c2..6676e462f 100644 --- a/src/Core/Events/Abstractions/IFormEventHandler.cs +++ b/src/Core/Events/Abstractions/IFormEventHandler.cs @@ -5,66 +5,66 @@ namespace JJMasterData.Core.Events.Abstractions; public interface IFormEventHandler : IEventHandler { - public Task OnBeforeInsertAsync(object sender, FormBeforeActionEventArgs args) + public ValueTask OnBeforeInsertAsync(object sender, FormBeforeActionEventArgs args) #if NET { - return Task.CompletedTask; + return ValueTask.CompletedTask; } #else ; #endif - public Task OnBeforeUpdateAsync(object sender, FormBeforeActionEventArgs args) + public ValueTask OnBeforeUpdateAsync(object sender, FormBeforeActionEventArgs args) #if NET { - return Task.CompletedTask; + return ValueTask.CompletedTask; } #else ; #endif - public Task OnBeforeDeleteAsync(object sender, FormBeforeActionEventArgs args) + public ValueTask OnBeforeDeleteAsync(object sender, FormBeforeActionEventArgs args) #if NET { - return Task.CompletedTask; + return ValueTask.CompletedTask; } #else ; #endif - public Task OnBeforeImportAsync(object sender, FormBeforeActionEventArgs args) + public ValueTask OnBeforeImportAsync(object sender, FormBeforeActionEventArgs args) #if NET { - return Task.CompletedTask; + return ValueTask.CompletedTask; } #else ; #endif - public Task OnAfterInsertAsync(object sender, FormAfterActionEventArgs args) + public ValueTask OnAfterInsertAsync(object sender, FormAfterActionEventArgs args) #if NET { - return Task.CompletedTask; + return ValueTask.CompletedTask; } #else ; #endif - public Task OnAfterUpdateAsync(object sender, FormAfterActionEventArgs args) + public ValueTask OnAfterUpdateAsync(object sender, FormAfterActionEventArgs args) #if NET { - return Task.CompletedTask; + return ValueTask.CompletedTask; } #else ; #endif - public Task OnAfterDeleteAsync(object sender, FormAfterActionEventArgs args) + public ValueTask OnAfterDeleteAsync(object sender, FormAfterActionEventArgs args) #if NET { - return Task.CompletedTask; + return ValueTask.CompletedTask; } #else ; #endif - public Task OnFormElementLoadAsync(object sender, FormElementLoadEventArgs args) + public ValueTask OnFormElementLoadAsync(object sender, FormElementLoadEventArgs args) #if NET { - return Task.CompletedTask; + return ValueTask.CompletedTask; } #else ; diff --git a/src/Core/Extensions/EncryptionServiceExtensions.cs b/src/Core/Extensions/EncryptionServiceExtensions.cs index 593bf624d..652fd5805 100644 --- a/src/Core/Extensions/EncryptionServiceExtensions.cs +++ b/src/Core/Extensions/EncryptionServiceExtensions.cs @@ -41,26 +41,11 @@ public static T DecryptObject(this IEncryptionService service, string encrypt return JsonConvert.DeserializeObject(service.DecryptStringWithUrlUnescape(encryptedObject)!); } - public static string EncryptDictionary(this IEncryptionService service, Dictionary dictionary) - { - return service.EncryptObject(dictionary); - } - public static Dictionary DecryptDictionary(this IEncryptionService service, string encryptedDictionary) { return service.DecryptObject>(encryptedDictionary); } - internal static string EncryptActionMap(this IEncryptionService service, ActionMap actionMap) - { - return service.EncryptStringWithUrlEscape(JsonConvert.SerializeObject(actionMap)); - } - - public static string EncryptRouteContext(this IEncryptionService service, RouteContext routeContext) - { - return service.EncryptObject(routeContext); - } - public static RouteContext DecryptRouteContext(this IEncryptionService service, string encryptedRouteContext) { return service.DecryptObject(encryptedRouteContext); diff --git a/src/Core/Extensions/QueryStringExtensions.cs b/src/Core/Extensions/HttpContextExtensions.cs similarity index 55% rename from src/Core/Extensions/QueryStringExtensions.cs rename to src/Core/Extensions/HttpContextExtensions.cs index 9f4437893..ee350da02 100644 --- a/src/Core/Extensions/QueryStringExtensions.cs +++ b/src/Core/Extensions/HttpContextExtensions.cs @@ -2,8 +2,13 @@ namespace JJMasterData.Core.Extensions; -public static class QueryStringExtensions +public static class HttpContextExtensions { + public static bool TryGetValue(this IFormValues formValues, string key, out string value) + { + value = formValues[key]; + return !string.IsNullOrEmpty(value); + } public static bool TryGetValue(this IQueryString queryString,string key, out string value) { value = queryString[key]; diff --git a/src/Core/Http/AspNetCore/FormValuesWrapper.cs b/src/Core/Http/AspNetCore/FormValuesWrapper.cs index 9a9d4c1e3..696efa4a0 100644 --- a/src/Core/Http/AspNetCore/FormValuesWrapper.cs +++ b/src/Core/Http/AspNetCore/FormValuesWrapper.cs @@ -7,7 +7,7 @@ namespace JJMasterData.Core.Http.AspNetCore; -internal class FormValuesWrapper(IHttpContextAccessor httpContextAccessor) : IFormValues +internal sealed class FormValuesWrapper(IHttpContextAccessor httpContextAccessor) : IFormValues { private IFormCollection? _formCollection; diff --git a/src/Core/Http/AspNetCore/HttpRequestWrapper.cs b/src/Core/Http/AspNetCore/HttpRequestWrapper.cs index 22fef3469..85535bbab 100644 --- a/src/Core/Http/AspNetCore/HttpRequestWrapper.cs +++ b/src/Core/Http/AspNetCore/HttpRequestWrapper.cs @@ -6,7 +6,7 @@ namespace JJMasterData.Core.Http.AspNetCore; -internal class HttpRequestWrapper( +internal sealed class HttpRequestWrapper( IHttpContextAccessor httpContextAccessor, IFormValues formValues, IQueryString queryString) diff --git a/src/Core/Http/AspNetCore/HttpSessionWrapper.cs b/src/Core/Http/AspNetCore/HttpSessionWrapper.cs index a81ba0eab..93e2c7b24 100644 --- a/src/Core/Http/AspNetCore/HttpSessionWrapper.cs +++ b/src/Core/Http/AspNetCore/HttpSessionWrapper.cs @@ -5,7 +5,7 @@ namespace JJMasterData.Core.Http.AspNetCore; -internal class HttpSessionWrapper(IHttpContextAccessor contextAccessor) : IHttpSession +internal sealed class HttpSessionWrapper(IHttpContextAccessor contextAccessor) : IHttpSession { private ISession Session { get; } = contextAccessor.HttpContext.Session; diff --git a/src/Core/Http/AspNetCore/QueryStringWrapper.cs b/src/Core/Http/AspNetCore/QueryStringWrapper.cs index aaaea4ca7..d02473f28 100644 --- a/src/Core/Http/AspNetCore/QueryStringWrapper.cs +++ b/src/Core/Http/AspNetCore/QueryStringWrapper.cs @@ -4,7 +4,7 @@ namespace JJMasterData.Core.Http.AspNetCore; -internal class QueryStringWrapper(IHttpContextAccessor httpContextAccessor) : IQueryString +internal sealed class QueryStringWrapper(IHttpContextAccessor httpContextAccessor) : IQueryString { private IQueryCollection QueryCollection { get; } = httpContextAccessor.HttpContext!.Request.Query; private QueryString QueryString { get; } = httpContextAccessor.HttpContext.Request.QueryString; diff --git a/src/Core/Http/HttpContextWrapper.cs b/src/Core/Http/HttpContextWrapper.cs index 6bd563a76..2faeedb24 100644 --- a/src/Core/Http/HttpContextWrapper.cs +++ b/src/Core/Http/HttpContextWrapper.cs @@ -3,7 +3,7 @@ namespace JJMasterData.Core.Http; -internal class HttpContextWrapper(IHttpSession session, IHttpRequest request, +internal sealed class HttpContextWrapper(IHttpSession session, IHttpRequest request, IClaimsPrincipalAccessor claimsPrincipalAccessor) : IHttpContext { diff --git a/src/Core/Http/SystemWeb/SystemWebFormValuesWrapper.cs b/src/Core/Http/SystemWeb/SystemWebFormValuesWrapper.cs index 9e2eacc87..d0a099df1 100644 --- a/src/Core/Http/SystemWeb/SystemWebFormValuesWrapper.cs +++ b/src/Core/Http/SystemWeb/SystemWebFormValuesWrapper.cs @@ -6,7 +6,7 @@ namespace JJMasterData.Core.Http.SystemWeb; -internal class SystemWebFormValuesWrapper : IFormValues +internal sealed class SystemWebFormValuesWrapper : IFormValues { private static UnvalidatedRequestValues UnvalidatedFormCollection => HttpContext.Request.Unvalidated; private static NameValueCollection FormCollection => HttpContext.Request.Form; diff --git a/src/Core/Http/SystemWeb/SystemWebHttpRequestWrapper.cs b/src/Core/Http/SystemWeb/SystemWebHttpRequestWrapper.cs index d0d6de035..e555304a5 100644 --- a/src/Core/Http/SystemWeb/SystemWebHttpRequestWrapper.cs +++ b/src/Core/Http/SystemWeb/SystemWebHttpRequestWrapper.cs @@ -7,7 +7,7 @@ namespace JJMasterData.Core.Http.SystemWeb; -internal class SystemWebHttpRequestWrapper(IQueryString queryString, IFormValues form) : IHttpRequest +internal sealed class SystemWebHttpRequestWrapper(IQueryString queryString, IFormValues form) : IHttpRequest { private static HttpRequest Request => HttpContext.Current.Request; public string UserHostAddress => Request.UserHostAddress; diff --git a/src/Core/Http/SystemWeb/SystemWebHttpSessionWrapper.cs b/src/Core/Http/SystemWeb/SystemWebHttpSessionWrapper.cs index ac4f63d08..bf7e991c1 100644 --- a/src/Core/Http/SystemWeb/SystemWebHttpSessionWrapper.cs +++ b/src/Core/Http/SystemWeb/SystemWebHttpSessionWrapper.cs @@ -6,7 +6,7 @@ namespace JJMasterData.Core.Http.SystemWeb; -internal class SystemWebHttpSessionWrapper : IHttpSession +internal sealed class SystemWebHttpSessionWrapper : IHttpSession { private static HttpSessionState Session => HttpContext.Current.Session; public string this[string key] diff --git a/src/Core/Http/SystemWeb/SystemWebQueryStringWrapper.cs b/src/Core/Http/SystemWeb/SystemWebQueryStringWrapper.cs index eb04277f0..b03f4a37e 100644 --- a/src/Core/Http/SystemWeb/SystemWebQueryStringWrapper.cs +++ b/src/Core/Http/SystemWeb/SystemWebQueryStringWrapper.cs @@ -5,7 +5,7 @@ #if NETFRAMEWORK namespace JJMasterData.Core.Http.SystemWeb; -internal class SystemWebQueryStringWrapper : IQueryString +internal sealed class SystemWebQueryStringWrapper : IQueryString { private static NameValueCollection QueryString => HttpContext.Current.Request.QueryString; public string this[string key] => QueryString[key]; diff --git a/src/Core/Logging/LogMessages.cs b/src/Core/Logging/LogMessages.cs index 8118bd901..b9f3076e1 100644 --- a/src/Core/Logging/LogMessages.cs +++ b/src/Core/Logging/LogMessages.cs @@ -1,7 +1,5 @@ #nullable enable using System; -using JJMasterData.Commons.Data; -using JJMasterData.Core.DataDictionary.Models; using Microsoft.Extensions.Logging; namespace JJMasterData.Core.Logging; diff --git a/src/Core/Tasks/AsyncHelper.cs b/src/Core/Tasks/AsyncHelper.cs index ea523b0c6..8d3cd76e5 100644 --- a/src/Core/Tasks/AsyncHelper.cs +++ b/src/Core/Tasks/AsyncHelper.cs @@ -41,7 +41,7 @@ public static void RunSync(Func task) SynchronizationContext.SetSynchronizationContext(oldContext); } - + /// /// Execute is an async Task method which has a T return type synchronously /// @@ -74,7 +74,17 @@ public static T RunSync(Func> task) return ret; } - private class ExclusiveSynchronizationContext : SynchronizationContext + public static void RunSync(Func task) + { + RunSync(()=>task().AsTask()); + } + + public static T RunSync(Func> task) + { + return RunSync(()=>task().AsTask()); + } + + private sealed class ExclusiveSynchronizationContext : SynchronizationContext { private bool done; public Exception InnerException { get; set; } diff --git a/src/Core/Tasks/ValueTaskHelper.cs b/src/Core/Tasks/ValueTaskHelper.cs new file mode 100644 index 000000000..ed12d8b8a --- /dev/null +++ b/src/Core/Tasks/ValueTaskHelper.cs @@ -0,0 +1,20 @@ +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace JJMasterData.Core.Tasks; + +internal static class ValueTaskHelper +{ + public static ValueTask CompletedTask + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if NET + return ValueTask.CompletedTask; +#else + return default; +#endif + } + } +} \ No newline at end of file diff --git a/src/Core/UI/BootstrapHelper.cs b/src/Core/UI/BootstrapHelper.cs index a4265726a..9cc5a0641 100644 --- a/src/Core/UI/BootstrapHelper.cs +++ b/src/Core/UI/BootstrapHelper.cs @@ -1,4 +1,7 @@ -namespace JJMasterData.Core.UI; +using System; +using System.Runtime.CompilerServices; + +namespace JJMasterData.Core.UI; /// /// Helper to Bootstrap strings, everything defaults to version 5. @@ -11,226 +14,236 @@ public static class BootstrapHelper { #region Options - public static int Version { get; set; } = 5; + public static readonly int Version; + #endregion - #region Panel - public static string GetPanel(string className) + static BootstrapHelper() { - if (Version == 3) + if (int.TryParse(AppContext.GetData("JJMasterData.BootstrapVersion")?.ToString(), out var version)) { - return $"panel panel-{className}"; + Version = version; } - - return $"card border-{className.Replace("default", "jjmasterdata")} border-opacity-75"; + else + { + Version = 5; + } + PanelTitle = Version switch + { + >= 4 => " jj-panel-title", + _ => " panel-title" + }; + PanelBody = Version switch + { + >= 4 => " card-body", + _ => " panel-body" + }; + PanelGroup = Version switch + { + >= 4 => " card-group", + _ => " panel-group" + }; + InputGroupBtn = Version switch + { + 5 => "input-group", + 4 => " input-group-prepend", + _ => " input-group-btn" + }; + InputGroupAddon = Version switch + { + 5 => "input-group-text", + 4 => " input-group-prepend", + _ => " input-group-addon" + }; + Show = Version switch + { + >= 4 => " show", + _ => " in" + }; + Label = Version switch + { + 5 => " form-label", + 4 => " col-form-label", + _ => " control-label" + }; + FormHorizontal = Version switch + { + >= 4 => string.Empty, + _ => " form-horizontal" + }; + HasError = Version switch + { + >= 4 => "is-invalid", + _ => "has-error" + }; + CenterBlock = Version switch + { + >= 4 => "d-block mx-auto", + _ => "center-block" + }; + PageHeader = Version switch + { + >= 4 => "pb-2 mb-2 border-bottom", + _ => "page-header" + }; + TextRight = Version switch + { + 5 => " text-end", + _ => " text-right" + }; + TextLeft = Version switch + { + 5 => " text-start", + _ => " text-left" + }; + MarginLeft = Version switch + { + 5 => " ms", + _ => " ml" + }; + MarginRight = Version switch + { + 5 => " me", + _ => " mr" + }; + FormGroup = Version switch + { + 5 => " mb-3", + _ => " form-group" + }; + Close = Version switch + { + 5 => " btn-close", + _ => " close" + }; + CloseButtonTimes = Version switch + { + 5 => string.Empty, + _ => "×" + }; + DataDismiss = Version switch + { + 5 => "data-bs-dismiss", + _ => "data-dismiss" + }; + DataToggle = Version switch + { + 5 => "data-bs-toggle", + _ => "data-toggle" + }; + PullRight = Version switch + { + 5 => "float-end", + 4 => "float-right", + _ => "pull-right" + }; + PullLeft = Version switch + { + 5 => "float-start", + 4 => "float-left", + _ => "pull-left" + }; + LabelSuccess = Version switch + { + >= 4 => "badge bg-success", + _ => "label label-success" + }; + LabelDefault = Version switch + { + >= 4 => "badge bg-secondary", + _ => "label label-default" + }; + LabelWarning = Version switch + { + >= 4 => "badge bg-warning", + _ => "label label-warning" + }; + LabelDanger = Version switch + { + >= 4 => "badge bg-danger", + _ => "label label-danger" + }; + BtnDefault = Version switch + { + >= 4 => " btn btn-secondary", + _ => " btn btn-default" + }; } - public static string GetPanelHeading(string className) => Version switch - { - >= 4 => $" card-header bg-{className.Replace("default", "jjmasterdata")} bg-opacity-75", - _ => " panel-heading" - }; - - public static string PanelTitle => Version switch - { - >= 4 => " jj-panel-title", - _ => " panel-title" - }; + #region Panel + + public static readonly string PanelTitle; - public static string PanelBody => Version switch - { - >= 4 => " card-body", - _ => " panel-body" - }; + public static readonly string PanelBody; - public static string PanelGroup => Version switch - { - >= 4 => " card-group", - _ => " panel-group" - }; + public static readonly string PanelGroup; - public static string PanelCollapse => Version switch - { - >= 4 => " panel-collapse in collapse show", - _ => " panel-collapse collapse in" - }; #endregion #region InputGroup - public static string InputGroupBtn => Version switch - { - 5 => "input-group", - 4 => " input-group-prepend", - _ => " input-group-btn" - }; + public static readonly string InputGroupBtn; + + public static readonly string InputGroupAddon; - public static string InputGroupAddon => Version switch - { - 5 => "input-group-text", - 4 => " input-group-prepend", - _ => " input-group-addon" - }; #endregion #region Miscellaneous - public static string Show => Version switch - { - >= 4 => " show", - _ => " in" - }; - public static string Label => Version switch - { - 5 => " form-label", - 4 => " col-form-label", - _ => " control-label" - }; - public static string FormHorizontal => Version switch - { - >= 4 => string.Empty, - _ => " form-horizontal" - }; - - public static string DateIcon => Version switch - { - >= 4 => "calendar", - _ => "th" - }; + public static readonly string Show; - public static string Well => Version switch - { - >= 4 => " card", - _ => " well" - }; + public static readonly string Label; - public static string HasError => Version switch - { - >= 4 => "is-invalid", - _ => "has-error" - }; + public static readonly string FormHorizontal; - public static string CenterBlock => Version switch - { - >= 4 => "d-block mx-auto", - _ => "center-block" - }; + public static readonly string HasError; - public static string PageHeader => Version switch - { - >= 4 => "pb-2 mb-2 border-bottom", - _ => "page-header" - }; + public static readonly string CenterBlock; + + public static readonly string PageHeader; #endregion #region Bootstrap5 Breaking Changes - public static string TextRight => Version switch - { - 5 => " text-end", - _ => " text-right" - }; - public static string TextLeft => Version switch - { - 5 => " text-start", - _ => " text-left" - }; + public static readonly string TextRight; - public static string MarginLeft => Version switch - { - 5 => " ms", - _ => " ml" - }; + public static readonly string TextLeft; - public static string MarginRight => Version switch - { - 5 => " me", - _ => " mr" - }; + public static readonly string MarginLeft; + public static readonly string MarginRight; - public static string FormGroup => Version switch - { - 5 => " mb-3", - _ => " form-group" - }; - public static string Close => Version switch - { - 5 => " btn-close", - _ => " close" - }; + public static readonly string FormGroup; - public static string CloseButtonTimes => Version switch - { - 5 => string.Empty, - _ => "×" - }; + public static readonly string Close; - public static string DataDismiss => Version switch - { - 5 => "data-bs-dismiss", - _ => "data-dismiss" - }; - - public static string GetDataDismiss(string value) - { - return $"{DataDismiss}={value}"; - } + public static readonly string CloseButtonTimes; - public static string DataToggle => Version switch - { - 5 => "data-bs-toggle", - _ => "data-toggle" - }; + public static readonly string DataDismiss; + + public static readonly string DataToggle; - public static string PullRight => Version switch - { - 5 => "float-end", - 4 => "float-right", - _ => "pull-right" - }; + public static readonly string PullRight; - public static string PullLeft => Version switch - { - 5 => "float-start", - 4 => "float-left", - _ => "pull-left" - }; + public static readonly string PullLeft; - public static string LabelSucess => Version switch - { - >= 4 => "badge bg-success", - _ => "label label-success" - }; + public static readonly string LabelSuccess; - public static string LabelDefault => Version switch - { - >= 4 => "badge bg-secondary", - _ => "label label-default" - }; + public static readonly string LabelDefault; - public static string LabelWarning => Version switch - { - >= 4 => "badge bg-warning", - _ => "label label-warning" - }; + public static readonly string LabelWarning; - public static string LabelDanger => Version switch - { - >= 4 => "badge bg-danger", - _ => "label label-danger" - }; + public static readonly string LabelDanger; - public static string BtnDefault => Version switch - { - >= 4 => " btn btn-secondary", - _ => " btn btn-default" - }; + public static readonly string BtnDefault; + #endregion + #region Methods + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string GetDataToggle(string value) { return $"{DataToggle}={value}"; @@ -248,15 +261,30 @@ public static string GetDataToggle(string value) _ => $"$('#{modalCssId}').modal('hide');" }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetDataDismiss(string value) + { + return $"{DataDismiss}={value}"; + } - #endregion - public static string ApplyCompatibility(string cssClass) + public static string GetPanel(string className) { - if (Version == 3) return null; - - cssClass = cssClass?.Replace("pull-right", PullRight); + if (Version == 3) + { + return $"panel panel-{className}"; + } - return cssClass; + return $"card border-{className.Replace("default", "jjmasterdata")} border-opacity-75"; } -} + public static string GetPanelHeading(string className) + { + return Version switch + { + >= 4 => $" card-header bg-{className.Replace("default", "jjmasterdata")} bg-opacity-75", + _ => " panel-heading" + }; + } + #endregion +} \ No newline at end of file diff --git a/src/Core/UI/Components/Actions/ActionButtonFactory.cs b/src/Core/UI/Components/Actions/ActionButtonFactory.cs index 6c03efab0..3de568e91 100644 --- a/src/Core/UI/Components/Actions/ActionButtonFactory.cs +++ b/src/Core/UI/Components/Actions/ActionButtonFactory.cs @@ -6,35 +6,23 @@ using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.DataManager.Expressions; using JJMasterData.Core.DataManager.Models; -using JJMasterData.Core.Http.Abstractions; using Microsoft.Extensions.Localization; namespace JJMasterData.Core.UI.Components; -public class ActionButtonFactory(IComponentFactory linkButtonFactory, +public class ActionButtonFactory( + IComponentFactory linkButtonFactory, + ActionScripts actionScripts, ExpressionsService expressionsService, - IMasterDataUrlHelper urlHelper, IEncryptionService encryptionService, IStringLocalizer stringLocalizer) { - private IComponentFactory LinkButtonFactory { get; } = linkButtonFactory; - - private ActionScripts _actionScripts; - private ExpressionsService ExpressionsService { get; } = expressionsService; - - private IMasterDataUrlHelper UrlHelper { get; } = urlHelper; - private IEncryptionService EncryptionService { get; } = encryptionService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private ActionScripts ActionScripts => _actionScripts ??= new ActionScripts(ExpressionsService, UrlHelper, EncryptionService, StringLocalizer); - - public JJLinkButton Create() - { - return LinkButtonFactory.Create(); - } + public JJLinkButton Create() => linkButtonFactory.Create(); + public JJLinkButton Create(BasicAction action, bool visible, bool enabled) { - var button = LinkButtonFactory.Create(); + var button = linkButtonFactory.Create(); button.Tooltip = action.Tooltip; button.Text = action.Text; button.Color = action.Color; @@ -54,8 +42,8 @@ public JJLinkButton Create(BasicAction action, bool visible, bool enabled) private JJLinkButton Create(BasicAction action, FormStateData formStateData) { - var isVisible = ExpressionsService.GetBoolValue(action.VisibleExpression, formStateData); - var isEnabled = ExpressionsService.GetBoolValue(action.EnableExpression, formStateData); + var isVisible = expressionsService.GetBoolValue(action.VisibleExpression, formStateData); + var isEnabled = expressionsService.GetBoolValue(action.EnableExpression, formStateData); return Create(action, isVisible, isEnabled); } @@ -67,10 +55,10 @@ public JJLinkButton CreateGridTableButton(BasicAction action, JJGridView gridVie switch (action) { case UserCreatedAction: - button.OnClientClick = ActionScripts.GetUserActionScript(actionContext, ActionSource.GridTable); + button.OnClientClick = actionScripts.GetUserActionScript(actionContext, ActionSource.GridTable); break; case GridTableAction: - button.OnClientClick = ActionScripts.GetFormActionScript(actionContext, ActionSource.GridTable); + button.OnClientClick = actionScripts.GetFormActionScript(actionContext, ActionSource.GridTable); break; default: throw new JJMasterDataException("Action is not user created or a GridTableAction."); @@ -87,7 +75,7 @@ public JJLinkButton CreateGridToolbarButton(BasicAction action, JJGridView gridV if (action is UserCreatedAction) { - button.OnClientClick = ActionScripts.GetUserActionScript(actionContext, ActionSource.GridToolbar); + button.OnClientClick = actionScripts.GetUserActionScript(actionContext, ActionSource.GridToolbar); return button; } @@ -98,17 +86,17 @@ public JJLinkButton CreateGridToolbarButton(BasicAction action, JJGridView gridV break; case AuditLogGridToolbarAction: button.OnClientClick = - ActionScripts.GetFormActionScript(actionContext, ActionSource.GridToolbar); + actionScripts.GetFormActionScript(actionContext, ActionSource.GridToolbar); break; case ImportAction: - var importationScripts = new DataImportationScripts($"{actionContext.ParentComponentName}-importation", actionContext.FormElement,StringLocalizer, EncryptionService); + var importationScripts = new DataImportationScripts($"{actionContext.ParentComponentName}-importation", actionContext.FormElement,stringLocalizer, encryptionService); button.OnClientClick = importationScripts.GetShowScript(); break; case ExportAction: - var exportationScripts = new DataExportationScripts(actionContext.ParentComponentName, actionContext.FormElement, EncryptionService); + var exportationScripts = new DataExportationScripts(actionContext.ParentComponentName, actionContext.FormElement, encryptionService); button.OnClientClick = exportationScripts.GetExportModalScript(); break; @@ -120,7 +108,7 @@ public JJLinkButton CreateGridToolbarButton(BasicAction action, JJGridView gridV BootstrapHelper.GetModalScript($"{actionContext.ParentComponentName}-filter-modal"); break; case InsertAction: - button.OnClientClick = ActionScripts.GetFormActionScript(actionContext, + button.OnClientClick = actionScripts.GetFormActionScript(actionContext, ActionSource.GridToolbar); break; case LegendAction: @@ -151,7 +139,7 @@ public JJLinkButton CreateFormToolbarButton( if (action is UserCreatedAction) { - button.OnClientClick = ActionScripts.GetUserActionScript(actionContext, ActionSource.FormToolbar); + button.OnClientClick = actionScripts.GetUserActionScript(actionContext, ActionSource.FormToolbar); } else if (action is FormToolbarAction) { @@ -174,7 +162,7 @@ public JJLinkButton CreateFormToolbarButton( } else { - button.OnClientClick = ActionScripts.GetFormActionScript(actionContext, ActionSource.FormToolbar); + button.OnClientClick = actionScripts.GetFormActionScript(actionContext, ActionSource.FormToolbar); } } break; @@ -187,7 +175,7 @@ public JJLinkButton CreateFormToolbarButton( } else { - button.OnClientClick = ActionScripts.GetFormActionScript(actionContext, ActionSource.FormToolbar); + button.OnClientClick = actionScripts.GetFormActionScript(actionContext, ActionSource.FormToolbar); } break; @@ -195,7 +183,7 @@ public JJLinkButton CreateFormToolbarButton( button.OnClientClick = formView.Scripts.GetSetPanelStateScript(PageState.Update); break; case AuditLogFormToolbarAction: - button.OnClientClick = ActionScripts.GetFormActionScript(actionContext, ActionSource.FormToolbar); + button.OnClientClick = actionScripts.GetFormActionScript(actionContext, ActionSource.FormToolbar); break; case SaveAction saveAction: var isAtModal = formView.DataPanel.IsAtModal; @@ -204,7 +192,7 @@ public JJLinkButton CreateFormToolbarButton( else button.Type = saveAction.IsGroup ? LinkButtonType.Link : LinkButtonType.Button; - button.OnClientClick = ActionScripts.GetFormActionScript(actionContext, ActionSource.FormToolbar,true,isAtModal); + button.OnClientClick = actionScripts.GetFormActionScript(actionContext, ActionSource.FormToolbar,true,isAtModal); break; } } @@ -223,7 +211,7 @@ public JJLinkButton CreateFieldButton(BasicAction action, ActionContext actionCo if (action is UserCreatedAction) { - button.OnClientClick = ActionScripts.GetUserActionScript(actionContext, ActionSource.Field); + button.OnClientClick = actionScripts.GetUserActionScript(actionContext, ActionSource.Field); } button.Enabled = button.Enabled && actionContext.FormStateData.PageState is not PageState.View; diff --git a/src/Core/UI/Components/Actions/ActionContext.cs b/src/Core/UI/Components/Actions/ActionContext.cs index 1c7b0f081..9f6c9ec2f 100644 --- a/src/Core/UI/Components/Actions/ActionContext.cs +++ b/src/Core/UI/Components/Actions/ActionContext.cs @@ -14,8 +14,7 @@ public record ActionContext public required FormElement FormElement { get; init; } public required FormStateData FormStateData { get; init; } public required string ParentComponentName { get; init; } - public required bool IsSubmit { get; init; } - + public bool IsSubmit => Action is ISubmittableAction { IsSubmit: true }; public string? FieldName { get; init; } internal ActionMap ToActionMap(ActionSource actionSource) @@ -28,7 +27,7 @@ internal ActionMap ToActionMap(ActionSource actionSource) FieldName = FieldName }; - if (!FormStateData.Values.Any()) + if (FormStateData.Values.Count == 0) return actionMap; var values = FormStateData.Values; diff --git a/src/Core/UI/Components/Actions/ActionMap.cs b/src/Core/UI/Components/Actions/ActionMap.cs index b62b9d9d4..7e2b29ddf 100644 --- a/src/Core/UI/Components/Actions/ActionMap.cs +++ b/src/Core/UI/Components/Actions/ActionMap.cs @@ -1,7 +1,6 @@ #nullable enable using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using JJMasterData.Commons.Exceptions; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Models.Actions; @@ -48,9 +47,9 @@ public ActionMap( if (!f.IsPk) continue; - if (row.ContainsKey(f.Name) && row[f.Name] != null) + if (row.TryGetValue(f.Name, out var value) && value != null) { - PkFieldValues.Add(f.Name, row[f.Name].ToString()!); + PkFieldValues.Add(f.Name, value.ToString()!); } } } @@ -64,13 +63,13 @@ public ActionMap( { var action = ActionSource switch { - ActionSource.GridTable => formElement.Options.GridTableActions.GetOrDefault(ActionName), - ActionSource.GridToolbar => formElement.Options.GridToolbarActions.GetOrDefault(ActionName), - ActionSource.FormToolbar => formElement.Options.FormToolbarActions.GetOrDefault(ActionName), - ActionSource.Field => formElement.Fields[FieldName!].Actions.GetOrDefault(ActionName), + ActionSource.GridTable => formElement.Options.GridTableActions.GetOrDefault(ActionName), + ActionSource.GridToolbar => formElement.Options.GridToolbarActions.GetOrDefault(ActionName), + ActionSource.FormToolbar => formElement.Options.FormToolbarActions.GetOrDefault(ActionName), + ActionSource.Field => formElement.Fields[FieldName!].Actions.GetOrDefault(ActionName), _ => throw new JJMasterDataException("Invalid ActionSource") }; - return action as TAction; + return action; } } \ No newline at end of file diff --git a/src/Core/UI/Components/Actions/ActionScripts.cs b/src/Core/UI/Components/Actions/ActionScripts.cs index 977ea793f..5abe055d8 100644 --- a/src/Core/UI/Components/Actions/ActionScripts.cs +++ b/src/Core/UI/Components/Actions/ActionScripts.cs @@ -6,6 +6,7 @@ using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.DataManager.Expressions; using JJMasterData.Core.DataManager.Models; +using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.Extensions; using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.UI.Routing; @@ -16,20 +17,16 @@ namespace JJMasterData.Core.UI.Components; public class ActionScripts( ExpressionsService expressionsService, + UrlRedirectService urlRedirectService, IMasterDataUrlHelper urlHelper, IEncryptionService encryptionService, IStringLocalizer stringLocalizer) { - private ExpressionsService ExpressionsService { get; } = expressionsService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private IMasterDataUrlHelper UrlHelper { get; } = urlHelper; - private IEncryptionService EncryptionService { get; } = encryptionService; - private string GetInternalUrlScript(InternalAction action, ActionContext actionContext) { var elementRedirect = action.ElementRedirect; string confirmationMessage = - GetParsedConfirmationMessage(StringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); + GetParsedConfirmationMessage(stringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); int popupSize = (int)elementRedirect.ModalSize; var @params = new StringBuilder(); @@ -50,17 +47,37 @@ private string GetInternalUrlScript(InternalAction action, ActionContext actionC } } - string url = UrlHelper.Action("Index", "InternalRedirect", + string url = urlHelper.Action("Index", "InternalRedirect", new { Area = "MasterData", - parameters = EncryptionService.EncryptStringWithUrlEscape(@params.ToString()) + parameters = encryptionService.EncryptStringWithUrlEscape(@params.ToString()) }); return $"ActionHelper.executeInternalRedirect('{url}','{popupSize}','{confirmationMessage}');"; } + + private string GetHtmlTemplateScript( + ActionContext actionContext, + ActionSource actionSource + ) + { + var action = actionContext.Action; + var actionMap = actionContext.ToActionMap(actionSource); + var encryptedActionMap = encryptionService.EncryptObject(actionMap); + + var encryptedRouteContext = + encryptionService.EncryptObject(RouteContext.FromFormElement(actionContext.FormElement, + ComponentContext.FormViewReload)); + + var confirmationMessage = + GetParsedConfirmationMessage(stringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); + + return + $"ActionHelper.executeHTMLTemplate('{actionContext.ParentComponentName}','{stringLocalizer[action.Text]}','{encryptedActionMap}','{encryptedRouteContext}',{(string.IsNullOrEmpty(confirmationMessage) ? "''" : $"'{confirmationMessage}'")});"; + } private string GetUrlRedirectScript( UrlRedirectAction action, @@ -69,24 +86,23 @@ ActionSource actionSource ) { string confirmationMessage = - GetParsedConfirmationMessage(StringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); + GetParsedConfirmationMessage(stringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); if (actionSource is ActionSource.Field or ActionSource.FormToolbar) { var actionMap = actionContext.ToActionMap(actionSource); - var encryptedActionMap = EncryptionService.EncryptActionMap(actionMap); + var encryptedActionMap = encryptionService.EncryptObject(actionMap); var routeContext = RouteContext.FromFormElement(actionContext.FormElement, ComponentContext.UrlRedirect); - var encryptedRouteContext = EncryptionService.EncryptRouteContext(routeContext); + var encryptedRouteContext = encryptionService.EncryptObject(routeContext); return $"ActionHelper.executeRedirectAction('{actionContext.ParentComponentName}','{encryptedRouteContext}','{encryptedActionMap}'{(string.IsNullOrEmpty(confirmationMessage) ? "" : $",'{confirmationMessage}'")});"; } var script = new StringBuilder(); - string url = ExpressionsService.ReplaceExpressionWithParsedValues(HttpUtility.UrlDecode(action.UrlRedirect), - actionContext.FormStateData, action.EncryptParameters); + string url = urlRedirectService.GetParsedUrl(action, actionContext.FormStateData); string isModal = action.IsModal ? "true" : "false"; string isIframe = action.IsIframe ? "true" : "false"; @@ -103,7 +119,7 @@ ActionSource actionSource script.Append("',"); script.Append(isIframe); script.Append(",'"); - script.Append(StringLocalizer[action.ConfirmationMessage]); + script.Append(stringLocalizer[action.ConfirmationMessage]); script.Append("');"); return script.ToString(); @@ -118,9 +134,9 @@ public string GetFormActionScript( var formElement = actionContext.FormElement; var action = actionContext.Action; var actionMap = actionContext.ToActionMap(actionSource); - var encryptedActionMap = EncryptionService.EncryptActionMap(actionMap); + var encryptedActionMap = encryptionService.EncryptObject(actionMap); var confirmationMessage = - GetParsedConfirmationMessage(StringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); + GetParsedConfirmationMessage(stringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); var actionData = new ActionData { @@ -156,7 +172,7 @@ public string GetFormActionScript( private string GetGridRouteContext(FormElement formElement) { var gridRouteContext = RouteContext.FromFormElement(formElement, ComponentContext.GridViewReload); - var encryptedRouteContext = EncryptionService.EncryptRouteContext(gridRouteContext); + var encryptedRouteContext = encryptionService.EncryptObject(gridRouteContext); return encryptedRouteContext; } @@ -173,9 +189,10 @@ internal string GetUserActionScript( UrlRedirectAction urlRedirectAction => GetUrlRedirectScript(urlRedirectAction, actionContext, actionSource), SqlCommandAction => GetSqlCommandScript(actionContext, actionSource), ScriptAction jsAction => HttpUtility.HtmlAttributeEncode( - ExpressionsService.ReplaceExpressionWithParsedValues(jsAction.OnClientClick, formStateData) ?? + expressionsService.ReplaceExpressionWithParsedValues(jsAction.OnClientClick, formStateData) ?? string.Empty), InternalAction internalAction => GetInternalUrlScript(internalAction, actionContext), + HtmlTemplateAction => GetHtmlTemplateScript(actionContext, actionSource), _ => GetFormActionScript(actionContext, actionSource) }; } @@ -185,14 +202,14 @@ private string GetSqlCommandScript(ActionContext actionContext, ActionSource act { var action = actionContext.Action; var actionMap = actionContext.ToActionMap(actionSource); - var encryptedActionMap = EncryptionService.EncryptActionMap(actionMap); + var encryptedActionMap = encryptionService.EncryptObject(actionMap); var encryptedRouteContext = - EncryptionService.EncryptRouteContext(RouteContext.FromFormElement(actionContext.FormElement, + encryptionService.EncryptObject(RouteContext.FromFormElement(actionContext.FormElement, ComponentContext.FormViewReload)); var confirmationMessage = - GetParsedConfirmationMessage(StringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); + GetParsedConfirmationMessage(stringLocalizer[action.ConfirmationMessage], actionContext.FormStateData); return $"ActionHelper.executeSqlCommand('{actionContext.ParentComponentName}','{encryptedActionMap}','{encryptedRouteContext}', {(actionContext.IsSubmit ? "true" : "false")},{(string.IsNullOrEmpty(confirmationMessage) ? "''" : $"'{confirmationMessage}'")});"; @@ -203,6 +220,6 @@ private string GetSqlCommandScript(ActionContext actionContext, ActionSource act private string GetParsedConfirmationMessage(string originalMessage, FormStateData formStateData) { - return ExpressionsService.ReplaceExpressionWithParsedValues(originalMessage, formStateData); + return expressionsService.ReplaceExpressionWithParsedValues(originalMessage, formStateData); } } \ No newline at end of file diff --git a/src/Core/UI/Components/AsyncComponent.cs b/src/Core/UI/Components/AsyncComponent.cs index 429088532..5833aa54c 100644 --- a/src/Core/UI/Components/AsyncComponent.cs +++ b/src/Core/UI/Components/AsyncComponent.cs @@ -9,12 +9,12 @@ namespace JJMasterData.Core.UI.Components; /// public abstract class AsyncComponent : ComponentBase { - public async Task GetResultAsync() + public Task GetResultAsync() { if (Visible) - return await BuildResultAsync(); + return BuildResultAsync(); - return new EmptyComponentResult(); + return Task.FromResult(EmptyComponentResult.Value); } protected abstract Task BuildResultAsync(); diff --git a/src/Core/UI/Components/AuditLog/AuditLogViewFactory.cs b/src/Core/UI/Components/AuditLog/AuditLogViewFactory.cs index 088092c55..a2354f08d 100644 --- a/src/Core/UI/Components/AuditLog/AuditLogViewFactory.cs +++ b/src/Core/UI/Components/AuditLog/AuditLogViewFactory.cs @@ -10,7 +10,7 @@ namespace JJMasterData.Core.UI.Components; -internal class AuditLogViewFactory(IHttpContext httpContext, +internal sealed class AuditLogViewFactory(IHttpContext httpContext, IEntityRepository entityRepository, AuditLogService auditLogService, IDataDictionaryRepository dataDictionaryRepository, @@ -19,23 +19,15 @@ internal class AuditLogViewFactory(IHttpContext httpContext, IStringLocalizer stringLocalizer) : IFormElementComponentFactory { - private IHttpContext HttpContext { get; } = httpContext; - private IEntityRepository EntityRepository { get; } = entityRepository; - private AuditLogService AuditLogService { get; } = auditLogService; - private IDataDictionaryRepository DataDictionaryRepository { get; } = dataDictionaryRepository; - private IComponentFactory ComponentFactory { get; } = componentFactory; - private IEncryptionService EncryptionService { get; } = encryptionService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - public JJAuditLogView Create(FormElement formElement) { - return new JJAuditLogView(formElement, HttpContext, EntityRepository, AuditLogService, ComponentFactory, - EncryptionService, StringLocalizer); + return new JJAuditLogView(formElement, httpContext, entityRepository, auditLogService, componentFactory, + encryptionService, stringLocalizer); } - public async Task CreateAsync(string elementName) + public async ValueTask CreateAsync(string elementName) { - var formElement = await DataDictionaryRepository.GetFormElementAsync(elementName); + var formElement = await dataDictionaryRepository.GetFormElementAsync(elementName); return Create(formElement); } } \ No newline at end of file diff --git a/src/Core/UI/Components/AuditLog/JJAuditLogView.cs b/src/Core/UI/Components/AuditLog/JJAuditLogView.cs index f097b5957..f527f76a1 100644 --- a/src/Core/UI/Components/AuditLog/JJAuditLogView.cs +++ b/src/Core/UI/Components/AuditLog/JJAuditLogView.cs @@ -14,6 +14,7 @@ using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.Extensions; using JJMasterData.Core.Http.Abstractions; +using JJMasterData.Core.Tasks; using JJMasterData.Core.UI.Html; using JJMasterData.Core.UI.Routing; using Microsoft.Extensions.Localization; @@ -30,7 +31,7 @@ public class JJAuditLogView : AsyncComponent private RouteContext _routeContext; - internal RouteContext RouteContext + private RouteContext RouteContext { get { @@ -44,7 +45,7 @@ internal RouteContext RouteContext } } - internal ComponentContext ComponentContext => RouteContext.ComponentContext; + private ComponentContext ComponentContext => RouteContext.ComponentContext; /// /// Id do usuário Atual @@ -57,12 +58,12 @@ internal RouteContext RouteContext private IHttpContext CurrentContext { get; } - public AuditLogService AuditLogService { get; } + private AuditLogService AuditLogService { get; } private IEncryptionService EncryptionService { get; } public JJGridView GridView => _gridView ??= CreateGridViewLog(); - internal JJDataPanel DataPanel + private JJDataPanel DataPanel { get { @@ -76,10 +77,10 @@ internal JJDataPanel DataPanel } } - public FormElement FormElement { get; private set; } + public FormElement FormElement { get; } private IStringLocalizer StringLocalizer { get; } - internal IEntityRepository EntityRepository { get; } + private IEntityRepository EntityRepository { get; } public JJAuditLogView( FormElement formElement, @@ -90,7 +91,7 @@ public JJAuditLogView( IEncryptionService encryptionService, IStringLocalizer stringLocalizer) { - Name = $"{ComponentNameGenerator.Create(formElement.Name)}-audit-log-view"; + Name = $"{formElement.Name.ToLowerInvariant()}-audit-log-view"; _componentFactory = componentFactory; FormElement = formElement; CurrentContext = currentContext; @@ -103,7 +104,7 @@ public JJAuditLogView( protected override async Task BuildResultAsync() { string logId = CurrentContext.Request.Form[$"audit-log-id-{FormElement.Name}"]; - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); if (string.IsNullOrEmpty(logId)) { @@ -160,7 +161,7 @@ private async Task GetEntryKey(Dictionary values) }); if (result.Data.Count > 0) - entryId = result.Data.ElementAt(0).ElementAt(0).Value.ToString(); + entryId = result.Data[0].ElementAt(0).Value.ToString(); return entryId; } @@ -175,10 +176,10 @@ public async Task GetLogDetailsHtmlAsync(Dictionary private async Task GetLogDetailsHtmlAsync(string logId) { - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); html.WithCssClass("mb-2"); if (GridView.ShowTitle) - html.AppendComponent(await GridView.GetTitleAsync()); + html.AppendComponent(GridView.GetTitle()); if (string.IsNullOrEmpty(logId)) { @@ -314,7 +315,7 @@ private JJGridView CreateGridViewLog() alert.Icon = IconType.SolidTriangleExclamation; alert.Messages.Add(StringLocalizer["Audit Log is disabled. Please contact the administrator."]); args.HtmlBuilder.AppendComponent(alert); - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; }; } @@ -381,11 +382,11 @@ private async Task GetLogListGroupHtml(string recordsKey, string vi } if (row["origin"]!.Equals((int)DataContextSource.Api)) - origem = DataContextSource.Api.ToString(); + origem = nameof(DataContextSource.Api); else if (row["origin"].Equals((int)DataContextSource.Form)) - origem = DataContextSource.Form.ToString(); + origem = nameof(DataContextSource.Form); else if (row["origin"].Equals((int)DataContextSource.Api)) - origem = DataContextSource.Upload.ToString(); + origem = nameof(DataContextSource.Upload); string logId = row["id"].ToString(); string message = $"{action} [{origem}] {row["userId"]?.ToString() ?? string.Empty}"; @@ -394,7 +395,7 @@ private async Task GetLogListGroupHtml(string recordsKey, string vi { var routeContext = RouteContext.FromFormElement(FormElement, ComponentContext.AuditLogView); - var encryptedRouteContext = EncryptionService.EncryptRouteContext(routeContext); + var encryptedRouteContext = EncryptionService.EncryptObject(routeContext); a.WithAttribute("href", $"javascript:AuditLogViewHelper.loadAuditLog('{FormElement.ParentName}','{logId}', '{encryptedRouteContext}')"); @@ -413,13 +414,15 @@ private async Task GetLogListGroupHtml(string recordsKey, string vi }); div.Append(HtmlTag.B, b => { b.AppendText(message); }); div.Append(HtmlTag.Br); - div.Append(HtmlTag.B, b => { b.AppendText((string)row["modified"]!.ToString()); }); + div.Append(HtmlTag.B, b => { b.AppendText(row["modified"]!.ToString()); }); div.Append(HtmlTag.Br); div.Append(HtmlTag.B, b => { b.AppendText($"IP: {row["ip"]}"); - var infoIcon = _componentFactory.Html.Icon.Create(IconType.InfoCircle); - infoIcon.CssClass = "help-description"; + var infoIcon = new JJIcon(IconType.InfoCircle) + { + CssClass = "help-description" + }; b.AppendComponent(infoIcon); b.WithToolTip(row["browser"]?.ToString()); }); diff --git a/src/Core/UI/Components/ComponentBase.cs b/src/Core/UI/Components/ComponentBase.cs index 0e4845968..229bd455d 100644 --- a/src/Core/UI/Components/ComponentBase.cs +++ b/src/Core/UI/Components/ComponentBase.cs @@ -55,8 +55,7 @@ public void SetAttr(string key, object value) { if (value == null || string.IsNullOrEmpty(value.ToString())) { - if (Attributes.ContainsKey(key)) - Attributes.Remove(key); + Attributes.Remove(key); } else { diff --git a/src/Core/UI/Components/ComponentFactory.cs b/src/Core/UI/Components/ComponentFactory.cs index 106f6ac19..80ace33f0 100644 --- a/src/Core/UI/Components/ComponentFactory.cs +++ b/src/Core/UI/Components/ComponentFactory.cs @@ -6,11 +6,10 @@ namespace JJMasterData.Core.UI.Components; -internal class ComponentFactory(IServiceProvider serviceProvider) : IComponentFactory +internal sealed class ComponentFactory(IServiceProvider serviceProvider) : IComponentFactory { private RouteContext? _routeContext; private HtmlComponentFactory? _htmlComponentFactory; - private IServiceProvider ServiceProvider => serviceProvider; public IFormElementComponentFactory AuditLog => GetFactory>(); @@ -43,16 +42,13 @@ internal class ComponentFactory(IServiceProvider serviceProvider) : IComponentFa GetFactory(); public HtmlComponentFactory Html => - _htmlComponentFactory ??= ServiceProvider.GetRequiredService(); + _htmlComponentFactory ??= serviceProvider.GetRequiredService(); - public ActionButtonFactory ActionButton => GetFactory(); - public TextGroupFactory TextGroup => GetFactory(); + public ActionButtonFactory ActionButton => GetFactory(); + public TextGroupFactory TextGroup => GetFactory(); public RouteContext RouteContext => - _routeContext ??= ServiceProvider.GetRequiredService().Create(); + _routeContext ??= serviceProvider.GetRequiredService().Create(); - private T GetFactory() where T : notnull - { - return ServiceProvider.GetRequiredService(); - } + private T GetFactory() where T : notnull => serviceProvider.GetRequiredService(); } \ No newline at end of file diff --git a/src/Core/UI/Components/ComponentNameGenerator.cs b/src/Core/UI/Components/ComponentNameGenerator.cs deleted file mode 100644 index b6e50ec47..000000000 --- a/src/Core/UI/Components/ComponentNameGenerator.cs +++ /dev/null @@ -1,12 +0,0 @@ -#nullable enable - - -namespace JJMasterData.Core.UI.Components; - -public static class ComponentNameGenerator -{ - public static string Create(string elementName) - { - return elementName.ToLower(); - } -} \ No newline at end of file diff --git a/src/Core/UI/Components/ContentComponentResult.cs b/src/Core/UI/Components/ContentComponentResult.cs index 59a415aeb..4cbbb545a 100644 --- a/src/Core/UI/Components/ContentComponentResult.cs +++ b/src/Core/UI/Components/ContentComponentResult.cs @@ -7,9 +7,9 @@ #endif namespace JJMasterData.Core.UI.Components; -public class ContentComponentResult(HtmlBuilder htmlBuilder) : HtmlComponentResult(htmlBuilder) +public sealed class ContentComponentResult(HtmlBuilder htmlBuilder) : HtmlComponentResult(htmlBuilder) #if NET - ,IActionResult + , IActionResult #endif { #if NET diff --git a/src/Core/UI/Components/Controls/CheckBox/CheckBoxFactory.cs b/src/Core/UI/Components/Controls/CheckBox/CheckBoxFactory.cs index 25290a4e7..a78fbaa46 100644 --- a/src/Core/UI/Components/Controls/CheckBox/CheckBoxFactory.cs +++ b/src/Core/UI/Components/Controls/CheckBox/CheckBoxFactory.cs @@ -6,14 +6,10 @@ namespace JJMasterData.Core.UI.Components; -internal class CheckBoxFactory(IFormValues formValues, IStringLocalizer stringLocalizer) +internal sealed class CheckBoxFactory(IFormValues formValues, IStringLocalizer stringLocalizer) : IControlFactory { - private IFormValues FormValues { get; } = formValues; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - - - public JJCheckBox Create() => new(FormValues,StringLocalizer); + public JJCheckBox Create() => new(formValues,stringLocalizer); public JJCheckBox Create(FormElement formElement, FormElementField field, ControlContext context) { var checkBox = Create(); diff --git a/src/Core/UI/Components/Controls/CheckBox/JJCheckBox.cs b/src/Core/UI/Components/Controls/CheckBox/JJCheckBox.cs index 694b3de1f..3a62e946e 100644 --- a/src/Core/UI/Components/Controls/CheckBox/JJCheckBox.cs +++ b/src/Core/UI/Components/Controls/CheckBox/JJCheckBox.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using JJMasterData.Commons.Extensions; using JJMasterData.Commons.Localization; using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.UI.Html; @@ -44,13 +43,13 @@ public JJCheckBox(IFormValues formValues, IStringLocalizer } - protected override Task BuildResultAsync() + protected override ValueTask BuildResultAsync() { var html = GetHtmlBuilder(); var result = new RenderedComponentResult(html); - return Task.FromResult(result); + return new ValueTask(result); } @@ -65,7 +64,7 @@ public HtmlBuilder GetHtmlBuilder() private HtmlBuilder GetInputHtml() { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClassIf(IsSwitch, "form-switch"); div.WithCssClassIf(IsSwitch && SwitchSize is not null, SwitchSize?.GetCssClass()); @@ -75,9 +74,9 @@ private HtmlBuilder GetInputHtml() { var checkboxHelperScript = $"CheckboxHelper.check('{Name.Replace(".", "_")}');"; - if (Attributes.ContainsKey("onchange")) + if (Attributes.TryGetValue("onchange", out string value)) { - Attributes["onchange"] = checkboxHelperScript + Attributes["onchange"]; + Attributes["onchange"] = checkboxHelperScript + value; } else { diff --git a/src/Core/UI/Components/Controls/ColorPicker/ColorPickerFactory.cs b/src/Core/UI/Components/Controls/ColorPicker/ColorPickerFactory.cs index 1fc51f2c5..60e22761c 100644 --- a/src/Core/UI/Components/Controls/ColorPicker/ColorPickerFactory.cs +++ b/src/Core/UI/Components/Controls/ColorPicker/ColorPickerFactory.cs @@ -3,13 +3,11 @@ namespace JJMasterData.Core.UI.Components.ColorPicker; -internal class ColorPickerFactory(IFormValues formValues) : IControlFactory +internal sealed class ColorPickerFactory(IFormValues formValues) : IControlFactory { - private IFormValues FormValues { get; } = formValues; - public JJColorPicker Create() { - return new JJColorPicker(FormValues); + return new JJColorPicker(formValues); } public JJColorPicker Create(FormElement formElement, FormElementField field, ControlContext context) diff --git a/src/Core/UI/Components/Controls/ColorPicker/JJColorPicker.cs b/src/Core/UI/Components/Controls/ColorPicker/JJColorPicker.cs index 77254615a..4fb58b9f1 100644 --- a/src/Core/UI/Components/Controls/ColorPicker/JJColorPicker.cs +++ b/src/Core/UI/Components/Controls/ColorPicker/JJColorPicker.cs @@ -11,7 +11,7 @@ public class JJColorPicker(IFormValues formValues) : ControlBase(formValues) public HtmlBuilder GetHtmlBuilder() { - return new Input() + return new HtmlBuilder(HtmlTag.Input) .WithNameAndId(Name) .WithValue(Text) .WithAttribute("title", Title) @@ -21,8 +21,8 @@ public HtmlBuilder GetHtmlBuilder() .WithCssClass("form-control form-control-color"); } - protected override Task BuildResultAsync() + protected override ValueTask BuildResultAsync() { - return Task.FromResult(new RenderedComponentResult(GetHtmlBuilder())); + return new ValueTask(new RenderedComponentResult(GetHtmlBuilder())); } } \ No newline at end of file diff --git a/src/Core/UI/Components/Controls/ComboBox/ComboBoxFactory.cs b/src/Core/UI/Components/Controls/ComboBox/ComboBoxFactory.cs index 1300c9a7b..e6a37d739 100644 --- a/src/Core/UI/Components/Controls/ComboBox/ComboBoxFactory.cs +++ b/src/Core/UI/Components/Controls/ComboBox/ComboBoxFactory.cs @@ -8,24 +8,20 @@ namespace JJMasterData.Core.UI.Components; -internal class ComboBoxFactory(IFormValues formValues, +internal sealed class ComboBoxFactory( + IFormValues formValues, DataItemService dataItemService, IStringLocalizer stringLocalizer, ILoggerFactory loggerFactory) : IControlFactory { - private IFormValues FormValues { get; } = formValues; - private DataItemService DataItemService { get; } = dataItemService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private ILoggerFactory LoggerFactory { get; } = loggerFactory; - public JJComboBox Create() { return new JJComboBox( - FormValues, - DataItemService, - StringLocalizer, - LoggerFactory.CreateLogger()); + formValues, + dataItemService, + stringLocalizer, + loggerFactory.CreateLogger()); } public JJComboBox Create(FormElement formElement, FormElementField field, ControlContext controlContext) diff --git a/src/Core/UI/Components/Controls/ComboBox/JJComboBox.cs b/src/Core/UI/Components/Controls/ComboBox/JJComboBox.cs index 582eccbd3..870a38ada 100644 --- a/src/Core/UI/Components/Controls/ComboBox/JJComboBox.cs +++ b/src/Core/UI/Components/Controls/ComboBox/JJComboBox.cs @@ -72,7 +72,7 @@ public JJComboBox( FormStateData = new FormStateData(defaultValues, PageState.List); } - protected override async Task BuildResultAsync() + protected override async ValueTask BuildResultAsync() { if (DataItem == null) throw new ArgumentException($"FormElementDataItem property is null for JJComboBox {Name}"); @@ -81,7 +81,7 @@ protected override async Task BuildResultAsync() if (ReadOnly && Enabled) { - var combobox = new Div(); + var combobox = new HtmlBuilder(HtmlTag.Div); combobox.AppendRange(GetReadOnlyInputs(values)); return new RenderedComponentResult(combobox); } @@ -109,7 +109,7 @@ private HtmlBuilder GetSelectHtml(List values) if (UseFloatingLabel) { - return new Div().WithCssClass("form-floating") + return new HtmlBuilder(HtmlTag.Div).WithCssClass("form-floating") .Append(select) .AppendLabel(label => { @@ -187,7 +187,7 @@ private IEnumerable GetReadOnlyInputs(IEnumerable va { if (SelectedValue != null) { - var hiddenInput = new Input() + var hiddenInput = new HtmlBuilder(HtmlTag.Input) .WithAttribute("type", "hidden") .WithNameAndId(Name) .WithValue(SelectedValue); @@ -197,7 +197,7 @@ private IEnumerable GetReadOnlyInputs(IEnumerable va var selectedText = GetSelectedText(values); - var readonlyInput = new Input() + var readonlyInput = new HtmlBuilder(HtmlTag.Input) .WithNameAndId($"cboview_{Name}") .WithCssClass("form-control form-select") .WithCssClass(CssClass) @@ -233,8 +233,7 @@ public Task> GetValuesAsync() { return DataItemService.GetValuesAsync(DataItem, new DataQuery(FormStateData, ConnectionId)); } - - + /// /// Recovers the description from the selected value; /// @@ -249,7 +248,7 @@ public Task> GetValuesAsync() if (DataItem.ShowIcon) { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); var icon = new JJIcon(item.Icon, item.IconColor, item.Description) { @@ -279,10 +278,9 @@ public Task> GetValuesAsync() SearchId = searchId }; var values = await DataItemService.GetValuesAsync(DataItem, dataQuery); - return values.FirstOrDefault(v => v.Id == searchId); + return values.Find(v => v.Id == searchId); } - private bool IsManualValues() { if (DataItem.Items == null) diff --git a/src/Core/UI/Components/Controls/ControlBase.cs b/src/Core/UI/Components/Controls/ControlBase.cs index b6e4d0d5b..a72050dd3 100644 --- a/src/Core/UI/Components/Controls/ControlBase.cs +++ b/src/Core/UI/Components/Controls/ControlBase.cs @@ -4,7 +4,7 @@ namespace JJMasterData.Core.UI.Components; -public abstract class ControlBase(IFormValues formValues) : AsyncComponent +public abstract class ControlBase(IFormValues formValues) : ComponentBase { private string _text; @@ -49,7 +49,7 @@ public string Text set => _text = value; } - public async Task GetHtmlBuilderAsync() + public async ValueTask GetHtmlBuilderAsync() { var result = await GetResultAsync(); @@ -60,4 +60,14 @@ public async Task GetHtmlBuilderAsync() return new HtmlBuilder(); } + + public ValueTask GetResultAsync() + { + if (Visible) + return BuildResultAsync(); + + return new ValueTask(EmptyComponentResult.Value); + } + + protected abstract ValueTask BuildResultAsync(); } diff --git a/src/Core/UI/Components/Controls/ControlFactory.cs b/src/Core/UI/Components/Controls/ControlFactory.cs index d4b76d417..73d3ef8da 100644 --- a/src/Core/UI/Components/Controls/ControlFactory.cs +++ b/src/Core/UI/Components/Controls/ControlFactory.cs @@ -14,9 +14,6 @@ namespace JJMasterData.Core.UI.Components; public class ControlFactory(IServiceProvider serviceProvider, ExpressionsService expressionsService) { - private IServiceProvider ServiceProvider { get; } = serviceProvider; - private ExpressionsService ExpressionsService { get; } = expressionsService; - public IControlFactory CheckBox => GetControlFactory(); public IControlFactory ComboBox => GetControlFactory(); public IControlFactory Lookup => GetControlFactory(); @@ -31,13 +28,13 @@ public class ControlFactory(IServiceProvider serviceProvider, private IControlFactory GetControlFactory() where TControl : ControlBase { - return ServiceProvider.GetRequiredService>(); + return serviceProvider.GetRequiredService>(); } internal TControl Create(FormElement formElement, FormElementField field, ControlContext controlContext) where TControl : ControlBase { - var factory = ServiceProvider.GetRequiredService>(); + var factory = serviceProvider.GetRequiredService>(); return factory.Create( formElement, @@ -60,10 +57,10 @@ public ControlBase Create( } var control = Create(formElement, field, context); - control.Enabled = ExpressionsService.GetBoolValue(field.EnableExpression, formStateData); + control.Enabled = expressionsService.GetBoolValue(field.EnableExpression, formStateData); if (field.ReadOnlyExpression != null) - control.ReadOnly = ExpressionsService.GetBoolValue(field.ReadOnlyExpression, formStateData); + control.ReadOnly = expressionsService.GetBoolValue(field.ReadOnlyExpression, formStateData); return control; } diff --git a/src/Core/UI/Components/Controls/IconPicker/JJIconPicker.cs b/src/Core/UI/Components/Controls/IconPicker/JJIconPicker.cs index 1ed868521..dbd7fb0e4 100644 --- a/src/Core/UI/Components/Controls/IconPicker/JJIconPicker.cs +++ b/src/Core/UI/Components/Controls/IconPicker/JJIconPicker.cs @@ -21,7 +21,7 @@ public class JJIconPicker( public IconType? SelectedIcon { get; set; } public string? Id { get; set; } - protected override async Task BuildResultAsync() + protected override async ValueTask BuildResultAsync() { var id = Id ?? Name; var comboBox = comboBoxFactory.Create(); @@ -52,7 +52,7 @@ protected override async Task BuildResultAsync() comboBox.Attributes["data-size"] = "false"; comboBox.Attributes["data-sanitize"] = "false"; comboBox.Attributes["data-none-results-text"] = stringLocalizer["No icons found."]; - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClassIf(Enabled,"input-group"); await div.AppendControlAsync(comboBox); div.AppendIf(Enabled,HtmlTag.Div,div => diff --git a/src/Core/UI/Components/Controls/Lookup/JJLookup.cs b/src/Core/UI/Components/Controls/Lookup/JJLookup.cs index ed7114432..ab83afeb1 100644 --- a/src/Core/UI/Components/Controls/Lookup/JJLookup.cs +++ b/src/Core/UI/Components/Controls/Lookup/JJLookup.cs @@ -5,7 +5,6 @@ using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Localization; using JJMasterData.Commons.Security.Cryptography.Abstractions; -using JJMasterData.Commons.Util; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataManager.Models; using JJMasterData.Core.DataManager.Services; @@ -148,7 +147,7 @@ internal JJLookup( #endregion - protected override async Task BuildResultAsync() + protected override async ValueTask BuildResultAsync() { if (ComponentContext is ComponentContext.LookupDescription && HttpRequest.QueryString["fieldName"] == FieldName) { @@ -178,13 +177,13 @@ private async Task GetLookupHtml() Attributes["lookup-field-name"] = FieldName; - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("input-group mb-3 d-flex" ); var routeContext = new RouteContext(ElementName, ParentElementName, ComponentContext.LookupDescription); - Attributes["route-context"] = EncryptionService.EncryptRouteContext(routeContext); + Attributes["route-context"] = EncryptionService.EncryptObject(routeContext); var flexLayout = GetFlexLayout(); diff --git a/src/Core/UI/Components/Controls/Lookup/LookupFactory.cs b/src/Core/UI/Components/Controls/Lookup/LookupFactory.cs index 18dc24413..5ae97c80c 100644 --- a/src/Core/UI/Components/Controls/Lookup/LookupFactory.cs +++ b/src/Core/UI/Components/Controls/Lookup/LookupFactory.cs @@ -8,7 +8,7 @@ namespace JJMasterData.Core.UI.Components; -internal class LookupFactory( +internal sealed class LookupFactory( IHttpRequest httpRequest, FormValuesService formValuesService, LookupService lookupService, @@ -18,27 +18,19 @@ internal class LookupFactory( IStringLocalizer stringLocalizer) : IControlFactory { - private IHttpRequest HttpRequest { get; } = httpRequest; - private FormValuesService FormValuesService { get; } = formValuesService; - private LookupService LookupService { get; } = lookupService; - private IComponentFactory ComponentFactory { get; } = componentFactory; - private IEncryptionService EncryptionService { get; } = encryptionService; - private RouteContextFactory RouteContextFactory { get; } = routeContextFactory; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - - + public JJLookup Create() { return new JJLookup( null, null, - HttpRequest, - RouteContextFactory, - FormValuesService, - EncryptionService, - LookupService, - StringLocalizer, - ComponentFactory); + httpRequest, + routeContextFactory, + formValuesService, + encryptionService, + lookupService, + stringLocalizer, + componentFactory); } public JJLookup Create(FormElement formElement, FormElementField field, ControlContext controlContext) @@ -46,13 +38,13 @@ public JJLookup Create(FormElement formElement, FormElementField field, ControlC var lookup = new JJLookup( field, controlContext, - HttpRequest, - RouteContextFactory, - FormValuesService, - EncryptionService, - LookupService, - StringLocalizer, - ComponentFactory); + httpRequest, + routeContextFactory, + formValuesService, + encryptionService, + lookupService, + stringLocalizer, + componentFactory); lookup.ElementName = formElement.Name; lookup.ParentElementName = formElement.ParentName; diff --git a/src/Core/UI/Components/Controls/RadioButton/JJRadioButton.cs b/src/Core/UI/Components/Controls/RadioButton/JJRadioButton.cs index a889b7814..129d2c192 100644 --- a/src/Core/UI/Components/Controls/RadioButton/JJRadioButton.cs +++ b/src/Core/UI/Components/Controls/RadioButton/JJRadioButton.cs @@ -5,21 +5,21 @@ namespace JJMasterData.Core.UI.Components; -internal class JJRadioButton(IFormValues formValues) : ControlBase(formValues) +internal sealed class JJRadioButton(IFormValues formValues) : ControlBase(formValues) { public string Id { get; set; } public HtmlBuilder LabelHtml { get; } = new(); public bool IsChecked { get; set; } public DataItemRadioLayout Layout { get; set; } - protected override Task BuildResultAsync() + protected override ValueTask BuildResultAsync() { - return Task.FromResult(new RenderedComponentResult(GetHtmlBuilder())); + return new ValueTask(new RenderedComponentResult(GetHtmlBuilder())); } public HtmlBuilder GetHtmlBuilder() { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("form-check"); div.WithCssClass(CssClass); div.WithCssClassIf(Layout is DataItemRadioLayout.Horizontal, "form-check-inline"); diff --git a/src/Core/UI/Components/Controls/RadioButton/JJRadioButtonGroup.cs b/src/Core/UI/Components/Controls/RadioButton/JJRadioButtonGroup.cs index ea9e53efc..283cc0cd4 100644 --- a/src/Core/UI/Components/Controls/RadioButton/JJRadioButtonGroup.cs +++ b/src/Core/UI/Components/Controls/RadioButton/JJRadioButtonGroup.cs @@ -34,7 +34,7 @@ public string? SelectedValue public Guid? ConnectionId { get; set; } public FormStateData FormStateData { get; set; } = null!; - protected override async Task BuildResultAsync() + protected override async ValueTask BuildResultAsync() { var values = await GetValuesAsync(); diff --git a/src/Core/UI/Components/Controls/RadioButton/RadioButtonGroupFactory.cs b/src/Core/UI/Components/Controls/RadioButton/RadioButtonGroupFactory.cs index 3936603c9..3a264430f 100644 --- a/src/Core/UI/Components/Controls/RadioButton/RadioButtonGroupFactory.cs +++ b/src/Core/UI/Components/Controls/RadioButton/RadioButtonGroupFactory.cs @@ -4,14 +4,11 @@ namespace JJMasterData.Core.UI.Components; -internal class RadioButtonGroupFactory(DataItemService dataItemService, IFormValues formValues) : IControlFactory +internal sealed class RadioButtonGroupFactory(DataItemService dataItemService, IFormValues formValues) : IControlFactory { - private DataItemService DataItemService { get; } = dataItemService; - private IFormValues FormValues { get; } = formValues; - public JJRadioButtonGroup Create() { - return new JJRadioButtonGroup(DataItemService, FormValues); + return new JJRadioButtonGroup(dataItemService, formValues); } public JJRadioButtonGroup Create(FormElement formElement, FormElementField field, ControlContext context) diff --git a/src/Core/UI/Components/Controls/SearchBox/JJSearchBox.cs b/src/Core/UI/Components/Controls/SearchBox/JJSearchBox.cs index 29af6f6b8..c906385b5 100644 --- a/src/Core/UI/Components/Controls/SearchBox/JJSearchBox.cs +++ b/src/Core/UI/Components/Controls/SearchBox/JJSearchBox.cs @@ -25,12 +25,12 @@ public class JJSearchBox : ControlBase, IDataItemControl #region "Events" /// - /// Evento disparado para recuperar os registros com parte do texto digitado + /// Event triggered to retrieve records with part of the text entered /// public event EventHandler? OnSearchQuery; /// - /// Evento disparado para recuperar a descrição com base no Id + /// Event triggered to retrieve description based on Id /// public event EventHandler? OnSearchId; @@ -83,7 +83,7 @@ public string HtmlId } /// - /// Quantidade minima de caracteres digitado para disparar a pesquisa + /// Minimum number of characters entered to trigger the search /// (Default = 1) /// public int TriggerLength @@ -98,8 +98,8 @@ public int TriggerLength } /// - /// Numero máximo de itens que será exibido na lista de pesquisa - /// (Default = 10) + /// Maximum number of items that will be displayed in the search list + /// (Default = 30) /// public int NumberOfItems { @@ -107,13 +107,13 @@ public int NumberOfItems { if (Attributes.TryGetValue(NumberOfItemsAttribute, out var attribute)) return int.Parse(attribute); - return 10; + return 30; } set => Attributes[NumberOfItemsAttribute] = value.ToString(); } /// - /// Exibir barra de rolagem na lista de pesquisa + /// Show scrollbar in search list /// (Default = false) /// /// @@ -127,10 +127,10 @@ public bool ScrollBar { get { - if (!Attributes.ContainsKey(ScrollbarAttribute)) + if (!Attributes.TryGetValue(ScrollbarAttribute, out string? value)) return true; - return Attributes[ScrollbarAttribute].Equals("true"); + return value.Equals("true"); } set { @@ -140,7 +140,7 @@ public bool ScrollBar } /// - /// Id correspondente ao texto pesquisado + /// Id corresponding to the searched text /// public async Task GetSelectedValueAsync() { @@ -168,7 +168,7 @@ public bool ScrollBar } /// - /// Ao recarregar o painel, manter os valores digitados no formulário + /// When reloading the panel, keep the values entered in the form /// (Default=True) /// public bool AutoReloadFormFields { get; set; } @@ -226,7 +226,6 @@ public JJSearchBox( Enabled = true; TriggerLength = 1; PlaceHolder = "Search..."; - NumberOfItems = 10; ScrollBar = true; AutoReloadFormFields = true; Name = "jjsearchbox1"; @@ -237,16 +236,16 @@ public JJSearchBox( #endregion - protected override Task BuildResultAsync() + protected override async ValueTask BuildResultAsync() { var fieldName = Request.QueryString["fieldName"]; if (ComponentContext is ComponentContext.SearchBox or ComponentContext.SearchBoxFilter && FieldName == fieldName) { - return GetItemsResult(); + return await GetItemsResult(); } - return GetRenderedComponentResult(); + return await GetRenderedComponentResult(); } internal async Task GetRenderedComponentResult() @@ -268,7 +267,7 @@ private async Task GetSearchBoxHtml() var selectedValue = await GetSelectedValueAsync(); - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); await div.AppendAsync(HtmlTag.Input, async input => { input.WithAttribute("id", $"{HtmlId}_text"); @@ -319,7 +318,7 @@ private string GetQueryString() var context = new RouteContext(ElementName, ParentElementName, componentContext); - var encryptedRoute = EncryptionService.EncryptRouteContext(context); + var encryptedRoute = EncryptionService.EncryptObject(context); url.Append($"routeContext={encryptedRoute}"); url.Append($"&fieldName={FieldName}"); @@ -328,10 +327,8 @@ private string GetQueryString() } /// - /// Recupera descrição com base no id + /// Retrieve description based on id /// - /// Id a ser pesquisado - /// Retorna descrição referente ao id public async Task GetDescriptionAsync(string searchId) { string? description = null; @@ -350,7 +347,7 @@ private string GetQueryString() _values ??= await DataItemService.GetValuesAsync(DataItem, dataQuery); } - var item = _values?.ToList().Find(x => x.Id.Equals(searchId)); + var item = _values?.FirstOrDefault(x => x.Id.Equals(searchId)); if (item != null) description = item.Description; @@ -368,10 +365,7 @@ private async Task> GetValuesAsync(string? searchText) { var args = new SearchBoxQueryEventArgs(searchText); OnSearchQuery.Invoke(this, args); - foreach (var value in args.Values) - { - list.Add(value); - } + list.AddRange(args.Values); } else { @@ -390,12 +384,12 @@ private async Task> GetSearchBoxItemsAsync() { var searchText = Request.Form[Name + "_text"]; var values = await GetValuesAsync(searchText); - return values.Select(v=>new DataItemResult() + return values.ConvertAll(v=>new DataItemResult { Id = v.Id, Description = v.Description, IconCssClass = DataItem.ShowIcon ? v.Icon.GetCssClass() : null, IconColor = DataItem.ShowIcon ? v.IconColor : null - }).ToList(); + }); } } \ No newline at end of file diff --git a/src/Core/UI/Components/Controls/SearchBox/SearchBoxFactory.cs b/src/Core/UI/Components/Controls/SearchBox/SearchBoxFactory.cs index 7f6871025..521f077c3 100644 --- a/src/Core/UI/Components/Controls/SearchBox/SearchBoxFactory.cs +++ b/src/Core/UI/Components/Controls/SearchBox/SearchBoxFactory.cs @@ -2,25 +2,20 @@ using System; using JJMasterData.Commons.Security.Cryptography.Abstractions; using JJMasterData.Core.DataDictionary.Models; -using JJMasterData.Core.DataDictionary.Repository.Abstractions; using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.Http.Abstractions; namespace JJMasterData.Core.UI.Components; -internal class SearchBoxFactory( +internal sealed class SearchBoxFactory( DataItemService dataItemService, IHttpRequest httpRequest, IEncryptionService encryptionService) : IControlFactory { - private DataItemService DataItemService { get; } = dataItemService; - private IHttpRequest HttpRequest { get; } = httpRequest; - private IEncryptionService EncryptionService { get; } = encryptionService; - public JJSearchBox Create() { - return new JJSearchBox(HttpRequest, EncryptionService, DataItemService); + return new JJSearchBox(httpRequest, encryptionService, dataItemService); } public JJSearchBox Create(FormElement formElement, FormElementField field, ControlContext controlContext) @@ -28,7 +23,7 @@ public JJSearchBox Create(FormElement formElement, FormElementField field, Contr if (field.DataItem == null) throw new ArgumentNullException(nameof(field.DataItem)); - var search = new JJSearchBox(HttpRequest, EncryptionService, DataItemService) + var search = new JJSearchBox(httpRequest, encryptionService, dataItemService) { DataItem = field.DataItem, ConnectionId = formElement.ConnectionId, diff --git a/src/Core/UI/Components/Controls/Slider/JJSlider.cs b/src/Core/UI/Components/Controls/Slider/JJSlider.cs index fda9c7344..298b6a88c 100644 --- a/src/Core/UI/Components/Controls/Slider/JJSlider.cs +++ b/src/Core/UI/Components/Controls/Slider/JJSlider.cs @@ -16,7 +16,7 @@ public class JJSlider(IFormValues formValues, IControlFactory textBox public bool ShowInput { get; set; } = true; public int NumberOfDecimalPlaces { get; set; } - protected override Task BuildResultAsync() + protected override ValueTask BuildResultAsync() { var html = new HtmlBuilder(HtmlTag.Div) .WithCssClass("row") @@ -50,12 +50,12 @@ protected override Task BuildResultAsync() var result = new RenderedComponentResult(html); - return Task.FromResult(result); + return new ValueTask(result); } private HtmlBuilder GetHtmlSlider() { - var slider = new Input() + var slider = new HtmlBuilder(HtmlTag.Input) .WithAttributes(Attributes) .WithAttribute("type", "range") .WithNameAndId(Name) diff --git a/src/Core/UI/Components/Controls/Slider/SliderFactory.cs b/src/Core/UI/Components/Controls/Slider/SliderFactory.cs index 319d8a5ac..3ba818610 100644 --- a/src/Core/UI/Components/Controls/Slider/SliderFactory.cs +++ b/src/Core/UI/Components/Controls/Slider/SliderFactory.cs @@ -3,16 +3,14 @@ namespace JJMasterData.Core.UI.Components; -internal class SliderFactory(IFormValues formValues, IControlFactory textBoxFactory) +internal sealed class SliderFactory( + IFormValues formValues, + IControlFactory textBoxFactory) : IControlFactory { - private IFormValues FormValues { get; } = formValues; - private IControlFactory TextBoxFactory { get; } = textBoxFactory; - - public JJSlider Create() { - return new JJSlider(FormValues,TextBoxFactory); + return new JJSlider(formValues,textBoxFactory); } public JJSlider Create(FormElement formElement, FormElementField field, ControlContext context) diff --git a/src/Core/UI/Components/Controls/TextArea/JJTextArea.cs b/src/Core/UI/Components/Controls/TextArea/JJTextArea.cs index 6cc708423..1a6975e3f 100644 --- a/src/Core/UI/Components/Controls/TextArea/JJTextArea.cs +++ b/src/Core/UI/Components/Controls/TextArea/JJTextArea.cs @@ -22,11 +22,11 @@ public JJTextArea(IFormValues formValues,IStringLocalizer s Rows = 5; } - protected override Task BuildResultAsync() + protected override ValueTask BuildResultAsync() { var html = GetHtmlBuilder(); - return Task.FromResult(new RenderedComponentResult(html)); + return new ValueTask(new RenderedComponentResult(html)); } public HtmlBuilder GetHtmlBuilder() @@ -50,7 +50,7 @@ public HtmlBuilder GetHtmlBuilder() { textArea.WithSingleAttribute("placeholder"); - return new Div() + return new HtmlBuilder(HtmlTag.Div) .WithCssClass("form-floating") .Append(textArea) .AppendLabel(label => diff --git a/src/Core/UI/Components/Controls/TextArea/TextAreaFactory.cs b/src/Core/UI/Components/Controls/TextArea/TextAreaFactory.cs index 9303aa08a..529472e04 100644 --- a/src/Core/UI/Components/Controls/TextArea/TextAreaFactory.cs +++ b/src/Core/UI/Components/Controls/TextArea/TextAreaFactory.cs @@ -6,15 +6,14 @@ namespace JJMasterData.Core.UI.Components; -internal class TextAreaFactory(IFormValues formValues, IStringLocalizer stringLocalizer) +internal sealed class TextAreaFactory( + IFormValues formValues, + IStringLocalizer stringLocalizer) : IControlFactory { - private IFormValues FormValues { get; } = formValues; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - public JJTextArea Create() { - return new JJTextArea(FormValues,StringLocalizer); + return new JJTextArea(formValues,stringLocalizer); } public JJTextArea Create(FormElement formElement, FormElementField field, ControlContext context) diff --git a/src/Core/UI/Components/Controls/TextBox/JJTextBox.cs b/src/Core/UI/Components/Controls/TextBox/JJTextBox.cs index b1d268dc8..5a1e8ab17 100644 --- a/src/Core/UI/Components/Controls/TextBox/JJTextBox.cs +++ b/src/Core/UI/Components/Controls/TextBox/JJTextBox.cs @@ -45,13 +45,13 @@ public JJTextBox(IFormValues formValues) : base(formValues) Enabled = true; } - protected override Task BuildResultAsync() + protected override ValueTask BuildResultAsync() { var html = GetHtmlBuilder(); var result = new RenderedComponentResult(html); - return Task.FromResult(result); + return new ValueTask(result); } public virtual HtmlBuilder GetHtmlBuilder() @@ -63,7 +63,7 @@ public virtual HtmlBuilder GetHtmlBuilder() CssClass += " jj-numeric"; } - var html = new Input() + var html = new HtmlBuilder(HtmlTag.Input) .WithNameAndId(Name) .WithAttributes(Attributes) .WithAttributeIfNotEmpty("placeholder", PlaceHolder) diff --git a/src/Core/UI/Components/Controls/TextBox/JJTextGroup.cs b/src/Core/UI/Components/Controls/TextBox/JJTextGroup.cs index 79a81f609..7bc3e3607 100644 --- a/src/Core/UI/Components/Controls/TextBox/JJTextGroup.cs +++ b/src/Core/UI/Components/Controls/TextBox/JJTextGroup.cs @@ -1,18 +1,19 @@ using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.UI.Html; namespace JJMasterData.Core.UI.Components; -public class JJTextGroup(IComponentFactory linkButtonGroupFactory, IFormValues formValues) +public sealed class JJTextGroup( + IComponentFactory linkButtonGroupFactory, + IFormValues formValues) : JJTextBox(formValues), IFloatingLabelControl { public string FloatingLabel { get; set; } public bool UseFloatingLabel { get; set; } - + /// /// Actions of input /// @@ -26,11 +27,11 @@ public class JJTextGroup(IComponentFactory linkButtonGroupFac public string GroupCssClass { get; set; } - protected override Task BuildResultAsync() + protected override ValueTask BuildResultAsync() { var inputGroup = GetHtmlBuilder(); - return Task.FromResult(new RenderedComponentResult(inputGroup)); + return new ValueTask(new RenderedComponentResult(inputGroup)); } public override HtmlBuilder GetHtmlBuilder() @@ -46,7 +47,7 @@ public override HtmlBuilder GetHtmlBuilder() } var input = base.GetHtmlBuilder(); - var hasAction = Actions.ToList().Exists(x => x.Visible); + var hasAction = Actions.Exists(x => x.Visible); var hasAddons = Addons != null; if (!hasAction && !hasAddons) @@ -54,7 +55,7 @@ public override HtmlBuilder GetHtmlBuilder() if (UseFloatingLabel) { input.WithSingleAttribute("placeholder"); - return new Div().WithCssClass("form-floating") + return new HtmlBuilder(HtmlTag.Div).WithCssClass("form-floating") .Append(input) .AppendLabel(label => { @@ -62,14 +63,14 @@ public override HtmlBuilder GetHtmlBuilder() label.WithAttribute("for", Name); }); } - + return input; } if (defaultAction is { Enabled: true }) { input.WithCssClass("default-option"); - input.WithOnChange( defaultAction.OnClientClick); + input.WithOnChange(defaultAction.OnClientClick); } var inputGroup = new HtmlBuilder(HtmlTag.Div) @@ -79,9 +80,6 @@ public override HtmlBuilder GetHtmlBuilder() if (hasAddons) inputGroup.Append(GetHtmlAddons()); - if (hasAction) - AddActionsAt(inputGroup); - if (UseFloatingLabel) { input.WithSingleAttribute("placeholder"); @@ -95,11 +93,15 @@ public override HtmlBuilder GetHtmlBuilder() label.WithAttribute("for", Name); }); }); - return inputGroup; } - - inputGroup.Append(input); - + else + { + inputGroup.Append(input); + } + + if (hasAction) + AddActionsAt(inputGroup); + return inputGroup; } @@ -122,8 +124,8 @@ private void AddActionsAt(HtmlBuilder inputGroup) btnGroup.Actions = Actions; if (UseFloatingLabel) - Actions.ForEach(a=>a.CssClass += "btn-floating-action"); - + Actions.ForEach(a => a.CssClass += "btn-floating-action"); + btnGroup.ShowAsButton = true; //Add builder Actions @@ -134,11 +136,11 @@ private void AddActionsAt(HtmlBuilder inputGroup) private HtmlBuilder GetHtmlAddons() { var html = new HtmlBuilder(HtmlTag.Span) - .WithCssClass(BootstrapHelper.InputGroupAddon) - .WithToolTip(Addons.Tooltip) - .AppendIf(Addons.Icon != null,()=> Addons.Icon.BuildHtml()) - .AppendTextIf(!string.IsNullOrEmpty(Addons.Text), Addons.Text); + .WithCssClass(BootstrapHelper.InputGroupAddon) + .WithToolTip(Addons.Tooltip) + .AppendIf(Addons.Icon != null, () => Addons.Icon.BuildHtml()) + .AppendTextIf(!string.IsNullOrEmpty(Addons.Text), Addons.Text); return html; } -} +} \ No newline at end of file diff --git a/src/Core/UI/Components/Controls/TextBox/TextBoxFactory.cs b/src/Core/UI/Components/Controls/TextBox/TextBoxFactory.cs index 8b265ec17..94964cba3 100644 --- a/src/Core/UI/Components/Controls/TextBox/TextBoxFactory.cs +++ b/src/Core/UI/Components/Controls/TextBox/TextBoxFactory.cs @@ -4,20 +4,17 @@ namespace JJMasterData.Core.UI.Components; -internal class TextBoxFactory(IFormValues formValues, IEncryptionService encryptionService) +internal sealed class TextBoxFactory(IFormValues formValues) : IControlFactory { - private IFormValues FormValues { get; } = formValues; - private IEncryptionService EncryptionService { get; } = encryptionService; - public JJTextBox Create() { - return new JJTextBox(FormValues); + return new JJTextBox(formValues); } public JJTextBox Create(FormElement formElement, FormElementField field, ControlContext context) { - return new JJTextBox(FormValues) + return new JJTextBox(formValues) { Name = field.Name, Text = context.Value?.ToString() diff --git a/src/Core/UI/Components/Controls/TextBox/TextGroupFactory.cs b/src/Core/UI/Components/Controls/TextBox/TextGroupFactory.cs index 1c021f6c3..50889345e 100644 --- a/src/Core/UI/Components/Controls/TextBox/TextGroupFactory.cs +++ b/src/Core/UI/Components/Controls/TextBox/TextGroupFactory.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using JJMasterData.Commons.Localization; +using JJMasterData.Commons.Util; using JJMasterData.Core.DataDictionary; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Models.Actions; @@ -17,14 +18,10 @@ public class TextGroupFactory( ActionButtonFactory actionButtonFactory) : IControlFactory { - private IFormValues FormValues { get; } = formValues; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private IComponentFactory LinkButtonGroupFactory { get; } = linkButtonGroupFactory; - private ActionButtonFactory ActionButtonFactory { get; } = actionButtonFactory; public JJTextGroup Create() { - return new JJTextGroup(LinkButtonGroupFactory,FormValues); + return new JJTextGroup(linkButtonGroupFactory,formValues); } public JJTextGroup Create(FormElementField field, object value) @@ -33,7 +30,7 @@ public JJTextGroup Create(FormElementField field, object value) if (field.Component == FormComponent.Currency) { - value = value?.ToString().Replace(RegionInfo.CurrentRegion.CurrencySymbol, string.Empty).Trim(); + value = value?.ToString()?.Replace(RegionInfo.CurrentRegion.CurrencySymbol, string.Empty).Trim(); } textGroup.Text = value?.ToString() ?? string.Empty; @@ -76,11 +73,10 @@ private void AddUserActions( FormElement = formElement, FormStateData = controlContext.FormStateData, FieldName = field.Name, - IsSubmit = action is ISubmittableAction { IsSubmit: true }, ParentComponentName = controlContext.ParentComponentName }; - var link = ActionButtonFactory.CreateFieldButton(action,actionContext); + var link = actionButtonFactory.CreateFieldButton(action,actionContext); textGroup.Actions.Add(link); } @@ -103,7 +99,7 @@ public JJTextGroup Create(FormElementField field) public JJTextGroup CreateTextDate() { - var textGroup = new JJTextGroup(LinkButtonGroupFactory,FormValues); + var textGroup = new JJTextGroup(linkButtonGroupFactory,formValues); SetDefaultAttrs(textGroup, FormComponent.Date); return textGroup; } @@ -196,8 +192,10 @@ private void SetDefaultAttrs(JJTextGroup textGroup, FormComponent component) textGroup.InputType = InputType.Text; textGroup.MaxLength = 5; textGroup.GroupCssClass = "flatpickr date jjform-hour"; - // textGroup.SetAttr("data-inputmask", - // $"'alias': 'datetime','inputFormat': '[{Format.TimeFormat.ToLower()}]', 'displayFormat': '[{Format.TimeFormat.ToLower()}]','placeholder':''"); + textGroup.SetAttr("data-inputmask-alias", "datetime"); + textGroup.SetAttr("data-inputmask-inputFormat", "HH:M"); + textGroup.SetAttr("data-inputmask-displayFormat","HH:M"); + textGroup.SetAttr("data-inputmask-placeholder", ""); textGroup.SetAttr("data-input", "date"); break; case FormComponent.Date: @@ -205,8 +203,10 @@ private void SetDefaultAttrs(JJTextGroup textGroup, FormComponent component) textGroup.Actions.Add(GetDateAction(component,textGroup.Enabled)); textGroup.InputType = InputType.Text; textGroup.MaxLength = 10; - // textGroup.SetAttr("data-inputmask", - // $"'alias': 'datetime','inputFormat': '[{Format.DateFormat.ToLower()}]','displayFormat': '[{Format.DateFormat.ToLower()}]', 'placeholder':''"); + textGroup.SetAttr("data-inputmask-alias", "datetime"); + textGroup.SetAttr("data-inputmask-inputFormat", Format.DateFormat); + textGroup.SetAttr("data-inputmask-displayFormat", Format.DateFormat); + textGroup.SetAttr("data-inputmask-placeholder", ""); textGroup.SetAttr("data-input", "date"); break; case FormComponent.DateTime: @@ -214,8 +214,10 @@ private void SetDefaultAttrs(JJTextGroup textGroup, FormComponent component) textGroup.Actions.Add(GetDateAction(component,textGroup.Enabled)); textGroup.InputType = InputType.Text; textGroup.MaxLength = 19; - // textGroup.SetAttr("data-inputmask", - // $"'alias': 'datetime','inputFormat': '[{Format.DateTimeFormat.ToLower()}]','displayFormat': '[{Format.DateTimeFormat.ToLower()}]', 'placeholder':''"); + textGroup.SetAttr("data-inputmask-alias", "datetime"); + textGroup.SetAttr("data-inputmask-inputFormat", $"{Format.DateFormat} HH:M"); + textGroup.SetAttr("data-inputmask-displayFormat", $"{Format.DateFormat} HH:M"); + textGroup.SetAttr("data-inputmask-placeholder", ""); textGroup.SetAttr("data-input", "date"); break; default: @@ -228,9 +230,9 @@ private void SetDefaultAttrs(JJTextGroup textGroup, FormComponent component) private JJLinkButton GetDateAction(FormComponent component, bool isEnabled) { - var btn = ActionButtonFactory.Create(); - btn.IconClass =component is FormComponent.Hour ? IconType.SolidClock.GetCssClass() : $"fa fa-{BootstrapHelper.DateIcon}"; - btn.Tooltip = component is FormComponent.Hour ? StringLocalizer["Clock"] : StringLocalizer["Calendar"]; + var btn = actionButtonFactory.Create(); + btn.IconClass =component is FormComponent.Hour ? IconType.SolidClock.GetCssClass() : "fa fa-calendar"; + btn.Tooltip = component is FormComponent.Hour ? stringLocalizer["Clock"] : stringLocalizer["Calendar"]; btn.Enabled = isEnabled; btn.SetAttr("data-toggle", "date"); btn.SetAttr("tabindex", "-1"); diff --git a/src/Core/UI/Components/Controls/TextFile/JJTextFile.cs b/src/Core/UI/Components/Controls/TextFile/JJTextFile.cs index 7e2a61292..80d530d64 100644 --- a/src/Core/UI/Components/Controls/TextFile/JJTextFile.cs +++ b/src/Core/UI/Components/Controls/TextFile/JJTextFile.cs @@ -13,7 +13,7 @@ namespace JJMasterData.Core.UI.Components; -public class JJTextFile(IHttpRequest request, +public sealed class JJTextFile(IHttpRequest request, IComponentFactory componentFactory, IStringLocalizer stringLocalizer, IEncryptionService encryptionService) @@ -109,7 +109,7 @@ public JJUploadView UploadView } } - protected override async Task BuildResultAsync() + protected override async ValueTask BuildResultAsync() { switch (RouteContext.ComponentContext) { @@ -179,7 +179,7 @@ private async Task GetRenderedComponentHtml() private HtmlBuilder GetHiddenInputHtml() { - var input = new Input(); + var input = new HtmlBuilder(HtmlTag.Input); input.WithAttribute("type", "hidden") .WithNameAndId(Name) .WithAttribute("value", GetFileName()); @@ -201,7 +201,7 @@ public void DeleteAll() } private bool HasPk() { - var pkFields = FormElement.Fields.ToList().FindAll(x => x.IsPk); + var pkFields = FormElement.Fields.FindAll(x => x.IsPk); if (pkFields.Count == 0) return false; diff --git a/src/Core/UI/Components/Controls/TextFile/TextFileFactory.cs b/src/Core/UI/Components/Controls/TextFile/TextFileFactory.cs index 9e9f8fd56..3ad97f626 100644 --- a/src/Core/UI/Components/Controls/TextFile/TextFileFactory.cs +++ b/src/Core/UI/Components/Controls/TextFile/TextFileFactory.cs @@ -7,22 +7,16 @@ namespace JJMasterData.Core.UI.Components; -internal class TextFileFactory(IHttpRequest request, +internal sealed class TextFileFactory( + IHttpRequest request, IComponentFactory componentFactory, IEncryptionService encryptionService, IStringLocalizer stringLocalizer) : IControlFactory { - private IHttpRequest Request { get; } = request; - private IComponentFactory ComponentFactory { get; } = componentFactory; - - private IEncryptionService EncryptionService { get; } = encryptionService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - - public JJTextFile Create() { - return new JJTextFile(Request,ComponentFactory, StringLocalizer, EncryptionService); + return new JJTextFile(request,componentFactory, stringLocalizer, encryptionService); } public JJTextFile Create(FormElement formElement, FormElementField field, ControlContext context) diff --git a/src/Core/UI/Components/Controls/TextFile/TextFileScripts.cs b/src/Core/UI/Components/Controls/TextFile/TextFileScripts.cs index ccd84e78d..58c14bd50 100644 --- a/src/Core/UI/Components/Controls/TextFile/TextFileScripts.cs +++ b/src/Core/UI/Components/Controls/TextFile/TextFileScripts.cs @@ -6,7 +6,7 @@ namespace JJMasterData.Core.UI.Components; -internal class TextFileScripts(JJTextFile textFile) +internal sealed class TextFileScripts(JJTextFile textFile) { public string GetShowScript() { @@ -16,14 +16,14 @@ public string GetShowScript() var routeContext = RouteContext.FromFormElement(textFile.FormElement, ComponentContext.TextFileUploadView); - return $"TextFileHelper.showUploadView('{textFile.FieldName}','{title}','{textFile.EncryptionService.EncryptRouteContext(routeContext)}');"; + return $"TextFileHelper.showUploadView('{textFile.FieldName}','{title}','{textFile.EncryptionService.EncryptObject(routeContext)}');"; } public string GetRefreshScript() { var routeContext = RouteContext.FromFormElement(textFile.FormElement, ComponentContext.TextFileUploadView); - return $"TextFileHelper.refresh('{textFile.FieldName}','{textFile.EncryptionService.EncryptRouteContext(routeContext)}')"; + return $"TextFileHelper.refresh('{textFile.FieldName}','{textFile.EncryptionService.EncryptObject(routeContext)}')"; } public string GetRefreshInputsScript() diff --git a/src/Core/UI/Components/Controls/TextRange/JJTextRange.cs b/src/Core/UI/Components/Controls/TextRange/JJTextRange.cs index c72d5cdb2..9f9b45f5b 100644 --- a/src/Core/UI/Components/Controls/TextRange/JJTextRange.cs +++ b/src/Core/UI/Components/Controls/TextRange/JJTextRange.cs @@ -23,9 +23,9 @@ public class JJTextRange(IFormValues formValues, public bool IsVerticalLayout { get; set; } - protected override async Task BuildResultAsync() + protected override async ValueTask BuildResultAsync() { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("row"); div.WithCssClass(CssClass); div.WithAttributes(Attributes); @@ -139,7 +139,7 @@ private string GetTodayScript(DateTime now) private static HtmlBuilder GetListItem(string label, string script) { - return new Li() + return new HtmlBuilder(HtmlTag.Li) .WithOnClick( script) .Append(HtmlTag.A, a => { diff --git a/src/Core/UI/Components/Controls/TextRange/TextRangeFactory.cs b/src/Core/UI/Components/Controls/TextRange/TextRangeFactory.cs index 4c7841f5c..bb70f24a7 100644 --- a/src/Core/UI/Components/Controls/TextRange/TextRangeFactory.cs +++ b/src/Core/UI/Components/Controls/TextRange/TextRangeFactory.cs @@ -5,7 +5,7 @@ namespace JJMasterData.Core.UI.Components.TextRange; -internal class TextRangeFactory( +internal sealed class TextRangeFactory( IHttpContext httpContext, IStringLocalizer stringLocalizer, TextGroupFactory textGroupFactory diff --git a/src/Core/UI/Components/DataPanel/DataPanelControl.cs b/src/Core/UI/Components/DataPanel/DataPanelControl.cs index 18564668e..eceafbf11 100644 --- a/src/Core/UI/Components/DataPanel/DataPanelControl.cs +++ b/src/Core/UI/Components/DataPanel/DataPanelControl.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using System.Web; using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Localization; using JJMasterData.Commons.Security.Cryptography.Abstractions; @@ -20,7 +19,7 @@ namespace JJMasterData.Core.UI.Components; /// /// Render components fields in a div /// -internal class DataPanelControl +internal sealed class DataPanelControl { private DataPanelScripts? _panelScripts; @@ -51,7 +50,7 @@ internal class DataPanelControl private bool IsGridViewFilter { get; } - private FieldsService FieldsService { get; } + private FieldFormattingService FieldFormattingService { get; } private IStringLocalizer StringLocalizer { get; } internal ExpressionsService ExpressionsService { get; } internal IEncryptionService EncryptionService { get; } @@ -65,7 +64,7 @@ public DataPanelControl(JJDataPanel dataPanel) ComponentFactory = dataPanel.ComponentFactory; Errors = dataPanel.Errors; EncryptionService = dataPanel.EncryptionService; - FieldsService = dataPanel.FieldsService; + FieldFormattingService = dataPanel.FieldFormattingService; Name = dataPanel.Name; ExpressionsService = dataPanel.ExpressionsService; FieldNamePrefix = dataPanel.FieldNamePrefix; @@ -87,7 +86,7 @@ public DataPanelControl(JJGridView gridView, Dictionary values) Name = gridView.Name; ComponentFactory = gridView.ComponentFactory; ExpressionsService = gridView.ExpressionsService; - FieldsService = gridView.FieldsService; + FieldFormattingService = gridView.FieldFormattingService; StringLocalizer = gridView.StringLocalizer; FormStateData = new FormStateData(values, gridView.UserValues, PageState.Filter); IsGridViewFilter = true; @@ -111,29 +110,29 @@ private async Task GetHtmlFormVertical(List field if (cols >= 1) colClass = $" col-sm-{12 / cols}"; - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); int lineGroup = int.MinValue; HtmlBuilder? row = null; var formData = new FormStateData(Values, UserValues, PageState); foreach (var field in fields) { - bool visible = ExpressionsService.GetBoolValue(field.VisibleExpression, formData); + var visible = ExpressionsService.GetBoolValue(field.VisibleExpression, formData); if (!visible) continue; object? value = null; - if (Values != null && Values.ContainsKey(field.Name)) - value = FieldsService.FormatValue(field, Values[field.Name]); + if (Values != null && Values.TryGetValue(field.Name, out value)) + value = FieldFormattingService.FormatValue(field, value); if (lineGroup != field.LineGroup) { lineGroup = field.LineGroup; - row = new Div().WithCssClass("row"); + row = new HtmlBuilder(HtmlTag.Div).WithCssClass("row"); html.Append(row); } - var formGroup = new Div() + var formGroup = new HtmlBuilder(HtmlTag.Div) .WithCssClass(BootstrapHelper.FormGroup); row?.Append(formGroup); @@ -205,7 +204,7 @@ private async Task GetHtmlFormHorizontal(List fie //Value object? value = null; if (Values != null && Values.TryGetValue(field.Name, out var nonFormattedValue)) - value = FieldsService.FormatValue(field, nonFormattedValue); + value = FieldFormattingService.FormatValue(field, nonFormattedValue); var isRange = IsRange(field, PageState); var label = CreateLabel(field, isRange); @@ -311,10 +310,10 @@ private JJLabel CreateLabel(FormElementField field, bool isRange) } - private async Task GetStaticField(FormElementField field) + private async ValueTask GetStaticField(FormElementField field) { var fieldSelector = new FormElementFieldSelector(FormElement, field.Name); - var staticValue = await FieldsService.FormatGridValueAsync(fieldSelector, FormStateData); + var staticValue = await FieldFormattingService.FormatGridValueAsync(fieldSelector, FormStateData); var html = new HtmlBuilder(HtmlTag.P) .WithCssClass("form-control-static") .AppendText(staticValue); @@ -322,7 +321,7 @@ private async Task GetStaticField(FormElementField field) return html; } - private Task GetControlFieldHtml(FormElementField field, object? value) + private ValueTask GetControlFieldHtml(FormElementField field, object? value) { var formStateData = new FormStateData(Values, UserValues, PageState); var control = ComponentFactory.Controls.Create(FormElement, field, formStateData, ParentComponentName, value); diff --git a/src/Core/UI/Components/DataPanel/DataPanelExpressionScripts.cs b/src/Core/UI/Components/DataPanel/DataPanelExpressionScripts.cs index 9f55de93f..c35534102 100644 --- a/src/Core/UI/Components/DataPanel/DataPanelExpressionScripts.cs +++ b/src/Core/UI/Components/DataPanel/DataPanelExpressionScripts.cs @@ -3,12 +3,11 @@ using System.Text; using JJMasterData.Commons.Util; using JJMasterData.Core.DataDictionary.Models; -using JJMasterData.Core.DataManager.Expressions; using JJMasterData.Core.DataManager.Models; namespace JJMasterData.Core.UI.Components; -internal class DataPanelExpressionScripts(JJDataPanel dataPanel) +internal sealed class DataPanelExpressionScripts(JJDataPanel dataPanel) { private JJDataPanel DataPanel { get; } = dataPanel; diff --git a/src/Core/UI/Components/DataPanel/DataPanelFactory.cs b/src/Core/UI/Components/DataPanel/DataPanelFactory.cs index e3a5699b0..5adaceef3 100644 --- a/src/Core/UI/Components/DataPanel/DataPanelFactory.cs +++ b/src/Core/UI/Components/DataPanel/DataPanelFactory.cs @@ -15,7 +15,8 @@ internal sealed class DataPanelFactory(IEntityRepository entityRepository, IDataDictionaryRepository dataDictionaryRepository, IHttpContext httpContext, IEncryptionService encryptionService, - FieldsService fieldsService, + FieldFormattingService fieldFormattingService, + FieldValidationService fieldValidationService, FormValuesService formValuesService, ExpressionsService expressionsService, IStringLocalizer stringLocalizer, @@ -23,35 +24,25 @@ internal sealed class DataPanelFactory(IEntityRepository entityRepository, UrlRedirectService urlRedirectService) : IFormElementComponentFactory { - private IEntityRepository EntityRepository { get; } = entityRepository; - private IDataDictionaryRepository DataDictionaryRepository { get; } = dataDictionaryRepository; - private IHttpContext HttpContext { get; } = httpContext; - private IEncryptionService EncryptionService { get; } = encryptionService; - private FieldsService FieldsService { get; } = fieldsService; - private FormValuesService FormValuesService { get; } = formValuesService; - private ExpressionsService ExpressionsService { get; } = expressionsService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private IComponentFactory ComponentFactory { get; } = componentFactory; - private UrlRedirectService UrlRedirectService { get; } = urlRedirectService; - public JJDataPanel Create(FormElement formElement) { return new JJDataPanel( formElement, - EntityRepository, - HttpContext, - EncryptionService, - FieldsService, - FormValuesService, - ExpressionsService, - UrlRedirectService, - StringLocalizer, - ComponentFactory); + entityRepository, + httpContext, + encryptionService, + fieldFormattingService, + fieldValidationService, + formValuesService, + expressionsService, + urlRedirectService, + stringLocalizer, + componentFactory); } - public async Task CreateAsync(string elementName) + public async ValueTask CreateAsync(string elementName) { - var formElement = await DataDictionaryRepository.GetFormElementAsync(elementName); + var formElement = await dataDictionaryRepository.GetFormElementAsync(elementName); return Create(formElement); } } \ No newline at end of file diff --git a/src/Core/UI/Components/DataPanel/DataPanelLayout.cs b/src/Core/UI/Components/DataPanel/DataPanelLayout.cs index 05f51b2a5..9ad63a2d4 100644 --- a/src/Core/UI/Components/DataPanel/DataPanelLayout.cs +++ b/src/Core/UI/Components/DataPanel/DataPanelLayout.cs @@ -13,7 +13,7 @@ namespace JJMasterData.Core.UI.Components; /// /// Render panels with fields /// -internal class DataPanelLayout(JJDataPanel dataPanel) +internal sealed class DataPanelLayout(JJDataPanel dataPanel) { private string Name { get; } = dataPanel.Name; @@ -29,9 +29,11 @@ internal class DataPanelLayout(JJDataPanel dataPanel) private ExpressionsService ExpressionsService { get; } = dataPanel.ExpressionsService; public async Task> GetHtmlPanelList() { - List panels = []; + List panels = + [ + await GetTabPanelsHtml() + ]; - panels.Add(await GetTabPanelsHtml()); panels.AddRange(await GetNonTabPanels()); panels.AddRange(await GetFieldsWithoutPanel()); @@ -66,8 +68,7 @@ private async Task> GetNonTabPanels() private async Task> GetFieldsWithoutPanel() { List htmlList = []; - bool dontContainsVisibleFields = !FormElement.Fields.ToList() - .Exists(x => x.PanelId == 0 && ExpressionsService.GetBoolValue(x.VisibleExpression, DataPanelControl.FormStateData)); + bool dontContainsVisibleFields = !FormElement.Fields.Exists(x => x.PanelId == 0 && ExpressionsService.GetBoolValue(x.VisibleExpression, DataPanelControl.FormStateData)); if (dontContainsVisibleFields) return htmlList; diff --git a/src/Core/UI/Components/DataPanel/DataPanelScripts.cs b/src/Core/UI/Components/DataPanel/DataPanelScripts.cs index 7367fdc3d..348a7d44b 100644 --- a/src/Core/UI/Components/DataPanel/DataPanelScripts.cs +++ b/src/Core/UI/Components/DataPanel/DataPanelScripts.cs @@ -5,7 +5,7 @@ namespace JJMasterData.Core.UI.Components; -internal class DataPanelScripts(DataPanelControl dataPanelControl) +internal sealed class DataPanelScripts(DataPanelControl dataPanelControl) { private IEncryptionService EncryptionService => dataPanelControl.EncryptionService; @@ -26,7 +26,7 @@ private string GetReloadPanelScriptInternal( { var componentName = dataPanelControl.Name; var routeContext = - EncryptionService.EncryptRouteContext(RouteContext.FromFormElement(dataPanelControl.FormElement, + EncryptionService.EncryptObject(RouteContext.FromFormElement(dataPanelControl.FormElement, ComponentContext.DataPanelReload)); diff --git a/src/Core/UI/Components/DataPanel/JJDataPanel.cs b/src/Core/UI/Components/DataPanel/JJDataPanel.cs index d1ec96e46..59501efef 100644 --- a/src/Core/UI/Components/DataPanel/JJDataPanel.cs +++ b/src/Core/UI/Components/DataPanel/JJDataPanel.cs @@ -36,6 +36,8 @@ public class JJDataPanel : AsyncComponent private PageState? _pageState; private bool _isAtModal; private FormUI _formUI; + private Dictionary _secretValues; + #endregion #region "Properties" @@ -61,7 +63,7 @@ public PageState PageState { get { - if (ContainsPanelState() && _pageState is null) + if (_pageState is null && ContainsPanelState()) _pageState = (PageState)int.Parse(CurrentContext.Request.Form[$"data-panel-state-{Name}"]); return _pageState ?? PageState.View; @@ -75,7 +77,7 @@ public PageState PageState internal bool ContainsPanelState() => CurrentContext.Request.Form[$"data-panel-state-{Name}"] != null; - internal bool HasCustomPanelState { get; set; } + internal bool HasCustomPanelState { get; private set; } /// /// Fields with error. @@ -89,6 +91,22 @@ public PageState PageState /// public Dictionary Values { get; set; } + + /// + /// Values not intended to be edited at the client. They are encrypted using . + /// + [CanBeNull] + public Dictionary SecretValues + { + get + { + if (_secretValues == null && CurrentContext.Request.Form.TryGetValue($"data-panel-secret-values-{Name}", out var secretValues)) + _secretValues = EncryptionService.DecryptDictionary(secretValues); + return _secretValues; + } + set => _secretValues = value; + } + /// /// When reloading the panel, keep the values entered in the form /// (Default=True) @@ -103,8 +121,8 @@ public PageState PageState /// internal bool RenderPanelGroup { get; set; } - private bool AppendPkValues { get; set; } = true; - + private static bool AppendPkValues => true; + public string FieldNamePrefix { get; set; } private RouteContext RouteContext @@ -138,7 +156,8 @@ public bool IsAtModal public IEntityRepository EntityRepository { get; } internal IHttpContext CurrentContext { get; } internal IEncryptionService EncryptionService { get; } - internal FieldsService FieldsService { get; } + internal FieldValidationService FieldValidationService { get; } + internal FieldFormattingService FieldFormattingService { get; } internal FormValuesService FormValuesService { get; } internal ExpressionsService ExpressionsService { get; } private UrlRedirectService UrlRedirectService { get; } @@ -153,7 +172,8 @@ public JJDataPanel( IEntityRepository entityRepository, IHttpContext currentContext, IEncryptionService encryptionService, - FieldsService fieldsService, + FieldFormattingService fieldFormattingService, + FieldValidationService fieldValidationService, FormValuesService formValuesService, ExpressionsService expressionsService, UrlRedirectService urlRedirectService, @@ -164,7 +184,8 @@ IComponentFactory componentFactory EntityRepository = entityRepository; CurrentContext = currentContext; EncryptionService = encryptionService; - FieldsService = fieldsService; + FieldFormattingService = fieldFormattingService; + FieldValidationService = fieldValidationService; FormValuesService = formValuesService; ExpressionsService = expressionsService; UrlRedirectService = urlRedirectService; @@ -180,15 +201,16 @@ public JJDataPanel( IEntityRepository entityRepository, IHttpContext currentContext, IEncryptionService encryptionService, - FieldsService fieldsService, + FieldFormattingService fieldFormattingService, + FieldValidationService fieldValidationService, FormValuesService formValuesService, ExpressionsService expressionsService, UrlRedirectService urlRedirectService, IStringLocalizer stringLocalizer, IComponentFactory componentFactory - ) : this(entityRepository, currentContext, encryptionService, fieldsService, formValuesService, expressionsService, urlRedirectService,stringLocalizer,componentFactory) + ) : this(entityRepository, currentContext, encryptionService, fieldFormattingService, fieldValidationService, formValuesService, expressionsService, urlRedirectService,stringLocalizer,componentFactory) { - Name = $"{ComponentNameGenerator.Create(formElement.Name)}-data-panel"; + Name = $"{formElement.Name.ToLowerInvariant()}-data-panel"; FormElement = formElement; RenderPanelGroup = formElement.Panels.Count > 0; } @@ -198,7 +220,7 @@ IComponentFactory componentFactory protected override async Task BuildResultAsync() { if (!RouteContext.CanRender(FormElement)) - return new EmptyComponentResult(); + return EmptyComponentResult.Value; Values = await GetFormValuesAsync(); @@ -235,14 +257,14 @@ protected override async Task BuildResultAsync() } } - private async Task GetFieldResultAsync() where TControl : ControlBase + private async ValueTask GetFieldResultAsync() where TControl : ControlBase { var fieldName = CurrentContext.Request.QueryString["fieldName"]; var formStateData = new FormStateData(await GetFormValuesAsync(), UserValues, PageState); var controlContext = new ControlContext(formStateData, Name); if (!FormElement.Fields.TryGetField(fieldName, out var field)) - return new EmptyComponentResult(); + return EmptyComponentResult.Value; var control = ComponentFactory.Controls.Create(FormElement, field, controlContext); control.Name = FieldNamePrefix + fieldName; @@ -272,17 +294,19 @@ internal async Task GetPanelHtmlBuilderAsync() private void AppendHiddenInputs(HtmlBuilder html) { if (DataHelper.ContainsPkValues(FormElement, Values) && AppendPkValues) - { html.AppendHiddenInput($"data-panel-pk-values-{FormElement.Name}", GetPkHiddenInput()); - } + html.AppendHiddenInput($"data-panel-state-{Name}", ((int)PageState).ToString()); html.AppendHiddenInput($"data-panel-is-at-modal-{Name}", IsAtModal.ToString()); + + if (SecretValues?.Count > 0) + html.AppendHiddenInput($"data-panel-secret-values-{Name}", EncryptionService.EncryptObject(SecretValues)); } private string GetPkHiddenInput() { - string pkval = DataHelper.ParsePkValues(FormElement, Values, '|'); - return EncryptionService.EncryptStringWithUrlEscape(pkval); + var pkValues = DataHelper.ParsePkValues(FormElement, Values, '|'); + return EncryptionService.EncryptStringWithUrlEscape(pkValues); } private string GetHtmlFormScript() @@ -318,6 +342,12 @@ public async Task> GetFormValuesAsync() { var formStateData = new FormStateData(Values, UserValues, PageState); var mergedValues = await FormValuesService.GetFormValuesWithMergedValuesAsync(FormElement, formStateData, AutoReloadFormFields, FieldNamePrefix); + + if (SecretValues?.Count > 0) + { + DataHelper.RemoveNullValues(SecretValues); + DataHelper.CopyIntoDictionary(mergedValues, SecretValues, true); + } DataHelper.CopyIntoDictionary(Values, mergedValues, true); DataHelper.RemoveNullValues(Values); @@ -336,7 +366,7 @@ public async Task LoadValuesFromPkAsync(Dictionary pks) Values = await EntityRepository.GetFieldsAsync(FormElement, pks); } - public async Task LoadValuesFromPkAsync(params object[] pks) + public Task LoadValuesFromPkAsync(params object[] pks) { var primaryKeys = FormElement.GetPrimaryKeys(); if (primaryKeys.Count == 0) @@ -352,26 +382,14 @@ public async Task LoadValuesFromPkAsync(params object[] pks) filter.Add(field.Name, pks[index]); } - await LoadValuesFromPkAsync(filter); - } - - - /// - /// Validate form fields and return a list with errors - /// - /// - /// Key = Field Name - /// Valor = Error message - /// - public Dictionary ValidateFields(Dictionary values, PageState pageState) - { - return ValidateFields(values, pageState, true); + return LoadValuesFromPkAsync(filter); } + /// public Dictionary ValidateFields(Dictionary values) { - return ValidateFields(values, PageState, true); + return ValidateFields(values, PageState); } /// @@ -381,9 +399,9 @@ public Dictionary ValidateFields(Dictionary valu /// Key = Field Name /// Valor = Error message /// - public Dictionary ValidateFields(Dictionary values, PageState pageState, bool enableErrorLink) + public Dictionary ValidateFields(Dictionary values, PageState pageState, bool enableErrorLink = true) { - return FieldsService.ValidateFields(FormElement, values, pageState, enableErrorLink); + return FieldValidationService.ValidateFields(FormElement, values, pageState, enableErrorLink); } internal Task GetUrlRedirectResult(ActionMap actionMap) diff --git a/src/Core/UI/Components/EmptyComponentResult.cs b/src/Core/UI/Components/EmptyComponentResult.cs index 0ffabd6bc..fadafd4f9 100644 --- a/src/Core/UI/Components/EmptyComponentResult.cs +++ b/src/Core/UI/Components/EmptyComponentResult.cs @@ -1,6 +1,10 @@ namespace JJMasterData.Core.UI.Components; -public class EmptyComponentResult : ComponentResult +public sealed class EmptyComponentResult : ComponentResult { + public static readonly EmptyComponentResult Value = new(); + private EmptyComponentResult() + { + } public override string Content => string.Empty; } \ No newline at end of file diff --git a/src/Core/UI/Components/Exportation/DataExportationFactory.cs b/src/Core/UI/Components/Exportation/DataExportationFactory.cs index 1a82073cd..bc60362eb 100644 --- a/src/Core/UI/Components/Exportation/DataExportationFactory.cs +++ b/src/Core/UI/Components/Exportation/DataExportationFactory.cs @@ -7,7 +7,6 @@ using JJMasterData.Core.DataDictionary.Repository.Abstractions; using JJMasterData.Core.DataManager.Exportation; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.Http.Abstractions; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; @@ -18,7 +17,6 @@ namespace JJMasterData.Core.UI.Components; internal class DataExportationFactory( IDataDictionaryRepository dataDictionaryRepository, ExpressionsService expressionsService, - FieldsService fieldsService, IOptionsSnapshot options, IBackgroundTaskManager backgroundTaskManager, IHttpContext httpContext, @@ -29,7 +27,7 @@ internal class DataExportationFactory( DataExportationWriterFactory dataExportationWriterFactory ) : IFormElementComponentFactory { - public async Task CreateAsync(string elementName) + public async ValueTask CreateAsync(string elementName) { var formElement = await dataDictionaryRepository.GetFormElementAsync(elementName); return Create(formElement); @@ -40,7 +38,6 @@ public JJDataExportation Create(FormElement formElement) return new JJDataExportation( formElement, expressionsService, - fieldsService, options, backgroundTaskManager, stringLocalizer, diff --git a/src/Core/UI/Components/Exportation/DataExportationLog.cs b/src/Core/UI/Components/Exportation/DataExportationLog.cs index 4ff94bb2e..4446187a9 100644 --- a/src/Core/UI/Components/Exportation/DataExportationLog.cs +++ b/src/Core/UI/Components/Exportation/DataExportationLog.cs @@ -11,7 +11,7 @@ internal class DataExportationLog(JJDataExportation dataExportation) internal HtmlBuilder GetLoadingHtml() { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("text-center"); diff --git a/src/Core/UI/Components/Exportation/DataExportationScripts.cs b/src/Core/UI/Components/Exportation/DataExportationScripts.cs index 3d921b511..69513f12b 100644 --- a/src/Core/UI/Components/Exportation/DataExportationScripts.cs +++ b/src/Core/UI/Components/Exportation/DataExportationScripts.cs @@ -21,7 +21,7 @@ private string EncryptedRouteContext get { var routeContext = RouteContext.FromFormElement(FormElement, ComponentContext.DataExportation); - var encryptedRouteContext = EncryptionService.EncryptRouteContext(routeContext); + var encryptedRouteContext = EncryptionService.EncryptObject(routeContext); return encryptedRouteContext; } } diff --git a/src/Core/UI/Components/Exportation/DataExportationSettings.cs b/src/Core/UI/Components/Exportation/DataExportationSettings.cs index 15379eed9..48ff9b746 100644 --- a/src/Core/UI/Components/Exportation/DataExportationSettings.cs +++ b/src/Core/UI/Components/Exportation/DataExportationSettings.cs @@ -12,14 +12,14 @@ namespace JJMasterData.Core.UI.Components; -internal class DataExportationSettings(JJDataExportation dataExportation) +internal sealed class DataExportationSettings(JJDataExportation dataExportation) { private JJDataExportation DataExportation { get; } = dataExportation; private IStringLocalizer StringLocalizer { get; } = dataExportation.StringLocalizer; internal HtmlBuilder GetHtmlBuilder() { - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); var folderPath = DataExportationHelper.GetFolderPath(DataExportation); @@ -59,7 +59,7 @@ internal HtmlBuilder GetHtmlBuilder() private HtmlBuilder GetFormHtmlElement(string exportationFolderPath) { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("row"); div.Append(GetFileExtensionField()); @@ -239,7 +239,7 @@ private HtmlBuilder GetDelimiterField() private HtmlBuilder GetFirstLineField() { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("col-sm-12"); div.WithCssClass(BootstrapHelper.FormGroup); var exportFirstLineCheckbox = DataExportation.ComponentFactory.Controls.CheckBox.Create(); @@ -297,7 +297,7 @@ private HtmlBuilder GetLastFilesHtml(List files) if (files == null || files.Count == 0) return new HtmlBuilder(StringLocalizer["No recently generated files."]); - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); foreach (var file in files) { if (FileIO.IsFileLocked(file)) @@ -306,7 +306,7 @@ private HtmlBuilder GetLastFilesHtml(List files) var icon = JJDataExportation.GetFileIcon(file.Extension); string url = DataExportation.GetDownloadUrl(file.FullName); - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("mb-1"); div.AppendComponent(icon); div.AppendText(" "); diff --git a/src/Core/UI/Components/Exportation/JJDataExportation.cs b/src/Core/UI/Components/Exportation/JJDataExportation.cs index 041c884df..0a94a0dd2 100644 --- a/src/Core/UI/Components/Exportation/JJDataExportation.cs +++ b/src/Core/UI/Components/Exportation/JJDataExportation.cs @@ -16,7 +16,6 @@ using JJMasterData.Core.DataManager.Exportation.Abstractions; using JJMasterData.Core.DataManager.Exportation.Configuration; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.UI.Events.Args; using JJMasterData.Core.UI.Html; @@ -67,7 +66,6 @@ public ExportOptions ExportOptions internal JJDataExportation( FormElement formElement, ExpressionsService expressionsService, - FieldsService fieldsService, IOptionsSnapshot masterDataOptions, IBackgroundTaskManager backgroundTaskManager, IStringLocalizer stringLocalizer, @@ -76,7 +74,7 @@ internal JJDataExportation( IHttpContext currentContext, IEncryptionService encryptionService, DataExportationWriterFactory dataExportationWriterFactory) : - base(currentContext, expressionsService, fieldsService, backgroundTaskManager, loggerFactory.CreateLogger(),encryptionService,stringLocalizer) + base(currentContext, expressionsService, backgroundTaskManager, loggerFactory.CreateLogger(),encryptionService,stringLocalizer) { DataExportationWriterFactory = dataExportationWriterFactory; ComponentFactory = componentFactory; @@ -119,7 +117,7 @@ private string GetFinishedMessageHtml(DataExportationReporter reporter) if (!reporter.HasError) { string url = GetDownloadUrl(reporter.FilePath); - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); if (reporter.HasError) { diff --git a/src/Core/UI/Components/FileComponentResult.cs b/src/Core/UI/Components/FileComponentResult.cs index a90a8624a..96a074418 100644 --- a/src/Core/UI/Components/FileComponentResult.cs +++ b/src/Core/UI/Components/FileComponentResult.cs @@ -8,7 +8,7 @@ namespace JJMasterData.Core.UI.Components; -public class FileComponentResult(string filePath) : ComponentResult +public sealed class FileComponentResult(string filePath) : ComponentResult #if NET ,IActionResult #endif diff --git a/src/Core/UI/Components/FormView/FormViewFactory.cs b/src/Core/UI/Components/FormView/FormViewFactory.cs index feb4ecae3..dd846782b 100644 --- a/src/Core/UI/Components/FormView/FormViewFactory.cs +++ b/src/Core/UI/Components/FormView/FormViewFactory.cs @@ -12,13 +12,14 @@ using JJMasterData.Core.Events.Abstractions; using JJMasterData.Core.Events.Args; using JJMasterData.Core.Http.Abstractions; +using JJMasterData.Core.Tasks; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace JJMasterData.Core.UI.Components; -internal class FormViewFactory( +internal sealed class FormViewFactory( IHttpContext currentContext, IEntityRepository entityRepository, IDataDictionaryRepository dataDictionaryRepository, @@ -27,6 +28,7 @@ internal class FormViewFactory( FormValuesService formValuesService, FieldValuesService fieldValuesService, ExpressionsService expressionsService, + HtmlTemplateService htmlTemplateService, IEnumerable pluginHandlers, IStringLocalizer stringLocalizer, IOptionsSnapshot options, @@ -47,6 +49,7 @@ public JJFormView Create(FormElement formElement) formValuesService, fieldValuesService, expressionsService, + htmlTemplateService, pluginHandlers, options, stringLocalizer, @@ -56,7 +59,7 @@ public JJFormView Create(FormElement formElement) return formView; } - public async Task CreateAsync(string elementName) + public async ValueTask CreateAsync(string elementName) { var formElement = await dataDictionaryRepository.GetFormElementAsync(elementName); var formView = Create(formElement); @@ -64,7 +67,7 @@ public async Task CreateAsync(string elementName) return formView; } - private Task SetFormEventHandlerAsync(JJFormView formView, FormElement formElement) + private ValueTask SetFormEventHandlerAsync(JJFormView formView, FormElement formElement) { var formEventHandler = formEventHandlerResolver.GetFormEventHandler(formElement.Name); formView.AddFormEventHandler(formEventHandler); @@ -73,7 +76,6 @@ private Task SetFormEventHandlerAsync(JJFormView formView, FormElement formEleme return formEventHandler.OnFormElementLoadAsync(formView, new FormElementLoadEventArgs(formElement))!; } - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; } - } \ No newline at end of file diff --git a/src/Core/UI/Components/FormView/FormViewRelationshipLayout.cs b/src/Core/UI/Components/FormView/FormViewRelationshipLayout.cs index d863a856f..216727a32 100644 --- a/src/Core/UI/Components/FormView/FormViewRelationshipLayout.cs +++ b/src/Core/UI/Components/FormView/FormViewRelationshipLayout.cs @@ -11,11 +11,11 @@ namespace JJMasterData.Core.UI.Components; -internal class FormViewRelationshipLayout(JJFormView parentFormView, List relationships) +internal sealed class FormViewRelationshipLayout(JJFormView parentFormView, List relationships) { public async Task GetRelationshipsResult() { - var relationshipsDiv = new Div(); + var relationshipsDiv = new HtmlBuilder(HtmlTag.Div); if (relationships.Any(r => r.Panel.Layout is PanelLayout.Tab)) { @@ -123,7 +123,7 @@ private string GetExpressionValue(string? expression) return panel.GetHtmlBuilder(); case PanelLayout.NoDecoration: var title = GetExpressionValue(relationship.Panel.Title); - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass(relationship.Panel.CssClass); if (title is not null) { @@ -147,8 +147,6 @@ private async Task GetRelationshipResult(FormElementRelationshi { var parentPanel = parentFormView.DataPanel; - var formContext = new FormContext(parentPanel.Values, parentPanel.Errors, parentPanel.PageState); - if (relationship.IsParent) return new RenderedComponentResult(await parentFormView.GetParentPanelHtmlAtRelationship(relationship)); @@ -159,9 +157,9 @@ await parentFormView.ComponentFactory.FormView.CreateAsync(relationship.ElementR var filter = new Dictionary(); foreach (var col in relationship.ElementRelationship.Columns.Where(col => - formContext.Values.ContainsKey(col.PkColumn))) + parentPanel.Values.ContainsKey(col.PkColumn))) { - var value = formContext.Values[col.PkColumn]; + var value = parentPanel.Values[col.PkColumn]; filter[col.FkColumn] = value; } @@ -213,7 +211,7 @@ private async Task ConfigureOneToOneFormView(JJFormView childFormView, FormElementRelationship relationship, Dictionary filter) { Dictionary? childValues = null; - if (filter.Any()) + if (filter.Count > 0) { childValues = await parentFormView.EntityRepository.GetFieldsAsync(childFormView.FormElement, filter!); @@ -236,7 +234,6 @@ private async Task ConfigureOneToOneFormView(JJFormView childFormView, childFormView.DataPanel.PageState = relationship.EditModeOpenByDefault ? childFormView.PageState: PageState.View; else childFormView.DataPanel.PageState = PageState.Insert; - } childFormView.RelationValues = DataHelper.GetRelationValues(parentFormView.FormElement, filter); diff --git a/src/Core/UI/Components/FormView/FormViewScripts.cs b/src/Core/UI/Components/FormView/FormViewScripts.cs index 308d56a97..2f7b2b45e 100644 --- a/src/Core/UI/Components/FormView/FormViewScripts.cs +++ b/src/Core/UI/Components/FormView/FormViewScripts.cs @@ -11,7 +11,7 @@ public class FormViewScripts(JJFormView formView) private string GetEncryptedRouteContext(ComponentContext context) { var routeContext = RouteContext.FromFormElement(formView.FormElement, context); - return formView.EncryptionService.EncryptRouteContext(routeContext); + return formView.EncryptionService.EncryptObject(routeContext); } public string GetShowInsertSuccessScript() @@ -24,7 +24,7 @@ public string GetShowInsertSuccessScript() public string GetInsertSelectionScript(Dictionary values) { var encryptedRouteContext = GetEncryptedRouteContext(ComponentContext.InsertSelection); - var encryptedValues = formView.EncryptionService.EncryptDictionary(values); + var encryptedValues = formView.EncryptionService.EncryptObject(values); //language=Javascript return $"FormViewHelper.insertSelection('{formView.Name}', '{encryptedValues}', '{encryptedRouteContext}')"; } diff --git a/src/Core/UI/Components/FormView/JJFormView.cs b/src/Core/UI/Components/FormView/JJFormView.cs index ea9c1647c..611434be5 100644 --- a/src/Core/UI/Components/FormView/JJFormView.cs +++ b/src/Core/UI/Components/FormView/JJFormView.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using JJMasterData.Commons.Data; using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Data.Entity.Repository.Abstractions; using JJMasterData.Commons.Exceptions; @@ -23,7 +22,6 @@ using JJMasterData.Core.DataDictionary.Repository.Abstractions; using JJMasterData.Core.DataManager; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.DataManager.Expressions.Providers; using JJMasterData.Core.DataManager.Models; using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.Events.Abstractions; @@ -31,6 +29,7 @@ using JJMasterData.Core.Extensions; using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.Logging; +using JJMasterData.Core.Tasks; using JJMasterData.Core.UI.Events.Args; using JJMasterData.Core.UI.Html; using JJMasterData.Core.UI.Routing; @@ -39,7 +38,6 @@ using Microsoft.Extensions.Options; #if NET48 using JJMasterData.Commons.Configuration; -using JJMasterData.Core.Tasks; #endif namespace JJMasterData.Core.UI.Components; @@ -70,6 +68,7 @@ public class JJFormView : AsyncComponent private JJDataPanel? _dataPanel; private JJGridView? _gridView; + private JJFormView? _insertSelectionFormView; private FormViewScripts? _scripts; private ActionMap? _currentActionMap; private BasicAction? _currentAction; @@ -178,7 +177,7 @@ public Dictionary RelationValues { get { - if (!_relationValues.Any()) + if (_relationValues.Count == 0) { _relationValues = GetRelationValuesFromForm(); } @@ -219,7 +218,7 @@ public JJGridView GridView } } - private async Task RenderInsertActionAtGrid(object _, GridRenderEventArgs args) + private async ValueTask RenderInsertActionAtGrid(object _, GridRenderEventArgs args) { var insertAction = GridView.InsertAction; var formStateData = await GridView.GetFormStateDataAsync(); @@ -230,7 +229,7 @@ private async Task RenderInsertActionAtGrid(object _, GridRenderEventArgs args) DataPanel.PageState = PageState.Insert; - var result = await GetFormResult(new FormContext(formStateData.Values, DataPanel.Errors, PageState.Insert), true); + var result = await GetFormResult(formStateData.Values, DataPanel.Errors, PageState.Insert, true); if (result is HtmlComponentResult htmlComponentResult) args.HtmlBuilder.Append(htmlComponentResult.HtmlBuilder); @@ -243,15 +242,21 @@ public PageState PageState { get { - if (CurrentContext.Request.Form[$"form-view-page-state-{Name}"] != null && _pageState is null) - _pageState = (PageState)int.Parse(CurrentContext.Request.Form[$"form-view-page-state-{Name}"]); + if (_pageState is null && CurrentContext.Request.Form.TryGetValue($"form-view-page-state-{Name}", out var formPageState)) + _pageState = (PageState)int.Parse(formPageState); + if (_pageState is null && CurrentContext.Request.QueryString.TryGetValue($"{FormElement.Name}_PageState", out var queryStringPageState)) + { + if(Enum.TryParse(queryStringPageState,ignoreCase: true, out PageState pageState)) + _pageState = pageState; + } + return _pageState ?? PageState.List; } internal set => _pageState = value; } - private ActionMap? CurrentActionMap + internal ActionMap? CurrentActionMap { get { @@ -318,8 +323,8 @@ internal RelationshipType RelationshipType { get { - if (CurrentContext.Request.Form[$"form-view-relationship-type-{Name}"] != null && _relationshipType is null) - _relationshipType = (RelationshipType)int.Parse(CurrentContext.Request.Form[$"form-view-relationship-type-{Name}"]); + if (_relationshipType is null && CurrentContext.Request.Form.TryGetValue($"form-view-relationship-type-{Name}", out var relationshipType)) + _relationshipType = (RelationshipType)int.Parse(relationshipType); return _relationshipType ?? RelationshipType.Parent; } @@ -328,23 +333,25 @@ internal RelationshipType RelationshipType internal bool IsInsertAtGridView => PageState is PageState.List && FormElement.Options.GridToolbarActions.InsertAction.ShowOpenedAtGrid; - + internal IHttpContext CurrentContext { get; } internal IFormValues FormValues => CurrentContext.Request.Form; - internal IQueryString QueryString => CurrentContext.Request.QueryString; + internal IEncryptionService EncryptionService { get; } + internal IComponentFactory ComponentFactory { get; } internal IEntityRepository EntityRepository { get; } - internal FormValuesService FormValuesService { get; } - internal FieldValuesService FieldValuesService { get; } internal ExpressionsService ExpressionsService { get; } + + private IQueryString QueryString => CurrentContext.Request.QueryString; + private FormValuesService FormValuesService { get; } + private FieldValuesService FieldValuesService { get; } + private HtmlTemplateService HtmlTemplateService { get; } private IEnumerable PluginHandlers { get; } private IOptionsSnapshot Options { get; } private IStringLocalizer StringLocalizer { get; } private ILogger Logger { get; } - internal IDataDictionaryRepository DataDictionaryRepository { get; } - internal FormService FormService { get; } - internal IEncryptionService EncryptionService { get; } - internal IComponentFactory ComponentFactory { get; } - + private IDataDictionaryRepository DataDictionaryRepository { get; } + private FormService FormService { get; } + #endregion #region "Constructors" @@ -359,6 +366,7 @@ internal JJFormView( FormValuesService formValuesService, FieldValuesService fieldValuesService, ExpressionsService expressionsService, + HtmlTemplateService htmlTemplateService, IEnumerable pluginHandlers, IOptionsSnapshot options, IStringLocalizer stringLocalizer, @@ -366,7 +374,7 @@ internal JJFormView( IComponentFactory componentFactory) { FormElement = formElement; - Name = ComponentNameGenerator.Create(FormElement.Name); + Name = formElement.Name.ToLowerInvariant(); CurrentContext = currentContext; EntityRepository = entityRepository; ShowTitle = formElement.Options.Grid.ShowTitle; @@ -375,6 +383,7 @@ internal JJFormView( FormValuesService = formValuesService; FieldValuesService = fieldValuesService; ExpressionsService = expressionsService; + HtmlTemplateService = htmlTemplateService; PluginHandlers = pluginHandlers; Options = options; StringLocalizer = stringLocalizer; @@ -388,7 +397,7 @@ internal JJFormView( protected override async Task BuildResultAsync() { if (!RouteContext.CanRender(FormElement)) - return new EmptyComponentResult(); + return EmptyComponentResult.Value; if (RouteContext.IsCurrentFormElement(FormElement.Name)) return await GetFormResultAsync(); @@ -413,15 +422,14 @@ private async Task GetChildFormView() var isInsertSelection = PageState is PageState.Insert && GridView.ToolbarActions.InsertAction.ElementNameToSelect == childFormView.FormElement.Name; - + childFormView.ShowTitle = isInsertSelection; var panelState = DataPanel.PageState; - if (PageState is PageState.View || panelState is PageState.Insert || panelState is PageState.Update) childFormView.DisableActionsAtViewMode(); - + if (!isInsertSelection) return childFormView; @@ -429,7 +437,6 @@ private async Task GetChildFormView() childFormView.GridView.ToolbarActions.Add(GetInsertSelectionBackAction()); childFormView.GridView.OnRenderActionAsync += InsertSelectionOnRenderAction; - return childFormView; } @@ -480,13 +487,20 @@ internal async Task GetReloadPanelResultAsync() var field = FormElement.Fields[fieldName]; var scripts = new HtmlBuilder(); + + var formStateData = new FormStateData + { + Values = values, + UserValues = UserValues, + PageState = PageState + }; foreach (var action in field.Actions) { if (action is not PluginFieldAction { TriggerOnChange: true } pluginAction) continue; - var result = await GetPluginActionResult(pluginAction, values, fieldName); + var result = await GetPluginActionResult(pluginAction, formStateData, fieldName); if (result.JsCallback is not null) scripts.AppendScript(result.JsCallback); @@ -519,18 +533,17 @@ private async Task GetSaveActionResult() DataPanel.Errors = errors; if (errors.Count != 0 && !IsInsertAtGridView) - return await GetFormResult(new FormContext(values, errors, PageState), true); + return await GetFormResult(values, errors, PageState, true); if (!string.IsNullOrEmpty(UrlRedirect)) { CurrentActionMap = null; return new RedirectComponentResult(UrlRedirect!); } - if (PageState is PageState.Insert && insertAction.ReopenForm) { - var formResult = await GetFormResult(new FormContext(new Dictionary(RelationValues!), PageState.Insert), false); + var formResult = await GetFormResult(new Dictionary(RelationValues!), PageState.Insert, false); if (formResult is HtmlComponentResult htmlComponent) { @@ -542,20 +555,24 @@ private async Task GetSaveActionResult() return formResult; } - if (ContainsRelationshipLayout(new FormStateData(values, PageState)) && DataPanel.ContainsPanelState()) + var isAtRelationship = + IsChildFormView && RelationshipType is not RelationshipType.OneToMany || + (ContainsRelationshipLayout(new FormStateData(values, PageState)) && DataPanel.ContainsPanelState()); + + if (isAtRelationship) { DataPanel.PageState = PageState.View; - return await GetFormResult(new FormContext(values, PageState.View), false); + return await GetFormResult(values, PageState.View, false); } if (PageState is PageState.Insert) { var visibleRelationships = GetVisibleRelationships(values, PageState.Update); - if(visibleRelationships.Any()) + if(visibleRelationships.Count > 0) { PageState = PageState.Update; - return await GetFormResult(new FormContext(values, PageState.View), false); + return await GetFormResult(values, PageState.View, false); } } @@ -568,7 +585,7 @@ private async Task GetSaveActionResult() return await GridView.GetResultAsync(); } - private static ComponentResult CloseModal() => new JsonComponentResult(new {closeModal = true}); + private static JsonComponentResult CloseModal() => new(new {closeModal = true}); private void AppendInsertSuccessAlert(HtmlBuilder htmlBuilder) { @@ -608,43 +625,35 @@ private Task GetBackActionResult() private async Task GetFormActionResult() { SetFormServiceEvents(); - - ComponentResult? result; - if (CurrentAction is ViewAction) - result = await GetViewResult(); - else if (CurrentAction is EditAction) - result = await GetUpdateResult(); - else if (CurrentAction is InsertAction) - result = await GetInsertResult(); - else if (CurrentAction is AuditLogFormToolbarAction or AuditLogGridToolbarAction) - result = await GetAuditLogResult(); - else if (CurrentAction is DeleteAction) - result = await GetDeleteResult(); - else if (CurrentAction is SaveAction) - result = await GetSaveActionResult(); - else if (CurrentAction is BackAction) - result = await GetBackActionResult(); - else if (CurrentAction is CancelAction) - result = await GetCancelActionResult(); - else if (CurrentAction is SqlCommandAction) - result = await GetSqlCommandActionResult(); - else if (CurrentAction is PluginAction) - result = await GetPluginActionResult(); - else - result = await GetDefaultResult(); - if (result is HtmlComponentResult htmlComponent) + var result = CurrentAction switch { - var html = htmlComponent.HtmlBuilder; + ViewAction => await GetViewResult(), + EditAction => await GetUpdateResult(), + InsertAction => await GetInsertResult(), + AuditLogFormToolbarAction or AuditLogGridToolbarAction => await GetAuditLogResult(), + DeleteAction => await GetDeleteResult(), + SaveAction => await GetSaveActionResult(), + BackAction => await GetBackActionResult(), + CancelAction => await GetCancelActionResult(), + SqlCommandAction => await GetSqlCommandActionResult(), + HtmlTemplateAction => await GetHtmlTemplateActionResult(), + PluginAction => await GetPluginActionResult(), + _ => await GetDefaultResult() + }; - html.WithNameAndId(Name); + if (result is not HtmlComponentResult htmlComponent) + return result; + + var html = htmlComponent.HtmlBuilder; + + html.WithNameAndId(Name); - AppendFormViewHiddenInputs(html); + AppendFormViewHiddenInputs(html); - if (ComponentContext is ComponentContext.FormViewReload) - { - return new ContentComponentResult(html); - } + if (ComponentContext is ComponentContext.FormViewReload) + { + return new ContentComponentResult(html); } return result; @@ -660,14 +669,23 @@ private void AppendFormViewHiddenInputs(HtmlBuilder html) html.AppendHiddenInput($"current-action-map-{Name}", - EncryptionService.EncryptActionMap(CurrentActionMap)); + EncryptionService.EncryptObject(CurrentActionMap)); html.AppendHiddenInput($"form-view-relation-values-{FormElement.Name}", - EncryptionService.EncryptDictionary(RelationValues)); + EncryptionService.EncryptObject(RelationValues)); html.AppendHiddenInput($"form-view-route-context-{Name}", - EncryptionService.EncryptRouteContext(RouteContext.FromFormElement(FormElement, ComponentContext.FormViewReload))); + EncryptionService.EncryptObject(RouteContext.FromFormElement(FormElement, ComponentContext.FormViewReload))); } } + private async Task GetHtmlTemplateActionResult() + { + var htmlTemplateAction = (HtmlTemplateAction)CurrentAction!; + + var html = await HtmlTemplateService.RenderTemplate(htmlTemplateAction, CurrentActionMap!.PkFieldValues); + + return new ContentComponentResult(html); + } + private async Task GetSqlCommandActionResult() { JJMessageBox? messageBox = null; @@ -707,14 +725,13 @@ private async Task GetSqlCommandActionResult() private async Task GetPluginActionResult() { - var formValues = await GetFormValuesAsync(); - - + var formStateData = await GetFormStateDataAsync(); + PluginActionResult result; try { - result = await GetPluginActionResult(formValues); + result = await GetPluginActionResult((PluginAction)CurrentAction!,formStateData, CurrentActionMap!.FieldName); } catch (Exception exception) { @@ -722,7 +739,7 @@ private async Task GetPluginActionResult() result = PluginActionResult.Error(StringLocalizer["Error"], exception.Message); } - var formResult = await GetDefaultResult(formValues); + var formResult = await GetDefaultResult(formStateData.Values); if (formResult is HtmlComponentResult htmlComponentResult) { @@ -732,45 +749,55 @@ private async Task GetPluginActionResult() return formResult; } - private Task GetPluginActionResult(Dictionary formValues) - { - var pluginAction = (PluginAction)CurrentAction!; - - return GetPluginActionResult(pluginAction, formValues, CurrentActionMap!.FieldName); - } - - private Task GetPluginActionResult(PluginAction pluginAction, - Dictionary values, string? fieldName) + private async Task GetPluginActionResult(PluginAction pluginAction, + FormStateData formStateData, string? fieldName) { var pluginHandler = PluginHandlers.First(p => p.Id == pluginAction.PluginId); - - var formStateData = new FormStateData - { - Values = values, - UserValues = UserValues, - PageState = PageState, - }; - + switch (pluginHandler) { case IPluginActionHandler pluginActionHandler: - return pluginActionHandler.ExecuteActionAsync(new PluginActionContext + { + var context = new PluginActionContext { ActionContext = GetActionContext(pluginAction, formStateData), ConfigurationMap = pluginAction.ConfigurationMap - }); + }; + + var result = await pluginActionHandler.ExecuteActionAsync(context); + + if (context.SecretValues.Count > 0) + SetSecretValues(context.SecretValues); + + return result; + } case IPluginFieldActionHandler pluginFieldActionHandler: - return pluginFieldActionHandler.ExecuteActionAsync(context: new PluginFieldActionContext + { + var context = new PluginFieldActionContext { ActionContext = GetActionContext(pluginAction, formStateData, fieldName), FieldMap = ((PluginFieldAction)pluginAction).FieldMap, ConfigurationMap = pluginAction.ConfigurationMap - }); + }; + + var result = await pluginFieldActionHandler.ExecuteActionAsync(context); + + if (context.SecretValues.Count > 0) + SetSecretValues(context.SecretValues); + + return result; + } default: throw new JJMasterDataException("Invalid plugin handler"); } } + + private void SetSecretValues(Dictionary secretValues) + { + DataPanel.SecretValues ??= new(); + DataHelper.CopyIntoDictionary(DataPanel.SecretValues, secretValues, true); + } private void SetFormServiceEvents() { @@ -809,33 +836,31 @@ private async Task GetUpdateResult() } PageState = PageState.Update; - return await GetFormResult(new FormContext(values, PageState), autoReloadFields); + return await GetFormResult(values, PageState, autoReloadFields); } private async Task GetDefaultResult(Dictionary? formValues = null) { - if (PageState is PageState.List || ContainsGridAction()) + var containsGridAction = false; + if (GridView.GridTableActions.Any(a=> a is InsertSelectionAction)) + { + containsGridAction = !string.IsNullOrEmpty(CurrentContext.Request.Form[$"grid-view-action-map-{Name}"]); + } + + if (PageState is PageState.List || containsGridAction) return await GetGridViewResult(); - + switch (PageState) { case PageState.Insert: { - formValues ??= new Dictionary(); - DataHelper.CopyIntoDictionary(formValues,RelationValues!); - var formContext = new FormContext(formValues, PageState); - var reloadFields = DataPanel.PageState is not PageState.View && CurrentAction is not PluginAction; - return await GetFormResult( - formContext, - reloadFields); + return await GetInsertResult(); } - case PageState.Update or PageState.View: { formValues ??= await GetFormValuesAsync(); var reloadFields = DataPanel.PageState is not PageState.View && CurrentAction is not PluginAction; - var formContext = new FormContext(formValues, PageState); - return await GetFormResult(formContext, reloadFields); + return await GetFormResult(formValues, PageState, reloadFields); } default: return await GetGridViewResult(); @@ -846,43 +871,50 @@ private async Task GetInsertResult() { var insertAction = GridView.ToolbarActions.InsertAction; var formData = new FormStateData(RelationValues!, UserValues, PageState.List); - + var isInsertSelection = !string.IsNullOrEmpty(insertAction.ElementNameToSelect); + bool isVisible = ExpressionsService.GetBoolValue(insertAction.VisibleExpression, formData); if (!isVisible) throw new UnauthorizedAccessException(StringLocalizer["Insert action is not enabled"]); + + if (isInsertSelection) + { + var insertSelectionFormView = await GetInsertSelectionFormView(); + if (insertSelectionFormView.GridView.HasAction() || PageState is PageState.Insert) + { + return await GetInsertSelectionListResult(); + } + } + + var formValues = await GetFormValuesAsync(); + var reloadFields = DataPanel.PageState is not PageState.View && CurrentAction is not PluginAction; + if (PageState == PageState.Insert) { - var formValues = await GetFormValuesAsync(); - return await GetFormResult(new FormContext(formValues, PageState), true); + DataHelper.CopyIntoDictionary(formValues,RelationValues!); + + return await GetFormResult(formValues, PageState.Insert, reloadFields); } PageState = PageState.Insert; - if (string.IsNullOrEmpty(insertAction.ElementNameToSelect)) - return await GetFormResult(new FormContext(new Dictionary(RelationValues!), PageState.Insert), false); - - return await GetInsertSelectionListResult(); + if (isInsertSelection) + return await GetInsertSelectionListResult(); + + return await GetFormResult(formValues, PageState.Insert, reloadFields); } private async Task GetInsertSelectionListResult() { - var insertAction = GridView.ToolbarActions.InsertAction; - var html = new Div(); + var formView = await GetInsertSelectionFormView(); + var html = new HtmlBuilder(HtmlTag.Div); html.AppendHiddenInput($"form-view-insert-selection-values-{Name}"); - - var formView = await ComponentFactory.FormView.CreateAsync(insertAction.ElementNameToSelect); - formView.FormElement.ParentName = FormElement.Name; - formView.UserValues = UserValues; - formView.GridView.OnRenderActionAsync += InsertSelectionOnRenderAction; - - formView.GridView.ToolbarActions.Add(GetInsertSelectionBackAction()); - - formView.GridView.GridTableActions.Add(new InsertSelectionAction()); - var result = await formView.GetFormResultAsync(); + CurrentActionMap = null; + if (result is HtmlComponentResult htmlComponentResult) { html.Append(htmlComponentResult.HtmlBuilder); @@ -895,6 +927,21 @@ private async Task GetInsertSelectionListResult() return new RenderedComponentResult(html); } + private async ValueTask GetInsertSelectionFormView() + { + if (_insertSelectionFormView != null) + return _insertSelectionFormView; + + _insertSelectionFormView = await ComponentFactory.FormView.CreateAsync(GridView.InsertAction.ElementNameToSelect); + _insertSelectionFormView.FormElement.ParentName = FormElement.Name; + _insertSelectionFormView.UserValues = UserValues; + _insertSelectionFormView.GridView.OnRenderActionAsync += InsertSelectionOnRenderAction; + _insertSelectionFormView.GridView.ToolbarActions.Add(GetInsertSelectionBackAction()); + _insertSelectionFormView.GridView.GridTableActions.Add(new InsertSelectionAction()); + + return _insertSelectionFormView; + } + private ScriptAction GetInsertSelectionBackAction() { return new ScriptAction @@ -911,7 +958,7 @@ private ScriptAction GetInsertSelectionBackAction() private async Task GetInsertSelectionResult() { var insertValues = EncryptionService.DecryptDictionary(FormValues[$"form-view-insert-selection-values-{Name}"]); - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); var childElementName = GridView.ToolbarActions.InsertAction.ElementNameToSelect; var childElement = await DataDictionaryRepository.GetFormElementAsync(childElementName); @@ -924,7 +971,7 @@ private async Task GetInsertSelectionResult() await FieldValuesService.MergeWithExpressionValuesAsync(FormElement, new FormStateData(mappedFkValues!,UserValues, PageState.Insert)); var errors = await InsertFormValuesAsync(values, false); - + if (errors.Count > 0) { html.AppendComponent(ComponentFactory.Html.MessageBox.Create(errors, MessageIcon.Warning)); @@ -943,9 +990,15 @@ private async Task GetInsertSelectionResult() } else { + var pks = DataHelper.GetPkValues(FormElement, values); + + var dbValues = await EntityRepository.GetFieldsAsync(FormElement, pks); + + DataHelper.CopyIntoDictionary(values,dbValues); + PageState = PageState.Update; - var result = await GetFormResult(new FormContext(values, PageState), false); + var result = await GetFormResult(values, PageState, false); if (result is RenderedComponentResult renderedComponentResult) { @@ -975,12 +1028,12 @@ private async Task GetViewResult() PageState = PageState.View; var filter = CurrentActionMap.PkFieldValues; var values = await EntityRepository.GetFieldsAsync(FormElement, filter); - return await GetFormResult(new FormContext(values, PageState), false); + return await GetFormResult(values, PageState, false); } private async Task GetDeleteResult() { - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); var messageFactory = ComponentFactory.Html.MessageBox; try { @@ -1032,7 +1085,7 @@ private async Task GetAuditLogResult() if (PageState == PageState.View) { - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); var logDetailsHtml = await AuditLogView.GetLogDetailsHtmlAsync(actionMap?.PkFieldValues); @@ -1057,7 +1110,7 @@ private async Task GetImportationResult() if (!isVisible) throw new UnauthorizedAccessException(StringLocalizer["Import action not enabled"]); - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); if (ShowTitle) html.AppendComponent(GetTitle(new FormStateData(UserValues, PageState.Import))); @@ -1083,11 +1136,14 @@ private async Task GetImportationResult() return new RenderedComponentResult(html); } - - private Task GetFormResult(FormContext formContext, bool autoReloadFormFields) + + private Task GetFormResult(Dictionary values, PageState pageState, bool autoReloadFormFields) { - var (values, errors, pageState) = formContext; + return GetFormResult(values, new(), pageState, autoReloadFormFields); + } + private Task GetFormResult(Dictionary values, Dictionary errors, PageState pageState, bool autoReloadFormFields) + { var visibleRelationships = GetVisibleRelationships(values, pageState); var containsRelationshipLayout = ContainsRelationshipLayout(visibleRelationships); @@ -1111,7 +1167,7 @@ internal async Task GetRelationshipLayoutResult( Dictionary values) { var formStateData = new FormStateData(values, UserValues, PageState); - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); if (ShowTitle) html.AppendComponent(GetTitle(formStateData)); @@ -1121,7 +1177,7 @@ internal async Task GetRelationshipLayoutResult( if (relationshipsResult is HtmlComponentResult htmlResult) { - var topActions = GetTopToolbarActions(FormElement).ToList(); + var topActions = GetTopToolbarActions(FormElement); html.PrependComponent(await GetFormToolbarAsync(topActions)); @@ -1129,7 +1185,7 @@ internal async Task GetRelationshipLayoutResult( var toolbarActions = FormElement.Options.FormToolbarActions; var bottomActions = - toolbarActions.Where(a => a.Location is FormToolbarActionLocation.Bottom).ToList(); + toolbarActions.FindAll(a => a.Location is FormToolbarActionLocation.Bottom); if(!IsChildFormView) toolbarActions.BackAction.SetVisible(true); @@ -1195,13 +1251,12 @@ private List GetVisibleRelationships( Dictionary values, PageState pageState) { + var formStateData = new FormStateData(values, pageState); var visibleRelationships = FormElement .Relationships - .Where(r => r.ViewType != RelationshipViewType.None || r.IsParent) - .Where(r => - ExpressionsService.GetBoolValue(r.Panel.VisibleExpression, - new FormStateData(values, pageState))) - .ToList(); + .FindAll(r => + r.ViewType != RelationshipViewType.None || r.IsParent + && ExpressionsService.GetBoolValue(r.Panel.VisibleExpression, formStateData)); return visibleRelationships; } @@ -1213,16 +1268,16 @@ internal bool ContainsRelationshipLayout(FormStateData formStateData) private static bool ContainsRelationshipLayout(List visibleRelationships) { - return visibleRelationships.Any() && visibleRelationships.Any(r => !r.IsParent); + return visibleRelationships.Count > 0 && visibleRelationships.Any(r => !r.IsParent); } private async Task GetDataPanelHtml() { - var formHtml = new Div(); + var formHtml = new HtmlBuilder(HtmlTag.Div); ConfigureFormToolbar(); - var topToolbarActions = GetTopToolbarActions(FormElement).ToList(); + var topToolbarActions = GetTopToolbarActions(FormElement); formHtml.AppendComponent(await GetFormToolbarAsync(topToolbarActions)); @@ -1231,7 +1286,7 @@ private async Task GetDataPanelHtml() var parentPanelHtml = await DataPanel.GetPanelHtmlBuilderAsync(); - var panelAndBottomToolbarActions = GetPanelToolbarActions(FormElement).ToList(); + var panelAndBottomToolbarActions = GetPanelToolbarActions(FormElement); panelAndBottomToolbarActions.AddRange(GetBottomToolbarActions(FormElement)); var toolbar = await GetFormToolbarAsync(panelAndBottomToolbarActions); @@ -1240,7 +1295,7 @@ private async Task GetDataPanelHtml() formHtml.AppendComponent(toolbar); - if (DataPanel.Errors.Any()) + if (DataPanel.Errors.Count > 0) formHtml.AppendComponent(ComponentFactory.Html.ValidationSummary.Create(DataPanel.Errors)); return formHtml; @@ -1248,7 +1303,7 @@ private async Task GetDataPanelHtml() internal async Task GetParentPanelHtmlAtRelationship(FormElementRelationship relationship) { - var formHtml = new Div(); + var formHtml = new HtmlBuilder(HtmlTag.Div); DataPanel.Values = await DataPanel.GetFormValuesAsync(); @@ -1259,7 +1314,7 @@ internal async Task GetParentPanelHtmlAtRelationship(FormElementRel ConfigureFormToolbar(); - var panelToolbarActions = GetPanelToolbarActions(FormElement).ToList(); + var panelToolbarActions = GetPanelToolbarActions(FormElement); var toolbar = await GetFormToolbarAsync(panelToolbarActions); @@ -1267,37 +1322,37 @@ internal async Task GetParentPanelHtmlAtRelationship(FormElementRel formHtml.AppendComponent(toolbar); - if (DataPanel.Errors.Any()) + if (DataPanel.Errors.Count > 0) formHtml.AppendComponent(ComponentFactory.Html.ValidationSummary.Create(DataPanel.Errors)); return formHtml; } - private static IEnumerable GetPanelToolbarActions(FormElement formElement) + private static List GetPanelToolbarActions(FormElement formElement) { var toolbarActions = formElement.Options.FormToolbarActions - .Where(a => a.Location == FormToolbarActionLocation.Panel); + .FindAll(a => a.Location == FormToolbarActionLocation.Panel); return toolbarActions; } - private static IEnumerable GetTopToolbarActions(FormElement formElement) + private static List GetTopToolbarActions(FormElement formElement) { var toolbarActions = formElement.Options.FormToolbarActions - .Where(a => a.Location == FormToolbarActionLocation.Top); + .FindAll(a => a.Location == FormToolbarActionLocation.Top); return toolbarActions; } - private static IEnumerable GetBottomToolbarActions(FormElement formElement) + private static List GetBottomToolbarActions(FormElement formElement) { var toolbarActions = formElement.Options.FormToolbarActions - .Where(a => a.Location == FormToolbarActionLocation.Bottom); + .FindAll(a => a.Location == FormToolbarActionLocation.Bottom); return toolbarActions; } - private async Task GetAuditLogBottomBar() + private async ValueTask GetAuditLogBottomBar() { var formStateData = await GetFormStateDataAsync(); var hideAuditLogButton = @@ -1312,7 +1367,7 @@ private async Task GetAuditLogBottomBar() return toolbar; } - private async Task GetFormToolbarAsync(IList actions) + private async ValueTask GetFormToolbarAsync(IList actions) { var toolbar = new JJToolbar { @@ -1340,7 +1395,7 @@ private async Task GetFormToolbarAsync(IList actions) var btnGroup = ComponentFactory.Html.LinkButtonGroup.Create(); btnGroup.CaretText = StringLocalizer["More"]; - foreach (var groupedAction in actions.Where(a => a.IsGroup).ToList()) + foreach (var groupedAction in actions.Where(a => a.IsGroup)) { btnGroup.ShowAsButton = groupedAction.ShowAsButton; var factory = ComponentFactory.ActionButton; @@ -1354,18 +1409,18 @@ private async Task GetFormToolbarAsync(IList actions) return toolbar; } - private Task InsertSelectionOnRenderAction(object? sender, ActionEventArgs args) + private ValueTask InsertSelectionOnRenderAction(object? sender, ActionEventArgs args) { if (sender is not JJGridView) - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; if (args.ActionName is not InsertSelectionAction.ActionName) - return Task.CompletedTask; - + return ValueTaskHelper.CompletedTask; + args.LinkButton.Tooltip = StringLocalizer["Select"]; args.LinkButton.OnClientClick = Scripts.GetInsertSelectionScript(args.FieldValues); - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; } @@ -1410,7 +1465,7 @@ public async Task> DeleteFormValuesAsync(Dictionary ValidateFields(Dictionary valu private void ClearTempFiles() { - var uploadFields = FormElement.Fields.ToList().FindAll(x => x.Component == FormComponent.File); + var uploadFields = FormElement.Fields.FindAll(x => x.Component == FormComponent.File); foreach (var field in uploadFields) { string sessionName = $"{field.Name}-upload-view_jjfiles"; @@ -1437,20 +1492,20 @@ private void ClearTempFiles() } - public async Task GetFormStateDataAsync() + public async ValueTask GetFormStateDataAsync() { if (_formStateData != null) return _formStateData; - var initalValues = new Dictionary(); + var initialValues = new Dictionary(); if(_dataPanel is not null) - DataHelper.CopyIntoDictionary(initalValues, DataPanel.Values); + DataHelper.CopyIntoDictionary(initialValues, DataPanel.Values); if(_currentActionMap is not null) - DataHelper.CopyIntoDictionary(initalValues, CurrentActionMap!.PkFieldValues!); + DataHelper.CopyIntoDictionary(initialValues, CurrentActionMap!.PkFieldValues!); - var initialFormStateData = new FormStateData(initalValues, UserValues, PageState); + var initialFormStateData = new FormStateData(initialValues, UserValues, PageState); var autoReloadFormFields = CurrentContext.Request.Form.ContainsFormValues(); var values = await FormValuesService.GetFormValuesWithMergedValuesAsync(FormElement, initialFormStateData, autoReloadFormFields); @@ -1468,8 +1523,6 @@ public Dictionary GetRelationValuesFromForm() return EncryptionService.DecryptDictionary(encryptedRelationValues); } - private bool ContainsGridAction() => !string.IsNullOrEmpty(CurrentContext.Request.Form[$"grid-view-action-map-{Name}"]); - internal void DisableActionsAtViewMode() { foreach (var action in FormElement.Options.GridTableActions) @@ -1500,7 +1553,6 @@ public ActionContext GetActionContext(BasicAction action, FormStateData formStat FormElement = FormElement, FormStateData = formStateData, FieldName = fieldName, - IsSubmit = action is ISubmittableAction { IsSubmit: true }, ParentComponentName = Name }; } @@ -1587,13 +1639,7 @@ public bool ShowToolbar get => GridView.ShowToolbar; set => GridView.ShowToolbar = value; } - - [Obsolete("Please use GridView.ShowPagging")] - public bool ShowPagging - { - get => GridView.ShowPagging; - set => GridView.ShowPagging = value; - } + #if NETFRAMEWORK [Obsolete("Please use GridView.GetGridValuesAsync()")] diff --git a/src/Core/UI/Components/GridView/GridCaptionView.cs b/src/Core/UI/Components/GridView/GridCaptionView.cs index ad3f2e202..1568584cc 100644 --- a/src/Core/UI/Components/GridView/GridCaptionView.cs +++ b/src/Core/UI/Components/GridView/GridCaptionView.cs @@ -8,15 +8,12 @@ namespace JJMasterData.Core.UI.Components; -internal class GridCaptionView( +internal sealed class GridCaptionView( string title, IControlFactory comboBoxFactory, IStringLocalizer stringLocalizer) { - private IControlFactory ComboBoxFactory { get; } = comboBoxFactory; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - public bool ShowAsModal { get; init; } = false; - + public bool ShowAsModal { get; init; } public required string Name { get; init; } public required FormElement FormElement { get; init; } @@ -34,41 +31,41 @@ public Task GetHtmlBuilderAsync() private async Task GetCaptionHtmlBuilder(FormElementField field) { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); if (field != null) { - var cbo = ComboBoxFactory.Create(); + var cbo = comboBoxFactory.Create(); cbo.Name = field.Name; if (field.DataItem != null) cbo.DataItem = field.DataItem; var values = await cbo.GetValuesAsync(); + + if (values is not { Count: > 0 }) + return div; - if (values is { Count: > 0 }) + foreach (var item in values) { - foreach (var item in values) + div.Append(HtmlTag.Div, div => { - div.Append(HtmlTag.Div, div => - { - div.WithStyle( "height:2.5rem"); + div.WithStyle( "height:2.5rem"); - div.AppendComponent(new JJIcon(item.Icon, item.IconColor) - { - CssClass = "fa-fw fa-2x" - }); - div.AppendText("  "); - div.AppendText(StringLocalizer[item.Description]); - div.Append(HtmlTag.Br); + div.AppendComponent(new JJIcon(item.Icon, item.IconColor) + { + CssClass = "fa-fw fa-2x" }); - } + div.AppendText("  "); + div.AppendText(stringLocalizer[item.Description]); + div.Append(HtmlTag.Br); + }); } } else { div.Append(HtmlTag.Br); - div.AppendText(StringLocalizer["There is no caption to be displayed"]); + div.AppendText(stringLocalizer["There is no caption to be displayed"]); } return div; @@ -86,15 +83,15 @@ private async Task GetModalHtmlBuilder() var dialog = new JJModalDialog { Name = $"{Name}-caption-modal", - Title = StringLocalizer[title], + Title = stringLocalizer[title], HtmlBuilderContent = form, Buttons = [ - new JJLinkButton(StringLocalizer) + new JJLinkButton(stringLocalizer) { Name = $"{Name}-caption-modal-close-btn", Icon = IconType.SolidXmark, - Text = StringLocalizer["Close"], + Text = stringLocalizer["Close"], ShowAsButton = true, OnClientClick = BootstrapHelper.GetCloseModalScript($"{Name}-caption-modal") } diff --git a/src/Core/UI/Components/GridView/GridFilter.cs b/src/Core/UI/Components/GridView/GridFilter.cs index 313133232..eafebfd54 100644 --- a/src/Core/UI/Components/GridView/GridFilter.cs +++ b/src/Core/UI/Components/GridView/GridFilter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Localization; @@ -19,57 +18,53 @@ namespace JJMasterData.Core.UI.Components; -internal class GridFilter(JJGridView gridView) +internal sealed class GridFilter(JJGridView gridView) { private const string FilterActionName = "filter"; private const string ClearActionName = "clear"; - + internal const string FilterFieldPrefix = "filter_"; + private readonly IHttpContext _currentContext = gridView.CurrentContext; + private readonly IStringLocalizer _stringLocalizer = gridView.StringLocalizer; + private Dictionary _currentFilter; private Dictionary _userFilters; - private JJGridView GridView { get; } = gridView; - private IHttpContext CurrentContext => GridView.CurrentContext; - private IStringLocalizer StringLocalizer => GridView.StringLocalizer; - public string Name => GridView.Name + "-filter"; + public string Name => gridView.Name + "-filter"; public event AsyncEventHandler OnFilterLoadAsync; - - internal async Task GetFilterHtml() + + internal async ValueTask GetFilterHtml() { - var filterAction = GridView.FilterAction; - var formData = await GridView.GetFormStateDataAsync(); + var filterAction = gridView.FilterAction; + var formData = await gridView.GetFormStateDataAsync(); var filterHtml = new HtmlBuilder(HtmlTag.Div).WithId(Name); - bool isVisible = GridView.ExpressionsService.GetBoolValue(filterAction.VisibleExpression, formData); + var isVisible = gridView.ExpressionsService.GetBoolValue(filterAction.VisibleExpression, formData); if (!isVisible) return new HtmlBuilder(string.Empty); - if (GridView.FilterAction is { ShowAsCollapse: true, EnableScreenSearch: true }) + if (gridView.FilterAction is { ShowAsCollapse: true, EnableScreenSearch: true }) { - var collapse = await GetFilterScreenCollapse(); + var collapse = GetFilterScreenCollapse(); return filterHtml.AppendComponent(collapse); } return filterHtml.Append(await GetDefaultFilter()); } - /// - /// Recupera o filtro atual da grid - /// - /// - public async Task> GetCurrentFilterAsync() + public async ValueTask> GetCurrentFilterAsync() { if (_currentFilter != null) return _currentFilter; - - _currentFilter ??= new Dictionary(); - + + _currentFilter ??= new Dictionary(StringComparer.InvariantCultureIgnoreCase); + DataHelper.CopyIntoDictionary(_currentFilter, _userFilters); - + //Action is captured here, because the user can call GetCurrentFilterAsync before GetResultAsync() - var currentFilterAction = CurrentContext.Request.Form[$"grid-view-filter-action-{GridView.Name}"]; + var currentFilterAction = _currentContext.Request.Form[$"grid-view-filter-action-{gridView.Name}"]; switch (currentFilterAction) { case FilterActionName: @@ -82,32 +77,28 @@ public async Task> GetCurrentFilterAsync() await ApplyCurrentFilter(null); return _currentFilter; } - - var sessionFilter = CurrentContext.Session.GetSessionValue>( - $"jjcurrentfilter_{GridView.Name}"); - - if (sessionFilter != null && GridView.MaintainValuesOnLoad) + + var sessionFilter = _currentContext.Session.GetSessionValue>( + $"jjcurrentfilter_{gridView.Name}"); + + if (sessionFilter != null && gridView.MaintainValuesOnLoad) { DataHelper.CopyIntoDictionary(_currentFilter, sessionFilter); return _currentFilter; } - - if (sessionFilter != null && (CurrentContext.Request.Form.ContainsFormValues() || IsDynamicPost())) + + if (sessionFilter != null && (_currentContext.Request.Form.ContainsFormValues() || IsDynamicPost())) { DataHelper.CopyIntoDictionary(_currentFilter, sessionFilter); return _currentFilter; } - + await ApplyCurrentFilter(null); - + return _currentFilter ?? new Dictionary(); } - - /// - /// Recupera o filtro atual da grid - /// - /// + public void SetCurrentFilter(string name, object value) { _userFilters ??= new Dictionary(); @@ -116,20 +107,20 @@ public void SetCurrentFilter(string name, object value) private bool IsDynamicPost() { - return !string.IsNullOrEmpty(CurrentContext.Request.QueryString["routeContext"]); + return !string.IsNullOrEmpty(_currentContext.Request.QueryString["routeContext"]); } - - public async Task ApplyCurrentFilter(Dictionary values) + + public async ValueTask ApplyCurrentFilter(Dictionary values) { _currentFilter ??= new Dictionary(); if (values == null) { - values = GridView.RelationValues; + values = gridView.RelationValues; } else { - foreach (var r in GridView.RelationValues) + foreach (var r in gridView.RelationValues) { values[r.Key] = r.Value; } @@ -146,77 +137,78 @@ public async Task ApplyCurrentFilter(Dictionary values) } var defaultValues = - await GridView.FieldsService.MergeWithDefaultValuesAsync(GridView.FormElement, new FormStateData(values,GridView.UserValues, PageState.List)); - + await gridView.FieldValuesService.MergeWithDefaultValuesAsync(gridView.FormElement, + new FormStateData(values, gridView.UserValues, PageState.List)); + DataHelper.CopyIntoDictionary(values, defaultValues); DataHelper.CopyIntoDictionary(_currentFilter, values); - - CurrentContext.Session.SetSessionValue($"jjcurrentfilter_{GridView.Name}", _currentFilter); + + _currentContext.Session.SetSessionValue($"jjcurrentfilter_{gridView.Name}", _currentFilter); } - private async Task GetDefaultFilter() + private async ValueTask GetDefaultFilter() { - var action = GridView.FilterAction; - var fields = GridView.FormElement.Fields.ToList().FindAll( + var action = gridView.FilterAction; + var fields = gridView.FormElement.Fields.FindAll( field => field.Filter.Type != FilterMode.None && !field.VisibleExpression.Equals("val:0")); foreach (var field in fields) { - if (GridView.RelationValues.ContainsKey(field.Name)) + if (gridView.RelationValues.ContainsKey(field.Name)) field.EnableExpression = "val:0"; field.IsRequired = false; if (field.AutoPostBack) { - field.Attributes["onchange"] = GridView.Scripts.GetReloadFilterScript(); + field.Attributes["onchange"] = gridView.Scripts.GetReloadFilterScript(); } } if (fields.Count == 0) return new HtmlBuilder(string.Empty); - + var values = await GetCurrentFilterAsync(); if (OnFilterLoadAsync != null) - await OnFilterLoadAsync(GridView, new GridFilterLoadEventArgs { Filters = values }); + await OnFilterLoadAsync(gridView, new GridFilterLoadEventArgs { Filters = values }); - var dataPanelControl = new DataPanelControl(GridView, values) + var dataPanelControl = new DataPanelControl(gridView, values) { FieldNamePrefix = FilterFieldPrefix }; - - var htmlPanel = await dataPanelControl.GetHtmlForm(fields.ConvertAll(f=>f.DeepCopy())); - htmlPanel.WithAttribute("id", $"current-grid-filter-{GridView.Name}"); + + var htmlPanel = await dataPanelControl.GetHtmlForm(fields.ConvertAll(f => f.DeepCopy())); + htmlPanel.WithAttribute("id", $"current-grid-filter-{gridView.Name}"); var html = new HtmlBuilder(HtmlTag.Div) .WithAttribute("id", $"{Name}-body") - .AppendHiddenInput($"grid-view-filter-action-{GridView.Name}") + .AppendHiddenInput($"grid-view-filter-action-{gridView.Name}") .Append(htmlPanel); - var btnDoFilter = GridView.ComponentFactory.Html.LinkButton.Create(); - btnDoFilter.Enabled = GridView.EnableFilter; - btnDoFilter.Text = GridView.StringLocalizer["Filter"]; + var btnDoFilter = gridView.ComponentFactory.Html.LinkButton.Create(); + btnDoFilter.Enabled = gridView.EnableFilter; + btnDoFilter.Text = gridView.StringLocalizer["Filter"]; btnDoFilter.IconClass = "fa fa-search"; btnDoFilter.ShowAsButton = true; btnDoFilter.Type = LinkButtonType.Submit; - btnDoFilter.OnClientClick = $"{GridView.Scripts.GetFilterScript()};return false;"; + btnDoFilter.OnClientClick = $"{gridView.Scripts.GetFilterScript()};return false;"; - var btnCancel = GridView.ComponentFactory.Html.LinkButton.Create(); - btnCancel.Enabled = GridView.EnableFilter; - btnCancel.Text = GridView.StringLocalizer["Clear Filter"]; + var btnCancel = gridView.ComponentFactory.Html.LinkButton.Create(); + btnCancel.Enabled = gridView.EnableFilter; + btnCancel.Text = gridView.StringLocalizer["Clear Filter"]; btnCancel.IconClass = "fa fa-trash"; btnCancel.ShowAsButton = true; - btnCancel.OnClientClick = $"{GridView.Scripts.GetClearFilterScript()};return false;"; + btnCancel.OnClientClick = $"{gridView.Scripts.GetClearFilterScript()};return false;"; if (action.ShowAsCollapse) { - var panel = new JJCollapsePanel( GridView.CurrentContext.Request.Form) + var panel = new JJCollapsePanel(gridView.CurrentContext.Request.Form) { - Name = $"grid-view-filter-collapse-{GridView.Name}", + Name = $"grid-view-filter-collapse-{gridView.Name}", HtmlBuilderContent = html, TitleIcon = action.ShowIconAtCollapse ? new JJIcon(action.Icon) : null, - Title = GridView.StringLocalizer[action.Text] + Title = gridView.StringLocalizer[action.Text] }; panel.Buttons.Add(btnDoFilter); panel.Buttons.Add(btnCancel); @@ -228,16 +220,16 @@ private async Task GetDefaultFilter() { var modal = new JJModalDialog { - Name = $"{GridView.Name}-filter-modal" + Name = $"{gridView.Name}-filter-modal" }; - btnDoFilter.Attributes.Add(BootstrapHelper.Version >= 5 ? "data-bs-dismiss" : "data-dismiss","modal"); - btnCancel.Attributes.Add(BootstrapHelper.Version >= 5 ? "data-bs-dismiss" : "data-dismiss","modal"); + btnDoFilter.Attributes.Add(BootstrapHelper.Version >= 5 ? "data-bs-dismiss" : "data-dismiss", "modal"); + btnCancel.Attributes.Add(BootstrapHelper.Version >= 5 ? "data-bs-dismiss" : "data-dismiss", "modal"); modal.HtmlBuilderContent = html; - modal.Title = GridView.StringLocalizer["Detailed Filters"]; + modal.Title = gridView.StringLocalizer["Detailed Filters"]; modal.Buttons.Add(btnDoFilter); modal.Buttons.Add(btnCancel); - + html = modal.GetHtmlBuilder(); } @@ -246,99 +238,100 @@ private async Task GetDefaultFilter() } - private async Task GetFilterScreenCollapse() + private JJCollapsePanel GetFilterScreenCollapse() { - var body = new Div(); + var body = new HtmlBuilder(HtmlTag.Div); body.WithCssClass("col-sm-12"); - body.Append(await GetHtmlToolBarSearch(isToolBar:false)); - - var panel = new JJCollapsePanel( GridView.CurrentContext.Request.Form) + body.Append(GetHtmlToolBarSearch(isToolBar: false)); + + var panel = new JJCollapsePanel(gridView.CurrentContext.Request.Form) { - Name = $"filter_collapse_{GridView.Name}", + Name = $"filter_collapse_{gridView.Name}", HtmlBuilderContent = body, Title = "Filter", - ExpandedByDefault = GridView.FilterAction.ExpandedByDefault + ExpandedByDefault = gridView.FilterAction.ExpandedByDefault }; return panel; } - public async Task GetHtmlToolBarSearch(bool isToolBar = true) + public HtmlBuilder GetHtmlToolBarSearch(bool isToolBar = true) { - string searchId = $"jjsearch_{GridView.Name}"; + var searchId = $"jjsearch_{gridView.Name}"; - var textBox = new JJTextBox( GridView.CurrentContext.Request.Form) + var textBox = new JJTextBox(gridView.CurrentContext.Request.Form) { Attributes = { - { "onkeyup", $"GridViewFilterHelper.searchOnDOM('{GridView.Name}', this);" } + { "onkeyup", $"GridViewFilterHelper.searchOnDOM('{gridView.Name}', this);" } }, - Tooltip = StringLocalizer["Filter by any field visible in the list"], - PlaceHolder = StringLocalizer["Filter"], + Tooltip = _stringLocalizer["Filter by any field visible in the list"], + PlaceHolder = _stringLocalizer["Filter"], CssClass = "jj-icon-search", Name = searchId, - Text = CurrentContext.Request.Form[searchId] + Text = _currentContext.Request.Form[searchId] }; - + var html = new HtmlBuilder(); if (isToolBar) { - await html.AppendAsync(HtmlTag.Div, async div => + html.Append(HtmlTag.Div, div => { - div.WithCssClass($"{BootstrapHelper.PullRight}"); - await div.AppendControlAsync(textBox); + div.WithCssClass(BootstrapHelper.PullRight); + div.Append(textBox.GetHtmlBuilder()); }); } else { - await html.AppendAsync(HtmlTag.Div, async div => + html.Append(HtmlTag.Div, div => { div.WithCssClass(BootstrapHelper.FormGroup); div.WithCssClass("has-feedback jjsearch"); div.Append(HtmlTag.Label, label => { label.WithCssClass(BootstrapHelper.Label); - label.AppendText(StringLocalizer["Filter by any field visible in the list"]); + label.AppendText(_stringLocalizer["Filter by any field visible in the list"]); }); - await div.AppendControlAsync(textBox); + div.Append(textBox.GetHtmlBuilder()); }); } return html; } - private static object ParseFilterValue(FormElementField field,string value) + private static object ParseFilterValue(FormElementField field, string value) { var fieldType = field.DataType; var component = field.Component; if (component is FormComponent.Number && !string.IsNullOrEmpty(value)) return FormValuesService.HandleNumericComponent(fieldType, value); - + if (fieldType is FieldType.DateTime or FieldType.DateTime2 && component == FormComponent.Date) { - return DateTime.TryParse(value, out var dateTime) ? - $"{dateTime.ToShortDateString()} {DateTime.MaxValue.ToLongTimeString()}" : null; + return DateTime.TryParse(value, out var dateTime) + ? $"{dateTime.ToShortDateString()} {DateTime.MaxValue.ToLongTimeString()}" + : null; } - + return value; } - + private async Task> GetFilterFormValues() { - if (GridView.FormElement == null) - throw new NullReferenceException(nameof(GridView.FormElement)); + if (gridView.FormElement == null) + throw new NullReferenceException(nameof(gridView.FormElement)); //Relation Filters var values = new Dictionary(); - var filters = CurrentContext.Request.Form[$"grid-view-filters-{GridView.Name}"]; + var filters = _currentContext.Request.Form[$"grid-view-filters-{gridView.Name}"]; if (!string.IsNullOrEmpty(filters)) { - var filterJson = GridView.EncryptionService.DecryptStringWithUrlUnescape(filters); + var filterJson = gridView.EncryptionService.DecryptStringWithUrlUnescape(filters); values = JsonConvert.DeserializeObject>(filterJson)!; } - var fieldsFilter = GridView.FormElement.Fields.ToList().FindAll(x => x.Filter.Type != FilterMode.None); + var fieldsFilter = gridView.FormElement.Fields.FindAll(x => x.Filter.Type != FilterMode.None); foreach (var field in fieldsFilter) { @@ -346,23 +339,24 @@ private async Task> GetFilterFormValues() if (field.Filter.Type == FilterMode.Range) { - var fromStringValue = CurrentContext.Request.Form[$"{name}_from"]; + var fromStringValue = _currentContext.Request.Form[$"{name}_from"]; if (!string.IsNullOrEmpty(fromStringValue)) { - object fromValue = ParseFilterValue(field,fromStringValue); + var fromValue = ParseFilterValue(field, fromStringValue); values.Add($"{field.Name}_from", fromValue); } - var toStringValue = CurrentContext.Request.Form[$"{name}_to"]; - if (!string.IsNullOrEmpty(toStringValue)) - { - object toValue = ParseFilterValue(field, toStringValue); - values.Add($"{field.Name}_to", toValue); - } + var toStringValue = _currentContext.Request.Form[$"{name}_to"]; + + if (string.IsNullOrEmpty(toStringValue)) + continue; + + var toValue = ParseFilterValue(field, toStringValue); + values.Add($"{field.Name}_to", toValue); } else { - object value = CurrentContext.Request.Form[name]; + object value = _currentContext.Request.Form[name]; switch (field.Component) { @@ -376,13 +370,15 @@ private async Task> GetFilterFormValues() value = StringManager.ParseBool(value) ? "1" : "0"; break; case FormComponent.Search: - var search = (JJSearchBox)GridView.ComponentFactory.Controls.Create(GridView.FormElement,field, new(values,GridView.UserValues, PageState.Filter),Name,value); + var search = (JJSearchBox)gridView.ComponentFactory.Controls.Create(gridView.FormElement, field, + new(values, gridView.UserValues, PageState.Filter), Name, value); search.Name = name; search.AutoReloadFormFields = true; value = await search.GetSelectedValueAsync(); break; case FormComponent.Lookup: - var lookup = (JJLookup)GridView.ComponentFactory.Controls.Create(GridView.FormElement,field, new(values,GridView.UserValues, PageState.Filter),Name, value); + var lookup = (JJLookup)gridView.ComponentFactory.Controls.Create(gridView.FormElement, field, + new(values, gridView.UserValues, PageState.Filter), Name, value); lookup.Name = name; lookup.AutoReloadFormFields = true; value = lookup.SelectedValue?.ToString(); @@ -403,65 +399,64 @@ private async Task> GetFilterFormValues() } - private Dictionary GetFilterQueryString() + private Dictionary GetFilterQueryString() { - Dictionary values = null; - var fieldsFilter = GridView.FormElement.Fields.ToList().FindAll(x => x.Filter.Type != FilterMode.None); - foreach (var f in fieldsFilter) + Dictionary values = null; + var fieldsFilter = gridView.FormElement.Fields.FindAll(x => x.Filter.Type != FilterMode.None); + foreach (var filter in fieldsFilter) { - string name = $"{FilterFieldPrefix}{f.Name}"; + var name = $"{FilterFieldPrefix}{filter.Name}"; - if (f.Filter.Type == FilterMode.Range) + if (filter.Filter.Type == FilterMode.Range) { - string sfrom = CurrentContext.Request.QueryString[$"{name}_from"]; - if (values == null && sfrom != null) + var fromString = _currentContext.Request.QueryString[$"{name}_from"]; + if (values == null && fromString != null) values = new Dictionary(); - if (!string.IsNullOrEmpty(sfrom)) + if (!string.IsNullOrEmpty(fromString)) { - values.Add($"{f.Name}_from", sfrom); + values.Add($"{filter.Name}_from", fromString); } - string sto = CurrentContext.Request.QueryString[$"{name}_to"]; - if (!string.IsNullOrEmpty(sto)) + var toString = _currentContext.Request.QueryString[$"{name}_to"]; + if (!string.IsNullOrEmpty(toString)) { - values?.Add($"{f.Name}_to", sto); + values?.Add($"{filter.Name}_to", toString); } } else { - string val = CurrentContext.Request.QueryString[name]; - if (!string.IsNullOrEmpty(val)) - { - if (values == null) - values = new Dictionary(); + var queryStringValue = _currentContext.Request.QueryString[name]; - values.Add(f.Name, val); - } + if (string.IsNullOrEmpty(queryStringValue)) + continue; + values ??= new Dictionary(); + + values.Add(filter.Name, queryStringValue); } } return values; } - + public async Task HasFilter() { - if (GridView.FormElement == null) - throw new NullReferenceException(nameof(GridView.FormElement)); + if (gridView.FormElement == null) + throw new NullReferenceException(nameof(gridView.FormElement)); foreach (var item in await GetCurrentFilterAsync()) { if (string.IsNullOrEmpty(item.Value.ToString())) continue; - if (!GridView.FormElement.Fields.Contains(item.Key)) + if (!gridView.FormElement.Fields.Contains(item.Key)) continue; - var field = GridView.FormElement.Fields[item.Key]; + var field = gridView.FormElement.Fields[item.Key]; if (field.Filter.Type != FilterMode.None && !field.VisibleExpression.Equals("val:0")) return true; } return false; } -} +} \ No newline at end of file diff --git a/src/Core/UI/Components/GridView/GridPagination.cs b/src/Core/UI/Components/GridView/GridPagination.cs index a4ca54b57..f8320c41f 100644 --- a/src/Core/UI/Components/GridView/GridPagination.cs +++ b/src/Core/UI/Components/GridView/GridPagination.cs @@ -9,10 +9,9 @@ namespace JJMasterData.Core.UI.Components; -internal class GridPagination(JJGridView gridView) +internal sealed class GridPagination(JJGridView gridView) { - private JJGridView GridView { get; } = gridView; - private IStringLocalizer StringLocalizer { get; } = gridView.StringLocalizer; + private readonly IStringLocalizer _stringLocalizer = gridView.StringLocalizer; private int _totalPages; private int _totalButtons; private int _startButtonIndex; @@ -20,9 +19,9 @@ internal class GridPagination(JJGridView gridView) public HtmlBuilder GetHtmlBuilder() { - _totalPages = (int)Math.Ceiling(GridView.TotalOfRecords / (double)GridView.CurrentSettings.RecordsPerPage); - _totalButtons = GridView.CurrentSettings.TotalPaginationButtons; - _startButtonIndex = (int)Math.Floor((GridView.CurrentPage - 1) / (double)_totalButtons) * _totalButtons + 1; + _totalPages = (int)Math.Ceiling(gridView.TotalOfRecords / (double)gridView.CurrentSettings.RecordsPerPage); + _totalButtons = gridView.CurrentSettings.TotalPaginationButtons; + _startButtonIndex = (int)Math.Floor((gridView.CurrentPage - 1) / (double)_totalButtons) * _totalButtons + 1; _endButtonIndex = _startButtonIndex + _totalButtons; var html = new HtmlBuilder(HtmlTag.Div) .WithCssClassIf(BootstrapHelper.Version > 3, "container-fluid p-0") @@ -49,13 +48,13 @@ public HtmlBuilder GetHtmlBuilder() private HtmlBuilder GetPaginationHtmlBuilder() { - var ul = new Ul(); + var ul = new HtmlBuilder(HtmlTag.Ul); ul.WithCssClass("pagination"); if (_startButtonIndex > _totalButtons) { - ul.Append(GetPageButton(1, IconType.AngleDoubleLeft, StringLocalizer["First page"])); - ul.Append(GetPageButton(_startButtonIndex - 1, IconType.AngleLeft, StringLocalizer["Previous page"])); + ul.Append(GetPageButton(1, IconType.AngleDoubleLeft, _stringLocalizer["First page"])); + ul.Append(GetPageButton(_startButtonIndex - 1, IconType.AngleLeft, _stringLocalizer["Previous page"])); } for (int i = _startButtonIndex; i < _endButtonIndex; i++) @@ -69,8 +68,8 @@ private HtmlBuilder GetPaginationHtmlBuilder() if (_endButtonIndex <= _totalPages) { ul.Append(GetPageButton(_endButtonIndex, IconType.AngleRight, - StringLocalizer["Next page"])); - ul.Append(GetPageButton(_totalPages, IconType.AngleDoubleRight, StringLocalizer["Last page"])); + _stringLocalizer["Next page"])); + ul.Append(GetPageButton(_totalPages, IconType.AngleDoubleRight, _stringLocalizer["Last page"])); } var showJumpToPage = _endButtonIndex <= _totalPages || _startButtonIndex > _totalButtons; @@ -85,8 +84,8 @@ private HtmlBuilder GetPaginationHtmlBuilder() private IEnumerable GetJumpToPageButtons() { - var jumpToPageName = GridView.Name + "-jump-to-page-input"; - var textBox = GridView.ComponentFactory.Controls.TextGroup.Create(); + var jumpToPageName = gridView.Name + "-jump-to-page-input"; + var textBox = gridView.ComponentFactory.Controls.TextGroup.Create(); textBox.Name = jumpToPageName; textBox.MinValue = 1; @@ -95,24 +94,24 @@ private IEnumerable GetJumpToPageButtons() textBox.Attributes["style"] = "display:none;width:150px"; - textBox.Attributes["onfocusout"] = GridView.Scripts.GetJumpToPageScript(); - textBox.PlaceHolder = StringLocalizer["Jump to page..."]; + textBox.Attributes["onfocusout"] = gridView.Scripts.GetJumpToPageScript(); + textBox.PlaceHolder = _stringLocalizer["Jump to page..."]; textBox.CssClass += " pagination-jump-to-page-input"; - yield return new Li() + yield return new HtmlBuilder(HtmlTag.Li) .WithCssClass("page-item") .Append(textBox.GetHtmlBuilder()) .AppendDiv(div => { div.WithId(jumpToPageName + "-invalid-feedback"); div.WithCssClass("invalid-feedback"); - div.AppendText(StringLocalizer["Page must be between 1 and {0}.", _totalPages]); + div.AppendText(_stringLocalizer["Page must be between 1 and {0}.", _totalPages]); }); - yield return new Li() + yield return new HtmlBuilder(HtmlTag.Li) .WithCssClass("page-item") - .Append(new JJLinkButton(GridView.StringLocalizer) + .Append(new JJLinkButton(gridView.StringLocalizer) { ShowAsButton = false, Icon = IconType.SolidMagnifyingGlassArrowRight, @@ -123,15 +122,15 @@ private IEnumerable GetJumpToPageButtons() private HtmlBuilder GetPageButton(int page, IconType? icon = null, string? tooltip = null) { - var li = new Li() + var li = new HtmlBuilder(HtmlTag.Li) .WithCssClass("page-item") - .WithCssClassIf(page == GridView.CurrentPage, "active") + .WithCssClassIf(page == gridView.CurrentPage, "active") .Append(HtmlTag.A, a => { a.WithCssClass("page-link"); a.WithStyle( "cursor:pointer; cursor:hand;"); a.WithToolTip(tooltip); - a.WithOnClick( $"javascript:{GridView.Scripts.GetPaginationScript(page)}"); + a.WithOnClick( $"javascript:{gridView.Scripts.GetPaginationScript(page)}"); if (icon != null) { a.AppendComponent(new JJIcon(icon.Value)); @@ -147,68 +146,68 @@ private HtmlBuilder GetPageButton(int page, IconType? icon = null, string? toolt private HtmlBuilder GetTotalRecordsHtmlBuilder() { - var div = new Div(); - div.WithCssClass($"col-sm-3 {BootstrapHelper.TextRight}"); + var div = new HtmlBuilder(HtmlTag.Div); + div.WithCssClass($"col-sm-3 {BootstrapHelper.TextRight} text-muted"); div.Append(HtmlTag.Label, label => { - label.WithAttribute("id", $"infotext_{GridView.Name}"); + label.WithAttribute("id", $"infotext_{gridView.Name}"); label.WithCssClass("small"); - label.AppendText(StringLocalizer["Showing"]); + label.AppendText(_stringLocalizer["Showing"]); label.AppendText(" "); if (_totalPages <= 1) { label.Append(HtmlTag.Span, span => { - span.WithAttribute("id", $"{GridView.Name}_totrows"); - span.AppendText($" {GridView.TotalOfRecords:N0} "); - span.AppendText(StringLocalizer["record(s)"]); + span.WithAttribute("id", $"{gridView.Name}_totrows"); + span.AppendText($" {gridView.TotalOfRecords:N0} "); + span.AppendText(_stringLocalizer["record(s)"]); }); } else { - var firstPageNumber = GridView.CurrentSettings.RecordsPerPage * GridView.CurrentPage - - GridView.CurrentSettings.RecordsPerPage + 1; + var firstPageNumber = gridView.CurrentSettings.RecordsPerPage * gridView.CurrentPage - + gridView.CurrentSettings.RecordsPerPage + 1; var lastPageNumber = - GridView.CurrentSettings.RecordsPerPage * GridView.CurrentPage > GridView.TotalOfRecords - ? GridView.TotalOfRecords - : GridView.CurrentSettings.RecordsPerPage * GridView.CurrentPage; + gridView.CurrentSettings.RecordsPerPage * gridView.CurrentPage > gridView.TotalOfRecords + ? gridView.TotalOfRecords + : gridView.CurrentSettings.RecordsPerPage * gridView.CurrentPage; - label.AppendText($"{firstPageNumber}-{lastPageNumber} {StringLocalizer["From"]}"); + label.AppendText($"{firstPageNumber}-{lastPageNumber} {_stringLocalizer["From"]}"); label.Append(HtmlTag.Span, span => { - span.WithAttribute("id", $"{GridView.Name}_totrows"); - span.AppendText(StringLocalizer["{0} records", GridView.TotalOfRecords]); + span.WithAttribute("id", $"{gridView.Name}_totrows"); + span.AppendText(_stringLocalizer["{0} records", gridView.TotalOfRecords]); }); } label.Append(HtmlTag.Br); if (_endButtonIndex <= _totalPages) - label.AppendText(StringLocalizer["{0} pages", _totalPages]); + label.AppendText(_stringLocalizer["{0} pages", _totalPages]); }); - div.AppendIf(GridView.EnableMultiSelect, HtmlTag.Br); - div.AppendIf(GridView.EnableMultiSelect, GetEnableMultSelectTotalRecords); + div.AppendIf(gridView.EnableMultiSelect, HtmlTag.Br); + div.AppendIf(gridView.EnableMultiSelect, GetEnableMultSelectTotalRecords); return div; } private HtmlBuilder GetEnableMultSelectTotalRecords() { - var selectedValues = GridView.GetSelectedGridValues(); - string noRecordSelected = GridView.StringLocalizer["No record selected"]; - string oneRecordSelected = GridView.StringLocalizer["A selected record"]; - string multipleRecordsSelected = GridView.StringLocalizer["{0} selected records", selectedValues.Count]; + var selectedValues = gridView.GetSelectedGridValues(); + string noRecordSelected = gridView.StringLocalizer["No record selected"]; + string oneRecordSelected = gridView.StringLocalizer["A selected record"]; + string multipleRecordsSelected = gridView.StringLocalizer["{0} selected records", selectedValues.Count]; var span = new HtmlBuilder(HtmlTag.Span); span.WithCssClass("small"); - span.WithAttribute("id", $"selected-text-{GridView.Name}"); + span.WithAttribute("id", $"selected-text-{gridView.Name}"); span.WithAttribute("no-record-selected-label", noRecordSelected); span.WithAttribute("one-record-selected-label", oneRecordSelected); - span.WithAttribute("multiple-records-selected-label", StringLocalizer["{0} selected records"]); + span.WithAttribute("multiple-records-selected-label", _stringLocalizer["{0} selected records"]); if (selectedValues.Count == 0) { diff --git a/src/Core/UI/Components/GridView/GridScripts.cs b/src/Core/UI/Components/GridView/GridScripts.cs index 62d0fb7a8..7394a9ba8 100644 --- a/src/Core/UI/Components/GridView/GridScripts.cs +++ b/src/Core/UI/Components/GridView/GridScripts.cs @@ -10,7 +10,7 @@ namespace JJMasterData.Core.UI.Components; public class GridScripts(JJGridView gridView) { - private IEncryptionService EncryptionService => gridView.EncryptionService; + private readonly IEncryptionService _encryptionService = gridView.EncryptionService; public string GetSortingScript(string fieldName) { @@ -43,7 +43,7 @@ public string GetJumpToPageScript() private string GetEncryptedRouteContext(ComponentContext componentContext = ComponentContext.GridViewReload) { var routeContext = RouteContext.FromFormElement(gridView.FormElement, componentContext); - var encryptedRouteContext = EncryptionService.EncryptRouteContext(routeContext); + var encryptedRouteContext = _encryptionService.EncryptObject(routeContext); return encryptedRouteContext; } @@ -71,7 +71,7 @@ public string GetSelectAllScript() public string GetGridSettingsScript(ConfigAction action, Dictionary formValues) { var actionMap = new ActionMap(ActionSource.GridToolbar, gridView.FormElement, formValues, action.Name); - string encryptedActionMap = EncryptionService.EncryptActionMap(actionMap); + string encryptedActionMap = _encryptionService.EncryptObject(actionMap); var encryptedRouteContext = GetEncryptedRouteContext(); // language=JavaScript return $"GridViewHelper.setGridSettings('{gridView.Name}','{encryptedRouteContext}','{encryptedActionMap}');"; @@ -119,7 +119,7 @@ private string GetReloadRowScript(string fieldName, int gridViewRowIndex) { var componentName = gridView.Name; - var routeContext = EncryptionService.EncryptRouteContext(RouteContext.FromFormElement(gridView.FormElement,ComponentContext.GridViewRow)); + var routeContext = _encryptionService.EncryptObject(RouteContext.FromFormElement(gridView.FormElement,ComponentContext.GridViewRow)); //language=Javascript return $"GridViewHelper.reloadGridRow('{componentName}','{fieldName}',{gridViewRowIndex},'{routeContext}');"; diff --git a/src/Core/UI/Components/GridView/GridSettings.cs b/src/Core/UI/Components/GridView/GridSettings.cs index fd68ed54f..8fafc9e3a 100644 --- a/src/Core/UI/Components/GridView/GridSettings.cs +++ b/src/Core/UI/Components/GridView/GridSettings.cs @@ -14,7 +14,7 @@ public class GridSettings public int TotalPaginationButtons { get; set; } = 5; - public bool ShowBorder { get; set; } = false; + public bool ShowBorder { get; set; } public bool ShowRowStriped { get; set; } = true; diff --git a/src/Core/UI/Components/GridView/GridSettingsForm.cs b/src/Core/UI/Components/GridView/GridSettingsForm.cs index dc570f7ab..691488fd9 100644 --- a/src/Core/UI/Components/GridView/GridSettingsForm.cs +++ b/src/Core/UI/Components/GridView/GridSettingsForm.cs @@ -9,7 +9,7 @@ namespace JJMasterData.Core.UI.Components; /// /// Class responsible to render the UI on JJGridView /// -internal class GridSettingsForm( +internal sealed class GridSettingsForm( string name, IHttpContext currentContext, IStringLocalizer stringLocalizer) @@ -125,7 +125,7 @@ private HtmlBuilder GetShowBorderHtml(GridSettings gridSettings) private HtmlBuilder GetPaginationHtml(GridSettings gridSettings) { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("col-sm-6"); if (BootstrapHelper.Version is 3) @@ -148,7 +148,7 @@ private HtmlBuilder GetPaginationHtml(GridSettings gridSettings) private HtmlBuilder GetPaginationLabel() { - var label = new Label(); + var label = new HtmlBuilder(HtmlTag.Label); label.WithCssClass("form-label"); label.WithAttribute("for", _tableTotalPerPage); label.AppendText(stringLocalizer["Records per page"]); @@ -157,7 +157,7 @@ private HtmlBuilder GetPaginationLabel() private HtmlBuilder GetPaginationSelect(GridSettings gridSettings) { - var select = new Select() + var select = new HtmlBuilder(HtmlTag.Select) .WithCssClass("form-control form-select") .WithNameAndId(_tableTotalPerPage); diff --git a/src/Core/UI/Components/GridView/GridSortingConfig.cs b/src/Core/UI/Components/GridView/GridSortingConfig.cs index 65669f510..958594ecf 100644 --- a/src/Core/UI/Components/GridView/GridSortingConfig.cs +++ b/src/Core/UI/Components/GridView/GridSortingConfig.cs @@ -11,21 +11,21 @@ namespace JJMasterData.Core.UI.Components; -internal class GridSortingConfig(JJGridView gridView) +internal sealed class GridSortingConfig(JJGridView gridView) { - private FormElement FormElement { get; } = gridView.FormElement; - private IStringLocalizer StringLocalizer { get; } = gridView.StringLocalizer; - private ExpressionsService ExpressionsService { get; } = gridView.ExpressionsService; - private IComponentFactory ComponentFactory { get; } = gridView.ComponentFactory; - + private readonly FormElement _formElement = gridView.FormElement; + private readonly IStringLocalizer _stringLocalizer = gridView.StringLocalizer; + private readonly ExpressionsService _expressionsService = gridView.ExpressionsService; + private readonly IComponentFactory _componentFactory = gridView.ComponentFactory; + public async Task GetHtmlBuilderAsync() { - var dialog = ComponentFactory.Html.ModalDialog.Create(); + var dialog = _componentFactory.Html.ModalDialog.Create(); dialog.Name = $"{gridView.Name}-sort-modal"; - dialog.Title =StringLocalizer["Sort Fields"]; + dialog.Title = _stringLocalizer["Sort Fields"]; dialog.Size = ModalSize.Small; - var btnSort = ComponentFactory.Html.LinkButton.Create(); + var btnSort = _componentFactory.Html.LinkButton.Create(); btnSort.Name = $"btnsort_{gridView.Name}"; btnSort.IconClass = IconType.Check.GetCssClass(); btnSort.ShowAsButton = true; @@ -33,7 +33,7 @@ public async Task GetHtmlBuilderAsync() btnSort.OnClientClick = gridView.Scripts.GetSortMultItemsScript(); dialog.Buttons.Add(btnSort); - var btnCancel = ComponentFactory.Html.LinkButton.Create(); + var btnCancel = _componentFactory.Html.LinkButton.Create(); btnCancel.Text = "Cancel"; btnCancel.IconClass = IconType.Times.GetCssClass(); btnCancel.ShowAsButton = true; @@ -43,7 +43,7 @@ public async Task GetHtmlBuilderAsync() var htmlContent = new HtmlBuilder(HtmlTag.Div) .AppendComponent(new JJAlert { - Title =StringLocalizer["Drag and drop to change order."], + Title = _stringLocalizer["Drag and drop to change order."], Icon = IconType.SolidCircleInfo, Color = BootstrapColor.Info, ShowIcon = true, @@ -69,16 +69,13 @@ private HtmlBuilder GetHtmlHeader() { tr.Append(HtmlTag.Th, th => { - th.WithStyle( "width:50px"); + th.WithStyle("width:50px"); th.AppendText("#"); }); + tr.Append(HtmlTag.Th, th => { th.AppendText(gridView.StringLocalizer["Column"]); }); tr.Append(HtmlTag.Th, th => { - th.AppendText(gridView.StringLocalizer["Column"]); - }); - tr.Append(HtmlTag.Th, th => - { - th.WithStyle( "width:220px"); + th.WithStyle("width:220px"); th.AppendText(gridView.StringLocalizer["Order"]); }); }); @@ -93,9 +90,9 @@ private async Task GetHtmlBody() tbody.WithCssClass("ui-sortable jjsortable"); var sortList = GetSortList(); - var fieldsList = sortList.Select(sort => FormElement.Fields[sort.FieldName]).ToList(); + var fieldsList = sortList.ConvertAll(sort => _formElement.Fields[sort.FieldName]); - foreach (var item in FormElement.Fields) + foreach (var item in _formElement.Fields) { var f = fieldsList.Find(x => x.Name.Equals(item.Name)); if (f == null) @@ -107,19 +104,19 @@ private async Task GetHtmlBody() foreach (var field in fieldsList.Where( f => f.DataBehavior is FieldBehavior.Real && - ExpressionsService.GetBoolValue(f.VisibleExpression, formStateData))) + _expressionsService.GetBoolValue(f.VisibleExpression, formStateData))) { - var comboBox = ComponentFactory.Controls.ComboBox.Create(); + var comboBox = _componentFactory.Controls.ComboBox.Create(); comboBox.Name = $"{field.Name}_order"; comboBox.SelectedValue = "N"; comboBox.DataItem.ShowIcon = true; comboBox.DataItem.Items = [ - new("A", StringLocalizer["Ascendant"], IconType.SortAmountAsc, null), - new("D", StringLocalizer["Descendant"], IconType.SortAmountDesc, null), - new("N", StringLocalizer["No Order"], IconType.Genderless, null) + new("A", _stringLocalizer["Ascendant"], IconType.SortAmountAsc, null), + new("D", _stringLocalizer["Descendant"], IconType.SortAmountDesc, null), + new("N", _stringLocalizer["No Order"], IconType.Genderless, null) ]; - + var sort = sortList.Find(x => x.FieldName.Equals(field.Name)); if (sort != null) { @@ -130,14 +127,8 @@ await tbody.AppendAsync(HtmlTag.Tr, async tr => { tr.WithAttribute("id", field.Name); tr.WithCssClass("ui-sortable-handle"); - tr.Append(HtmlTag.Td, td => - { - td.AppendComponent(new JJIcon("fa fa-arrows")); - }); - tr.Append(HtmlTag.Td, td => - { - td.AppendText(StringLocalizer[field.LabelOrName]); - }); + tr.Append(HtmlTag.Td, td => td.AppendComponent(new JJIcon("fa fa-arrows"))); + tr.Append(HtmlTag.Td, td => td.AppendText(_stringLocalizer[field.LabelOrName])); await tr.AppendAsync(HtmlTag.Td, async td => { var comboHtml = await comboBox.GetHtmlBuilderAsync(); @@ -161,7 +152,8 @@ private List GetSortList() foreach (string order in orders) { var parValue = order.Split(' '); - var isDesc = parValue.Length > 1 && parValue[1].Trim().ToUpper().Equals("DESC"); + var isDesc = parValue.Length > 1 && + parValue[1].Trim().Equals("DESC", System.StringComparison.OrdinalIgnoreCase); var sort = new SortItem( parValue[0].Trim(), !isDesc diff --git a/src/Core/UI/Components/GridView/GridSqlCommandAction.cs b/src/Core/UI/Components/GridView/GridSqlCommandAction.cs index 0b951c7ce..b2a2177b2 100644 --- a/src/Core/UI/Components/GridView/GridSqlCommandAction.cs +++ b/src/Core/UI/Components/GridView/GridSqlCommandAction.cs @@ -7,15 +7,14 @@ using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.DataManager.Expressions.Providers; using JJMasterData.Core.DataManager.Models; using JJMasterData.Core.Logging; namespace JJMasterData.Core.UI.Components; -internal class GridSqlCommandAction(JJGridView gridView) +internal sealed class GridSqlCommandAction(JJGridView gridView) { - public async Task ExecuteSqlCommand(ActionMap map, SqlCommandAction sqlCommandAction) + public async Task ExecuteSqlCommand(ActionMap map, SqlCommandAction sqlCommandAction) { var messageFactory = gridView.ComponentFactory.Html.MessageBox; try @@ -26,7 +25,7 @@ public async Task ExecuteSqlCommand(ActionMap map, SqlCommandActio if (selectedRows.Count == 0) { string msg = gridView.StringLocalizer["No lines selected."]; - return messageFactory.Create(msg, MessageIcon.Warning); + return new RenderedComponentResult(messageFactory.Create(msg, MessageIcon.Warning).GetHtmlBuilder()); } await ExecuteOnList(sqlCommandAction, selectedRows); @@ -41,10 +40,13 @@ public async Task ExecuteSqlCommand(ActionMap map, SqlCommandActio { gridView.Logger.LogSqlActionException(ex, sqlCommandAction.SqlCommand); string msg = gridView.StringLocalizer[ExceptionManager.GetMessage(ex)]; - return messageFactory.Create(msg, MessageIcon.Error); + return new RenderedComponentResult(messageFactory.Create(msg, MessageIcon.Error).GetHtmlBuilder()); } - return null; + if (!string.IsNullOrEmpty(sqlCommandAction.RedirectUrl)) + return new RedirectComponentResult(sqlCommandAction.RedirectUrl); + + return EmptyComponentResult.Value; } private bool IsApplyOnList(ActionMap map, SqlCommandAction sqlCommandAction) @@ -73,13 +75,13 @@ private async Task ExecuteOnRecord(ActionMap map, SqlCommandAction sqlCommandAct { var formElement = gridView.FormElement; Dictionary formValues; - if (map.PkFieldValues.Any()) + if (map.PkFieldValues.Count > 0) { formValues = await gridView.EntityRepository.GetFieldsAsync(formElement, map.PkFieldValues); } else { - formValues = await gridView.FieldsService.GetDefaultValuesAsync(formElement, new FormStateData(new Dictionary(), PageState.List)); + formValues = await gridView.FieldValuesService.GetDefaultValuesAsync(formElement, new FormStateData(new Dictionary(), PageState.List)); } var formStateData = new FormStateData(formValues, gridView.UserValues, PageState.List); diff --git a/src/Core/UI/Components/GridView/GridTable.cs b/src/Core/UI/Components/GridView/GridTable.cs index 990fe0de6..78139c79d 100644 --- a/src/Core/UI/Components/GridView/GridTable.cs +++ b/src/Core/UI/Components/GridView/GridTable.cs @@ -4,7 +4,7 @@ namespace JJMasterData.Core.UI.Components; -internal class GridTable(JJGridView gridView) +internal sealed class GridTable(JJGridView gridView) { internal GridSettings Settings { get; } = gridView.CurrentSettings; @@ -12,11 +12,11 @@ internal class GridTable(JJGridView gridView) internal GridTableBody Body { get; } = new(gridView); - public async Task GetHtmlBuilder() + public async ValueTask GetHtmlBuilder() { - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClass("pt-1"); - div.WithCssClassIf(Settings.IsResponsive && !Settings.IsHeaderFixed, "table-responsive"); + div.WithCssClassIf(Settings is { IsResponsive: true, IsHeaderFixed: false }, "table-responsive"); var table = new HtmlBuilder(HtmlTag.Table); table.WithCssClass("table"); diff --git a/src/Core/UI/Components/GridView/GridTableBody.cs b/src/Core/UI/Components/GridView/GridTableBody.cs index 239dcb124..4b6103be6 100644 --- a/src/Core/UI/Components/GridView/GridTableBody.cs +++ b/src/Core/UI/Components/GridView/GridTableBody.cs @@ -17,32 +17,30 @@ namespace JJMasterData.Core.UI.Components; -internal class GridTableBody(JJGridView gridView) +internal sealed class GridTableBody(JJGridView gridView) { - private string Name { get; } = $"{gridView.Name}-table"; - private JJGridView GridView { get; } = gridView; - + private readonly string _name = $"{gridView.Name}-table"; public event AsyncEventHandler? OnRenderActionAsync; public event AsyncEventHandler? OnRenderCellAsync; public event AsyncEventHandler? OnRenderSelectedCellAsync; public event AsyncEventHandler? OnRenderRowAsync; - public async Task GetHtmlBuilderAsync() + public async ValueTask GetHtmlBuilderAsync() { var tbody = new HtmlBuilder(HtmlTag.Tbody); - tbody.WithAttribute("id", Name); + tbody.WithAttribute("id", _name); tbody.AppendRange(await GetRowsList()); return tbody; } - private async Task> GetRowsList() + private async ValueTask> GetRowsList() { List rows = []; - var dataSource = GridView.DataSource; + var dataSource = gridView.DataSource; for (var i = 0; i < dataSource?.Count; i++) { @@ -52,39 +50,41 @@ private async Task> GetRowsList() return rows; } - private async Task GetRowHtml(Dictionary row, int index) + private async ValueTask GetRowHtml(Dictionary row, int index) { var tr = new HtmlBuilder(HtmlTag.Tr); - var basicActions = GridView.FormElement.Options.GridTableActions.OrderBy(x => x.Order).ToList(); - var defaultAction = basicActions.Find(x => x.IsVisible && x.IsDefaultOption); + var basicActions = gridView.FormElement.Options.GridTableActions.OrderBy(x => x.Order).ToList(); + var defaultAction = basicActions.Find(x => x is { IsVisible: true, IsDefaultOption: true }); tr.WithAttribute("id", $"row{index}"); - var enableGridAction = !GridView.EnableEditMode && (defaultAction != null || GridView.EnableMultiSelect); + var enableGridAction = !gridView.EnableEditMode && (defaultAction != null || gridView.EnableMultiSelect); tr.WithCssClassIf(enableGridAction, "tr-hover-action"); tr.AppendRange(await GetTdHtmlList(row, index)); if (OnRenderRowAsync is not null) - await OnRenderRowAsync(GridView, new() + { + await OnRenderRowAsync(gridView, new() { HtmlBuilder = tr, RowValues = row }); - + } + return tr; } - internal async Task> GetTdHtmlList(Dictionary row, int index) + internal async ValueTask> GetTdHtmlList(Dictionary row, int index) { var values = await GetValues(row); - var formStateData = new FormStateData(values, GridView.UserValues, PageState.List); - var basicActions = GridView.FormElement.Options.GridTableActions.OrderBy(x => x.Order).ToList(); - var defaultAction = basicActions.FirstOrDefault(x => x is { IsVisible: true, IsDefaultOption: true }); + var formStateData = new FormStateData(values, gridView.UserValues, PageState.List); + var basicActions = gridView.FormElement.Options.GridTableActions.OrderBy(x => x.Order).ToList(); + var defaultAction = basicActions.Find(x => x is { IsVisible: true, IsDefaultOption: true }); var onClickScript = await GetOnClickScript(formStateData, defaultAction); var tdList = new List(); - if (GridView.EnableMultiSelect) + if (gridView.EnableMultiSelect) { var checkBox = await GetMultiSelectCheckbox(row, index, values); var td = new HtmlBuilder(HtmlTag.Td); @@ -92,7 +92,7 @@ internal async Task> GetTdHtmlList(Dictionary await td.AppendControlAsync(checkBox); - if (!GridView.EnableEditMode && onClickScript == string.Empty) + if (!gridView.EnableEditMode && onClickScript.Length == 0) { onClickScript = $"$('#{checkBox.Name}').not(':disabled').prop('checked',!$('#{checkBox.Name}').is(':checked')).change()"; @@ -101,28 +101,21 @@ internal async Task> GetTdHtmlList(Dictionary tdList.Add(td); } - foreach (var visibleFieldHtml in await GetVisibleFieldsHtmlList(row, index, values, onClickScript)) - { - tdList.Add(visibleFieldHtml); - } - - foreach (var actionHtml in await GetActionsHtmlListAsync(formStateData)) - { - tdList.Add(actionHtml); - } + tdList.AddRange(await GetVisibleFieldsHtmlList(row, index, values, onClickScript)); + tdList.AddRange(await GetActionsHtmlListAsync(formStateData)); return tdList; } - private async Task> GetVisibleFieldsHtmlList( + private async ValueTask> GetVisibleFieldsHtmlList( Dictionary row, int index, Dictionary values, string onClickScript) { List result = []; - var formStateData = new FormStateData(values, GridView.UserValues, PageState.List); - foreach (var field in await GridView.GetVisibleFieldsAsync()) + var formStateData = new FormStateData(values, gridView.UserValues, PageState.List); + foreach (var field in await gridView.GetVisibleFieldsAsync()) { var formattedValue = string.Empty; var stringValue = string.Empty; @@ -138,7 +131,7 @@ private async Task> GetVisibleFieldsHtmlList( td.WithAttributeIfNotEmpty("style", style); td.WithAttributeIfNotEmpty("onclick", onClickScript); - if (GridView.EnableEditMode && field.DataBehavior != FieldBehavior.ViewOnly) + if (gridView.EnableEditMode && field.DataBehavior != FieldBehavior.ViewOnly) { td.Append(await GetEditModeFieldHtml(field, formStateData, row, index, formattedValue)); } @@ -153,7 +146,7 @@ private async Task> GetVisibleFieldsHtmlList( return result; } - private async Task GetGridFieldHtml(FormElementField field, + private async ValueTask GetGridFieldHtml(FormElementField field, FormStateData formStateData, Dictionary row, string stringValue) @@ -171,9 +164,9 @@ private async Task GetGridFieldHtml(FormElementField field, } else if (field.DataFile is not null) { - var controlContext = new ControlContext(formStateData, Name, stringValue); - var controlFactory = GridView.ComponentFactory.Controls; - var textFile = controlFactory.Create(GridView.FormElement, field,controlContext); + var controlContext = new ControlContext(formStateData, _name, stringValue); + var controlFactory = gridView.ComponentFactory.Controls; + var textFile = controlFactory.Create(gridView.FormElement, field,controlContext); cell = textFile.GetButtonGroupHtml(); } else if (field.Component is FormComponent.Color && !string.IsNullOrEmpty(stringValue)) @@ -188,13 +181,13 @@ private async Task GetGridFieldHtml(FormElementField field, } else if (!string.IsNullOrEmpty(field.GridRenderingTemplate)) { - var replacedTemplate = ExpressionHelper.ReplaceExpression(field.GridRenderingTemplate!, formStateData.Values, field.EncodeHtml); + var replacedTemplate = await gridView.HtmlTemplateService.RenderTemplate(field.GridRenderingTemplate!, formStateData.Values); cell = new HtmlBuilder(replacedTemplate); } else { - var selector = new FormElementFieldSelector(GridView.FormElement, field.Name); - var gridValue = await GridView.FieldsService.FormatGridValueAsync(selector, formStateData); + var selector = new FormElementFieldSelector(gridView.FormElement, field.Name); + var gridValue = await gridView.FieldFormattingService.FormatGridValueAsync(selector, formStateData); var gridStringValue = gridValue?.Trim() ?? string.Empty; cell = new HtmlBuilder(gridStringValue); } @@ -219,14 +212,14 @@ private async Task GetGridFieldHtml(FormElementField field, private async Task GetDataItemIconCell(FormElementDataItem dataItem, FormStateData formStateData, string stringValue) { HtmlBuilder cell; - var dataQuery = new DataQuery(formStateData, GridView.FormElement.ConnectionId) + var dataQuery = new DataQuery(formStateData, gridView.FormElement.ConnectionId) { SearchId = stringValue }; - var dataItemValues = await GridView.DataItemService.GetValuesAsync(dataItem, dataQuery); - var dataItemValue = dataItemValues.FirstOrDefault(d => d.Id == stringValue); + var dataItemValues = await gridView.DataItemService.GetValuesAsync(dataItem, dataQuery); + var dataItemValue = dataItemValues.Find(d => d.Id == stringValue); - var tooltip = dataItem.GridBehavior is DataItemGridBehavior.Icon ? GridView.StringLocalizer[dataItemValue?.Description ?? string.Empty] : string.Empty; + var tooltip = dataItem.GridBehavior is DataItemGridBehavior.Icon ? gridView.StringLocalizer[dataItemValue?.Description ?? string.Empty] : string.Empty; if (dataItemValue != null) { @@ -250,7 +243,7 @@ private async Task GetDataItemIconCell(FormElementDataItem dataItem private static HtmlBuilder GetIconCell(IconType iconType, string? color = null, string? tooltip = null) { - var cell = new Div(); + var cell = new HtmlBuilder(HtmlTag.Div); var icon = new JJIcon(iconType, color); if (tooltip is not null) { @@ -261,27 +254,27 @@ private static HtmlBuilder GetIconCell(IconType iconType, string? color = null, return cell; } - private async Task GetEditModeFieldHtml( + private async ValueTask GetEditModeFieldHtml( FormElementField field, FormStateData formStateData, Dictionary row, int index, string? value) { - var name = GridView.GetFieldName(field.Name, formStateData.Values); - var hasError = GridView.Errors.ContainsKey(name); + var name = gridView.GetFieldName(field.Name, formStateData.Values); + var hasError = gridView.Errors.ContainsKey(name); - var div = new Div(); + var div = new HtmlBuilder(HtmlTag.Div); div.WithCssClassIf(hasError, BootstrapHelper.HasError); - var control = GridView.ComponentFactory.Controls.Create(GridView.FormElement, field, formStateData, name, value); + var control = gridView.ComponentFactory.Controls.Create(gridView.FormElement, field, formStateData, name, value); control.Name = name; control.Attributes.Add("gridViewRowIndex", index.ToString()); if(field.AutoPostBack) - control.Attributes.Add("onchange",GridView.Scripts.GetReloadRowScript(field,index)); + control.Attributes.Add("onchange",gridView.Scripts.GetReloadRowScript(field,index)); control.CssClass = field.Name; @@ -289,7 +282,7 @@ private async Task GetEditModeFieldHtml( { var args = new GridCellEventArgs { Field = field, DataRow = row, Sender = control }; - await OnRenderCellAsync(GridView, args); + await OnRenderCellAsync(gridView, args); if (args.HtmlResult is not null) { @@ -312,14 +305,11 @@ private async Task GetEditModeFieldHtml( public async Task> GetActionsHtmlListAsync(FormStateData formStateData) { List result = []; - var basicActions = GridView.GridTableActions.OrderBy(x => x.Order).ToList(); - var actionsWithoutGroup = basicActions.FindAll(x => x.IsVisible && !x.IsGroup); - var groupedActions = basicActions.FindAll(x => x.IsVisible && x.IsGroup); + var basicActions = gridView.GridTableActions.OrderBy(x => x.Order).ToList(); + var actionsWithoutGroup = basicActions.FindAll(x => x is { IsVisible: true, IsGroup: false }); + var groupedActions = basicActions.FindAll(x => x is { IsVisible: true, IsGroup: true }); - foreach (var action in await GetActionsWithoutGroupHtmlAsync(actionsWithoutGroup, formStateData)) - { - result.Add(action); - } + result.AddRange(await GetActionsWithoutGroupHtmlAsync(actionsWithoutGroup, formStateData)); if (groupedActions.Count > 0) { @@ -330,25 +320,25 @@ public async Task> GetActionsHtmlListAsync(FormStateData formS } - private async Task GetActionsGroupHtmlAsync(IEnumerable actions, + private async ValueTask GetActionsGroupHtmlAsync(List actions, FormStateData formStateData) { var td = new HtmlBuilder(HtmlTag.Td); td.WithCssClass("table-action"); - var btnGroup = GridView.ComponentFactory.Html.LinkButtonGroup.Create(); + var btnGroup = gridView.ComponentFactory.Html.LinkButtonGroup.Create(); - var factory = GridView.ComponentFactory.ActionButton; + var factory = gridView.ComponentFactory.ActionButton; - foreach (var groupedAction in actions.Where(a => a.IsGroup).ToList()) + foreach (var groupedAction in actions.FindAll(a => a.IsGroup)) { btnGroup.ShowAsButton = groupedAction.ShowAsButton; - var linkButton = factory.CreateGridTableButton(groupedAction, GridView, formStateData); + var linkButton = factory.CreateGridTableButton(groupedAction, gridView, formStateData); if (OnRenderActionAsync != null) { var args = new ActionEventArgs(groupedAction, linkButton, formStateData.Values); - await OnRenderActionAsync(GridView, args); + await OnRenderActionAsync(gridView, args); } btnGroup.Actions.Add(linkButton); @@ -359,21 +349,21 @@ private async Task GetActionsGroupHtmlAsync(IEnumerable> GetActionsWithoutGroupHtmlAsync( - IEnumerable actionsWithoutGroup, FormStateData formStateData) + private async ValueTask> GetActionsWithoutGroupHtmlAsync( + List actionsWithoutGroup, FormStateData formStateData) { - var factory = GridView.ComponentFactory.ActionButton; + var factory = gridView.ComponentFactory.ActionButton; List result = []; foreach (var action in actionsWithoutGroup) { var td = new HtmlBuilder(HtmlTag.Td); td.WithCssClass("table-action"); - var link = factory.CreateGridTableButton(action, GridView, formStateData); + var link = factory.CreateGridTableButton(action, gridView, formStateData); if (OnRenderActionAsync is not null) { var args = new ActionEventArgs(action, link, formStateData.Values); - await OnRenderActionAsync(GridView, args); + await OnRenderActionAsync(gridView, args); if (args.HtmlResult != null) { @@ -432,25 +422,25 @@ private static string GetTdStyle(FormElementField field) return string.Empty; } - private async Task GetMultiSelectCheckbox(Dictionary row, int index, + private async ValueTask GetMultiSelectCheckbox(Dictionary row, int index, Dictionary values) { - string pkValues = DataHelper.ParsePkValues(GridView.FormElement, values, ';'); + string pkValues = DataHelper.ParsePkValues(gridView.FormElement, values, ';'); var td = new HtmlBuilder(HtmlTag.Td); td.WithCssClass("jj-checkbox"); - var checkBox = new JJCheckBox(GridView.CurrentContext.Request.Form, GridView.StringLocalizer) + var checkBox = new JJCheckBox(gridView.CurrentContext.Request.Form, gridView.StringLocalizer) { Name = $"jjchk_{index}", - Value = GridView.EncryptionService.EncryptStringWithUrlEscape(pkValues), + Value = gridView.EncryptionService.EncryptStringWithUrlEscape(pkValues), Text = string.Empty, Attributes = { - ["onchange"] = $"GridViewSelectionHelper.selectItem('{GridView.Name}', this);" + ["onchange"] = $"GridViewSelectionHelper.selectItem('{gridView.Name}', this);" } }; - var selectedGridValues = GridView.GetSelectedGridValues(); + var selectedGridValues = gridView.GetSelectedGridValues(); checkBox.IsChecked = selectedGridValues.Any(x => x.Any(kvp => kvp.Value.Equals(pkValues))); @@ -462,7 +452,7 @@ private async Task GetMultiSelectCheckbox(Dictionary GetMultiSelectCheckbox(Dictionary GetOnClickScript(FormStateData formStateData, BasicAction? defaultAction) + private async ValueTask GetOnClickScript(FormStateData formStateData, BasicAction? defaultAction) { - if (GridView.EnableEditMode || defaultAction == null) + if (gridView.EnableEditMode || defaultAction == null) return string.Empty; - var factory = GridView.ComponentFactory.ActionButton; + var factory = gridView.ComponentFactory.ActionButton; - var actionButton = factory.CreateGridTableButton(defaultAction, GridView, formStateData); + var actionButton = factory.CreateGridTableButton(defaultAction, gridView, formStateData); if (OnRenderActionAsync != null) { var args = new ActionEventArgs(defaultAction, actionButton, formStateData.Values); - await OnRenderActionAsync(GridView, args); + await OnRenderActionAsync(gridView, args); if (args.HtmlResult != null) actionButton = null; @@ -502,13 +492,13 @@ private async Task GetOnClickScript(FormStateData formStateData, BasicAc return string.Empty; } - private async Task> GetValues(Dictionary row) + private async ValueTask> GetValues(Dictionary row) { - if (!GridView.EnableEditMode) + if (!gridView.EnableEditMode) return row; - var prefixName = GridView.GetFieldName(string.Empty, row); - return await GridView.FormValuesService.GetFormValuesWithMergedValuesAsync(GridView.FormElement, - new FormStateData(row, GridView.UserValues, PageState.List), GridView.AutoReloadFormFields, prefixName); + var prefixName = gridView.GetFieldName(string.Empty, row); + return await gridView.FormValuesService.GetFormValuesWithMergedValuesAsync(gridView.FormElement, + new FormStateData(row, gridView.UserValues, PageState.List), gridView.AutoReloadFormFields, prefixName); } } \ No newline at end of file diff --git a/src/Core/UI/Components/GridView/GridTableHeader.cs b/src/Core/UI/Components/GridView/GridTableHeader.cs index a548d0324..0464e9a40 100644 --- a/src/Core/UI/Components/GridView/GridTableHeader.cs +++ b/src/Core/UI/Components/GridView/GridTableHeader.cs @@ -10,25 +10,19 @@ namespace JJMasterData.Core.UI.Components; -internal class GridTableHeader +internal sealed class GridTableHeader(JJGridView gridView) { - private JJGridView GridView { get; } - private IStringLocalizer StringLocalizer { get; } - public GridTableHeader(JJGridView gridView) - { - GridView = gridView; - StringLocalizer = GridView.StringLocalizer; - } + private readonly IStringLocalizer _stringLocalizer = gridView.StringLocalizer; public async Task GetHtmlBuilderAsync() { var html = new HtmlBuilder(HtmlTag.Thead); - if (GridView.DataSource?.Count == 0 && !GridView.ShowHeaderWhenEmpty) + if (gridView.DataSource?.Count == 0 && !gridView.ShowHeaderWhenEmpty) return html; await html.AppendAsync(HtmlTag.Tr, async tr => { - tr.AppendIf(GridView.EnableMultiSelect, GetMultSelectThHtmlElement); + tr.AppendIf(gridView.EnableMultiSelect, GetMultSelectThHtmlElement); tr.AppendRange(await GetVisibleFieldsThList()); tr.AppendRange(GetActionsThList()); }); @@ -38,7 +32,7 @@ await html.AppendAsync(HtmlTag.Tr, async tr => private IEnumerable GetActionsThList() { - var basicActions = GridView.GridTableActions.OrderBy(x => x.Order).ToList(); + var basicActions = gridView.GridTableActions.OrderBy(x => x.Order).ToList(); var actions = basicActions.FindAll(x => x.IsVisible && !x.IsGroup); var actionsWithGroupCount = basicActions.Count(x => x.IsVisible && x.IsGroup); @@ -58,15 +52,15 @@ private IEnumerable GetActionsThList() private async Task> GetVisibleFieldsThList() { List thList = []; - foreach (var field in await GridView.GetVisibleFieldsAsync()) + foreach (var field in await gridView.GetVisibleFieldsAsync()) { var th = new HtmlBuilder(HtmlTag.Th); - string style = GetThStyle(field); + var style = GetThStyle(field); th.WithAttributeIfNotEmpty("style", style); th.Append(HtmlTag.Span, span => { - if (GridView.EnableSorting && field.DataBehavior is FieldBehavior.Real) + if (gridView.EnableSorting && field.DataBehavior is FieldBehavior.Real) { SetSortAttributes(span, field); } @@ -77,11 +71,11 @@ private async Task> GetVisibleFieldsThList() s.WithToolTip(field.HelpDescription); } - s.AppendText(StringLocalizer[field.LabelOrName]); + s.AppendText(_stringLocalizer[field.LabelOrName]); }); }); - var orderByString = GridView.CurrentOrder.ToQueryParameter(); + var orderByString = gridView.CurrentOrder.ToQueryParameter(); if (!string.IsNullOrEmpty(orderByString)) { foreach (var orderField in orderByString.Split(',')) @@ -98,7 +92,7 @@ private async Task> GetVisibleFieldsThList() if (order.Equals($"{field.Name} DESC")) th.Append(GetDescendingIcon()); - else if (order.Equals($"{field.Name} ASC") || order.Equals((string)field.Name)) + else if (order.Equals($"{field.Name} ASC") || order.Equals(field.Name)) th.Append(GetAscendingIcon()); } } @@ -110,13 +104,13 @@ private async Task> GetVisibleFieldsThList() th.Append(GetAscendingIcon()); } - var currentFilter = await GridView.GetCurrentFilterAsync(); + var currentFilter = await gridView.GetCurrentFilterAsync(); if (IsAppliedFilter(field, currentFilter)) { th.AppendText(" "); th.Append(new JJIcon("fa fa-filter").GetHtmlBuilder() - .WithToolTip(StringLocalizer["Applied filter"])); + .WithToolTip(_stringLocalizer["Applied filter"])); } thList.Add(th); @@ -136,14 +130,14 @@ private static bool IsAppliedFilter(ElementField field, Dictionary new JJIcon("fa fa-sort-amount-asc").GetHtmlBuilder() - .WithToolTip(StringLocalizer["Ascending order"]); + .WithToolTip(_stringLocalizer["Ascending order"]); private HtmlBuilder GetDescendingIcon() => new JJIcon("fa fa-sort-amount-desc").GetHtmlBuilder() - .WithToolTip(StringLocalizer["Descending order"]); + .WithToolTip(_stringLocalizer["Descending order"]); private static string GetThStyle(FormElementField field) { @@ -191,14 +185,14 @@ private HtmlBuilder GetMultSelectThHtmlElement() { var th = new HtmlBuilder(HtmlTag.Th); - bool hasPages = true; - if (!GridView.IsPagingEnabled()) + var hasPages = true; + if (!gridView.IsPagingEnabled()) { hasPages = false; } else { - int totalPages = (int)Math.Ceiling(GridView.TotalOfRecords / (double)GridView.CurrentSettings.RecordsPerPage); + var totalPages = (int)Math.Ceiling(gridView.TotalOfRecords / (double)gridView.CurrentSettings.RecordsPerPage); if (totalPages <= 1) hasPages = false; } @@ -207,9 +201,9 @@ private HtmlBuilder GetMultSelectThHtmlElement() .Append(HtmlTag.Input, input => { input.WithAttribute("type", "checkbox") - .WithNameAndId($"{GridView.Name}-checkbox-select-all-rows") + .WithNameAndId($"{gridView.Name}-checkbox-select-all-rows") .WithCssClass("form-check-input") - .WithToolTip(StringLocalizer["Mark|Unmark all from page"]) + .WithToolTip(_stringLocalizer["Mark|Unmark all from page"]) .WithOnClick( "GridViewSelectionHelper.selectAllAtSamePage(this)"); }); @@ -235,18 +229,18 @@ private HtmlBuilder GetMultSelectThHtmlElement() { a.WithCssClass("dropdown-item"); a.WithAttribute("href", "javascript:void(0);"); - a.WithOnClick( $"GridViewSelectionHelper.unSelectAll('{GridView.Name}')"); - a.AppendText(StringLocalizer["Unmark all selected records"]); + a.WithOnClick( $"GridViewSelectionHelper.unSelectAll('{gridView.Name}')"); + a.AppendText(_stringLocalizer["Unmark all selected records"]); }); }); - ul.AppendIf(GridView.TotalOfRecords <= 50000, HtmlTag.Li, li => + ul.AppendIf(gridView.TotalOfRecords <= 50000, HtmlTag.Li, li => { li.Append(HtmlTag.A, a => { a.WithCssClass("dropdown-item"); a.WithAttribute("href", "javascript:void(0);"); - a.WithOnClick( GridView.Scripts.GetSelectAllScript()); - a.AppendText(GridView.StringLocalizer["Mark all {0} records", GridView.TotalOfRecords]); + a.WithOnClick( gridView.Scripts.GetSelectAllScript()); + a.AppendText(gridView.StringLocalizer["Mark all {0} records", gridView.TotalOfRecords]); }); }); }); diff --git a/src/Core/UI/Components/GridView/GridToolbar.cs b/src/Core/UI/Components/GridView/GridToolbar.cs index ae080a328..463801668 100644 --- a/src/Core/UI/Components/GridView/GridToolbar.cs +++ b/src/Core/UI/Components/GridView/GridToolbar.cs @@ -7,11 +7,11 @@ namespace JJMasterData.Core.UI.Components; -internal class GridToolbar(JJGridView gridView) +internal sealed class GridToolbar(JJGridView gridView) { internal event AsyncEventHandler OnRenderToolbarActionAsync; - public async Task GetHtmlBuilderAsync() + public async ValueTask GetHtmlBuilderAsync() { var toolbar = new JJToolbar(); @@ -20,7 +20,7 @@ public async Task GetHtmlBuilderAsync() return toolbar.GetHtmlBuilder().WithCssClass("mb-1"); } - private async Task AddActionsToToolbar(JJToolbar toolbar) + private async ValueTask AddActionsToToolbar(JJToolbar toolbar) { var actions = gridView.ToolbarActions .OrderBy(a => a.Order); @@ -44,7 +44,7 @@ private async Task AddActionsToToolbar(JJToolbar toolbar) case InsertAction { ShowOpenedAtGrid: true }: continue; case FilterAction { EnableScreenSearch: true }: - toolbar.Items.Add(await gridView.Filter.GetHtmlToolBarSearch()); + toolbar.Items.Add(gridView.Filter.GetHtmlToolBarSearch()); continue; } @@ -81,7 +81,7 @@ private async Task AddActionsToToolbar(JJToolbar toolbar) groupedAction.Actions.Add(linkButton); } - if (groupedAction.Actions.Any()) + if (groupedAction.Actions.Count > 0) toolbar.Items.Add(groupedAction.GetHtmlBuilder()); } diff --git a/src/Core/UI/Components/GridView/GridViewFactory.cs b/src/Core/UI/Components/GridView/GridViewFactory.cs index b73dcf490..8a0dab78a 100644 --- a/src/Core/UI/Components/GridView/GridViewFactory.cs +++ b/src/Core/UI/Components/GridView/GridViewFactory.cs @@ -16,17 +16,20 @@ namespace JJMasterData.Core.UI.Components; -internal class GridViewFactory(IHttpContext currentContext, +internal sealed class GridViewFactory(IHttpContext currentContext, IEntityRepository entityRepository, IDataDictionaryRepository dataDictionaryRepository, IEncryptionService encryptionService, DataItemService dataItemService, ExpressionsService expressionsService, - FieldsService fieldsService, + FieldFormattingService fieldFormattingService, + FieldValuesService fieldValuesService, + FieldValidationService fieldValidationService, FormValuesService formValuesService, IStringLocalizer stringLocalizer, IGridEventHandlerResolver gridEventHandlerResolver, UrlRedirectService urlRedirectService, + HtmlTemplateService htmlTemplateService, ILoggerFactory loggerFactory, IComponentFactory componentFactory) : IFormElementComponentFactory @@ -40,10 +43,13 @@ public JJGridView Create(FormElement formElement) encryptionService, dataItemService, expressionsService, - fieldsService, formValuesService, + fieldFormattingService, + fieldValuesService, + fieldValidationService, stringLocalizer, urlRedirectService, + htmlTemplateService, loggerFactory.CreateLogger(), componentFactory); @@ -75,7 +81,7 @@ public JJGridView Create(DataTable dataTable) return gridView; } - public async Task CreateAsync(string elementName) + public async ValueTask CreateAsync(string elementName) { var formElement = await dataDictionaryRepository.GetFormElementAsync(elementName); @@ -102,7 +108,7 @@ internal void SetGridUiOptions(JJGridView grid, GridUI gridOptions) grid.EnableSorting = gridOptions.EnableSorting; grid.EnableMultiSelect = gridOptions.EnableMultiSelect; grid.MaintainValuesOnLoad = gridOptions.MaintainValuesOnLoad; - grid.ShowPagging = gridOptions.ShowPagging; + grid.ShowPaging = gridOptions.ShowPagging; grid.ShowToolbar = gridOptions.ShowToolBar; if (!grid.GridSettingsForm.HasFormValues() || !grid.ShowToolbar || !grid.ConfigAction.IsVisible) diff --git a/src/Core/UI/Components/GridView/JJGridView.cs b/src/Core/UI/Components/GridView/JJGridView.cs index 52d07aa63..0ef59af07 100644 --- a/src/Core/UI/Components/GridView/JJGridView.cs +++ b/src/Core/UI/Components/GridView/JJGridView.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Web; @@ -26,7 +27,7 @@ using JJMasterData.Core.Extensions; using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.Logging; -using JJMasterData.Core.Tasks; +using JJMasterData.Core.UI.Events; using JJMasterData.Core.UI.Events.Args; using JJMasterData.Core.UI.Html; using JJMasterData.Core.UI.Routing; @@ -68,7 +69,7 @@ public class JJGridView : AsyncComponent /// 3) If the OnDataLoad action is not implemented, try to retrieve /// using the proc informed in the FormElement; /// - public event AsyncEventHandler? OnDataLoadAsync; + public event GridDataLoadEventHandler? OnDataLoadAsync; public event AsyncEventHandler? OnRenderActionAsync; public event AsyncEventHandler? OnFilterLoadAsync; public event AsyncEventHandler? OnRenderToolbarActionAsync; @@ -87,7 +88,7 @@ public class JJGridView : AsyncComponent private ExportOptions? _currentExportConfig; private GridFilter? _filter; private GridTable? _table; - private IList>? _dataSource; + private List>? _dataSource; private List? _pkFields; private Dictionary? _defaultValues; private FormStateData? _formStateData; @@ -143,13 +144,13 @@ private List PrimaryKeyFields if (FormElement == null) throw new ArgumentNullException(nameof(FormElement)); - _pkFields = FormElement.Fields.ToList().FindAll(x => x.IsPk); + _pkFields = FormElement.Fields.FindAll(x => x.IsPk); return _pkFields; } } - internal async Task> GetVisibleFieldsAsync() + internal async ValueTask> GetVisibleFieldsAsync() { if (FormElement == null) throw new ArgumentNullException(nameof(FormElement)); @@ -173,8 +174,6 @@ private static bool VisibleAtGrid(FormElementField field) return field.DataBehavior is not FieldBehavior.WriteOnly && field.DataBehavior is not FieldBehavior.Virtual; } - internal FormValuesService FormValuesService { get; } - /// /// /// @@ -186,7 +185,7 @@ private static bool VisibleAtGrid(FormElementField field) /// 2) If the DataSource property is null, try to execute the OnDataLoad action; /// 3) If the OnDataLoad action is not implemented, try to retrieve /// Using the stored procedure informed in the FormElement; - public IList>? DataSource + public List>? DataSource { get => _dataSource; set @@ -284,10 +283,10 @@ public int CurrentPage } else { - object tablePage = CurrentContext.Session[$"jjcurrentpage_{Name}"]; + var tablePage = CurrentContext.Session[$"jjcurrentpage_{Name}"]; if (tablePage != null) { - if (int.TryParse(tablePage.ToString(), out var page)) + if (int.TryParse(tablePage, out var page)) currentPage = page; } } @@ -299,10 +298,10 @@ public int CurrentPage int page = 1; if (MaintainValuesOnLoad) { - object tablePage = CurrentContext.Session[$"jjcurrentpage_{Name}"]; + var tablePage = CurrentContext.Session[$"jjcurrentpage_{Name}"]; if (tablePage != null) { - if (int.TryParse(tablePage.ToString(), out var nAuxPage)) + if (int.TryParse(tablePage, out var nAuxPage)) page = nAuxPage; } } @@ -333,8 +332,8 @@ public GridSettings CurrentSettings if (_currentSettings != null) return _currentSettings; - var action = CurrentActionMap?.GetAction(FormElement); - if (action is ConfigAction) + var action = CurrentActionMap?.GetAction(FormElement); + if (action is not null) { CurrentSettings = GridSettingsForm.LoadFromForm(); return _currentSettings!; @@ -457,7 +456,7 @@ public ExportOptions CurrentExportConfig /// /// If the TotalRecords property is equal to zero, pagination will not be displayed. /// - public bool ShowPagging { get; set; } + public bool ShowPaging { get; set; } /// /// Key-Value pairs with the errors. @@ -514,6 +513,7 @@ private ActionMap? CurrentActionMap _currentActionMap = EncryptionService.DecryptActionMap(encryptedActionMap); return _currentActionMap; } + set => _currentActionMap = value; } private string? SelectedRowsId @@ -522,8 +522,6 @@ private string? SelectedRowsId set => _selectedRowsId = value ?? ""; } - - protected RouteContext RouteContext { get @@ -544,11 +542,13 @@ protected RouteContext RouteContext #endregion #region Injected Services - internal FieldsService FieldsService { get; } internal ExpressionsService ExpressionsService { get; } - + internal FormValuesService FormValuesService { get; } + internal FieldValuesService FieldValuesService { get; } + internal FieldValidationService FieldValidationService { get; } internal IStringLocalizer StringLocalizer { get; } private UrlRedirectService UrlRedirectService { get; } + internal HtmlTemplateService HtmlTemplateService { get; } internal IComponentFactory ComponentFactory { get; } internal IEntityRepository EntityRepository { get; } @@ -573,6 +573,7 @@ private GridToolbar Toolbar } internal ILogger Logger { get; } + internal FieldFormattingService FieldFormattingService { get; } #endregion @@ -585,37 +586,43 @@ internal JJGridView( IEncryptionService encryptionService, DataItemService dataItemService, ExpressionsService expressionsService, - FieldsService fieldsService, FormValuesService formValuesService, + FieldFormattingService fieldFormattingService, + FieldValuesService fieldValuesService, + FieldValidationService fieldValidationService, IStringLocalizer stringLocalizer, UrlRedirectService urlRedirectService, + HtmlTemplateService htmlTemplateService, ILogger logger, IComponentFactory componentFactory) { - Name = $"{ComponentNameGenerator.Create(formElement.Name)}"; + Name = formElement.Name.ToLowerInvariant(); FormElement = formElement; ShowTitle = formElement.Options.Grid.ShowTitle; EnableFilter = true; EnableSorting = formElement.Options.Grid.EnableSorting; + FieldFormattingService = fieldFormattingService; ShowHeaderWhenEmpty = formElement.Options.Grid.ShowHeaderWhenEmpty; - ShowPagging = formElement.Options.Grid.ShowPagging; + ShowPaging = formElement.Options.Grid.ShowPagging; ShowToolbar = formElement.Options.Grid.ShowToolBar; EmptyDataText = formElement.Options.Grid.EmptyDataText; AutoReloadFormFields = true; RelationValues = new Dictionary(); TitleSize = formElement.TitleSize; - - FieldsService = fieldsService; + ExpressionsService = expressionsService; EncryptionService = encryptionService; StringLocalizer = stringLocalizer; UrlRedirectService = urlRedirectService; + HtmlTemplateService = htmlTemplateService; Logger = logger; ComponentFactory = componentFactory; EntityRepository = entityRepository; CurrentContext = currentContext; DataItemService = dataItemService; FormValuesService = formValuesService; + FieldValuesService = fieldValuesService; + FieldValidationService = fieldValidationService; } #endregion @@ -623,7 +630,7 @@ internal JJGridView( protected override async Task BuildResultAsync() { if (!RouteContext.CanRender(FormElement)) - return new EmptyComponentResult(); + return EmptyComponentResult.Value; if (ComponentContext is ComponentContext.GridViewReload) return new ContentComponentResult(await GetTableHtmlBuilder()); @@ -673,46 +680,73 @@ protected override async Task BuildResultAsync() { return await UrlRedirectService.GetUrlRedirectResult(this,CurrentActionMap); } + + HtmlBuilder? sqlActionError = null; + + if (TryGetSqlAction(out var sqlCommandAction)) + { + var sqlResult = await ExecuteSqlCommand(sqlCommandAction); + if (sqlResult == EmptyComponentResult.Value) + CurrentActionMap = null; + else if (sqlResult is RedirectComponentResult redirectResult) + { + return redirectResult; + } + else if(sqlResult is RenderedComponentResult result) + { + sqlActionError = result.HtmlBuilder; + } + } + + var gridHtml = await GetHtmlBuilderAsync(); + + if (sqlActionError is not null) + { + gridHtml.Append(sqlActionError); + } - return new RenderedComponentResult(await GetHtmlBuilderAsync()); + return new RenderedComponentResult(gridHtml); } internal async Task GetHtmlBuilderAsync() { - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); html.WithAttribute("id", Name); await html.AppendAsync(HtmlTag.Div, async div => { if (ShowTitle) - div.AppendComponent(await GetTitleAsync()); + div.AppendComponent(GetTitle()); if (FilterAction.IsVisible) div.Append(await Filter.GetFilterHtml()); if (OnBeforeTableRenderAsync is not null) + { await OnBeforeTableRenderAsync(this, new() { HtmlBuilder = div }); - + } + if (ShowToolbar) div.Append(await GetToolbarHtmlBuilder()); div.Append(await GetTableHtmlBuilder()); if (OnAfterTableRenderAsync is not null) + { await OnAfterTableRenderAsync(this, new() { HtmlBuilder = div }); - + } }); return html; } - public Task> GetCurrentFilterAsync() + public ValueTask> GetCurrentFilterAsync() { return Filter.GetCurrentFilterAsync(); } @@ -728,19 +762,8 @@ public void SetCurrentFilter(string key, object value) private async Task GetTableHtmlBuilder() { AssertProperties(); - - string? currentAction = CurrentContext.Request[$"grid-view-action-map-{Name}"]; - - var html = new Div(); - - if (CheckForSqlCommand()) - { - var errorMessage = await ExecuteSqlCommand(); - if (errorMessage == null) - currentAction = null; - else - html.AppendComponent(errorMessage); - } + + var html = new HtmlBuilder(HtmlTag.Div); await SetDataSource(); @@ -751,7 +774,7 @@ private async Task GetTableHtmlBuilder() if (SortAction.IsVisible) await html.AppendAsync(GetSortingConfigAsync); - html.AppendRange(GetHiddenInputs(currentAction)); + html.AppendRange(GetHiddenInputs()); if (CurrentPage <= 0) { @@ -782,10 +805,10 @@ private async Task GetTableHtmlBuilder() html.Append(await GetExportHtml()); - html.Append(await GetLegendHtml()); + html.Append(await GetCaptionHtml()); } - html.Append(HtmlTag.Div, div => { div.WithCssClass("clearfix"); }); + html.Append(HtmlTag.Div, div => div.WithCssClass("clearfix")); return html; } @@ -803,11 +826,11 @@ private JJAlert GetPaginationWarningAlert(int totalPages) }; } - private IEnumerable GetHiddenInputs(string? currentAction) + private IEnumerable GetHiddenInputs() { yield return new HtmlBuilder().AppendHiddenInput($"grid-view-order-{Name}", CurrentOrder.ToQueryParameter() ?? string.Empty); yield return new HtmlBuilder().AppendHiddenInput($"grid-view-page-{Name}", CurrentPage.ToString()); - yield return new HtmlBuilder().AppendHiddenInput($"grid-view-action-map-{Name}", currentAction ?? string.Empty); + yield return new HtmlBuilder().AppendHiddenInput($"grid-view-action-map-{Name}", EncryptionService.EncryptObject(CurrentActionMap) ?? string.Empty); yield return new HtmlBuilder().AppendHiddenInput($"grid-view-row-{Name}", string.Empty); if (EnableMultiSelect) @@ -827,46 +850,46 @@ internal async Task GetTableRowHtmlAsync(int rowIndex) return result; } - [Obsolete("Please use GetTitleHtmlAsync")] public string GetTitleHtml() { - return AsyncHelper.RunSync(GetTitleAsync).GetHtml(); + return GetTitle().GetHtml(); } - public async Task GetTitleHtmlAsync() + internal JJTitle GetTitle() { - return (await GetTitleAsync()).GetHtml(); - } - - internal async Task GetTitleAsync() - { - return ComponentFactory.Html.Title.Create(FormElement, await GetFormStateDataAsync(), TitleActions); + return ComponentFactory.Html.Title.Create(FormElement, new FormStateData(RelationValues!,UserValues, PageState.List), TitleActions); } - internal Task GetToolbarHtmlBuilder() => Toolbar.GetHtmlBuilderAsync(); + internal ValueTask GetToolbarHtmlBuilder() => Toolbar.GetHtmlBuilderAsync(); - public Task GetFilterHtmlAsync() => Filter.GetFilterHtml(); + public ValueTask GetFilterHtmlAsync() => Filter.GetFilterHtml(); - public Task GetToolbarHtmlAsync() => GetToolbarHtmlBuilder(); + public ValueTask GetToolbarHtmlAsync() => GetToolbarHtmlBuilder(); private Task GetSortingConfigAsync() => new GridSortingConfig(this).GetHtmlBuilderAsync(); - private bool CheckForSqlCommand() + private bool TryGetSqlAction(out SqlCommandAction? sqlCommandAction) { var action = CurrentActionMap?.GetAction(FormElement); - return action is SqlCommandAction; + if (action is SqlCommandAction sqlAction) + { + sqlCommandAction = sqlAction; + return true; + } + + sqlCommandAction = null; + + return false; } - private Task ExecuteSqlCommand() + private Task ExecuteSqlCommand(SqlCommandAction? action) { - var action = CurrentActionMap!.GetAction(FormElement); - if (action is null) throw new JJMasterDataException("Action not found at your FormElement"); var gridSqlAction = new GridSqlCommandAction(this); - return gridSqlAction.ExecuteSqlCommand(CurrentActionMap, (SqlCommandAction)action); + return gridSqlAction.ExecuteSqlCommand(CurrentActionMap, action); } private void AssertProperties() @@ -899,14 +922,14 @@ private async Task GetNoRecordsAlert() return alert.GetHtmlBuilder(); } - internal async Task> GetDefaultValuesAsync() => _defaultValues ??= - await FieldsService.GetDefaultValuesAsync(FormElement, new FormStateData(new Dictionary(),UserValues, PageState.List)); + internal async ValueTask> GetDefaultValuesAsync() => _defaultValues ??= + await FieldValuesService.GetDefaultValuesAsync(FormElement, new FormStateData(new Dictionary(),UserValues, PageState.List)); - internal async Task GetFormStateDataAsync() + internal async ValueTask GetFormStateDataAsync() { if (_formStateData == null) { - var defaultValues = await FieldsService.GetDefaultValuesAsync(FormElement, new FormStateData(new Dictionary(RelationValues!),UserValues, PageState.List)); + var defaultValues = await FieldValuesService.GetDefaultValuesAsync(FormElement, new FormStateData(new Dictionary(RelationValues!),UserValues, PageState.List)); var userValues = new Dictionary(StringComparer.OrdinalIgnoreCase); DataHelper.CopyIntoDictionary(userValues, RelationValues!, true); @@ -953,7 +976,7 @@ private async Task GetSettingsHtml() private bool CanCustomPaging() => IsPagingEnabled() && CurrentSettings.RecordsPerPage % 5 == 0 && CurrentSettings.RecordsPerPage <= 50; - private async Task GetExportHtml() + private async ValueTask GetExportHtml() { var action = ExportAction; var formData = await GetFormStateDataAsync(); @@ -970,7 +993,7 @@ private async Task GetExportHtml() return modal.GetHtmlBuilder(); } - private async Task GetLegendHtml() + private async Task GetCaptionHtml() { var action = LegendAction; var formData = await GetFormStateDataAsync(); @@ -979,14 +1002,14 @@ private async Task GetLegendHtml() if (!isVisible) return new HtmlBuilder(string.Empty); - var legend = new GridCaptionView(action.Tooltip,ComponentFactory.Controls.ComboBox, StringLocalizer) + var captionView = new GridCaptionView(action.Tooltip,ComponentFactory.Controls.ComboBox, StringLocalizer) { Name = Name, ShowAsModal = true, FormElement = FormElement }; - return await legend.GetHtmlBuilderAsync(); + return await captionView.GetHtmlBuilderAsync(); } internal string GetFieldName(string fieldName, Dictionary row) @@ -1052,20 +1075,18 @@ private async Task GetExportationResult() var exportationResult = await DataExportation.ExecuteExportationAsync(result); return exportationResult; } - else + + try { - try - { - await ExportFileInBackground(); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error executing DataExportation."); - var errorMessage = StringLocalizer[ExceptionManager.GetMessage(ex)]; - var validationSummary =ComponentFactory.Html.ValidationSummary.Create(errorMessage); - validationSummary.MessageTitle = StringLocalizer["Error"]; - return new ContentComponentResult(validationSummary.GetHtmlBuilder()); - } + await ExportFileInBackground(); + } + catch (Exception ex) + { + Logger.LogError(ex, "Error executing DataExportation."); + var errorMessage = StringLocalizer[ExceptionManager.GetMessage(ex)]; + var validationSummary =ComponentFactory.Html.ValidationSummary.Create(errorMessage); + validationSummary.MessageTitle = StringLocalizer["Error"]; + return new ContentComponentResult(validationSummary.GetHtmlBuilder()); } var html = new DataExportationLog(DataExportation).GetLoadingHtml(); @@ -1081,10 +1102,10 @@ private async Task GetExportationResult() return new JsonComponentResult(new {}); } - return new EmptyComponentResult(); + return EmptyComponentResult.Value; } - public async Task ExportFileInBackground() + public async ValueTask ExportFileInBackground() { DataExportation.ExportFileInBackground(await GetCurrentFilterAsync(), CurrentOrder); } @@ -1303,7 +1324,7 @@ public Dictionary ValidateGridFields(List ValidateGridFields(List FormElement.Options.Grid = options; public bool IsExportPost() { return "startProcess".Equals(CurrentContext.Request["dataExportationOperation"]) && Name.Equals(CurrentContext.Request["gridViewName"]); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool HasAction() => CurrentActionMap is not null; + public ActionContext GetActionContext(BasicAction basicAction, FormStateData formStateData) { return new ActionContext @@ -1468,7 +1489,6 @@ public ActionContext GetActionContext(BasicAction basicAction, FormStateData for Action = basicAction, FormElement = FormElement, FormStateData = formStateData, - IsSubmit = basicAction is ISubmittableAction { IsSubmit: true }, ParentComponentName = Name }; } diff --git a/src/Core/UI/Components/Html/Alert/AlertFactory.cs b/src/Core/UI/Components/Html/Alert/AlertFactory.cs index 47b8190f6..562567d40 100644 --- a/src/Core/UI/Components/Html/Alert/AlertFactory.cs +++ b/src/Core/UI/Components/Html/Alert/AlertFactory.cs @@ -2,8 +2,5 @@ namespace JJMasterData.Core.UI.Components; public class AlertFactory : IComponentFactory { - public JJAlert Create() - { - return new JJAlert(); - } + public JJAlert Create() => new(); } \ No newline at end of file diff --git a/src/Core/UI/Components/Html/Alert/JJAlert.cs b/src/Core/UI/Components/Html/Alert/JJAlert.cs index 492ffcfe0..5a4aba03a 100644 --- a/src/Core/UI/Components/Html/Alert/JJAlert.cs +++ b/src/Core/UI/Components/Html/Alert/JJAlert.cs @@ -1,6 +1,5 @@ #nullable enable using System.Collections.Generic; -using System.Linq; using JetBrains.Annotations; using JJMasterData.Core.DataDictionary; using JJMasterData.Core.DataDictionary.Models; @@ -45,58 +44,70 @@ internal override HtmlBuilder BuildHtml() if (ShowCloseButton) html.Append(GetCloseButton("alert")); - if (!string.IsNullOrEmpty(Title)) + + var hasTitle = !string.IsNullOrEmpty(Title); + + if (hasTitle) { html.Append(HtmlTag.H5, h5 => { h5.WithCssClass("alert-heading"); - if (ShowIcon && Icon is not null) + if (ShowIcon) { - var icon = new JJIcon(Icon.Value); - icon.CssClass += $"{BootstrapHelper.MarginRight}-{1}"; - h5.AppendComponent(icon); + AppendAlertIcon(h5); } h5.AppendText(Title); }); } - else - { - if (ShowIcon && Icon is not null) - { - var icon = new JJIcon(Icon.Value); - icon.CssClass += $"{BootstrapHelper.MarginRight}-{1}"; - html.AppendComponent(icon); - } - } if (InnerHtml is not null) + { + if (!hasTitle && ShowIcon) + AppendAlertIcon(html); html.Append(InnerHtml); - - if (Messages.Count > 1) + } + + html.AppendDiv(div => { - html.Append(HtmlTag.Ul, ul => + div.WithCssClass("alert-content"); + + if (!hasTitle && ShowIcon) + AppendAlertIcon(div); + + if (Messages.Count > 1) { - ul.WithCssClass("m-0"); - foreach (var message in Messages) + div.Append(HtmlTag.Ul, ul => { - ul.Append(HtmlTag.Li, li => + ul.WithCssClass("m-0"); + foreach (var message in Messages) { - li.AppendText(message); - }); - } - }); - } - else if(Messages.Count == 1) - { - html.AppendText(Messages[0]); - } - - - + ul.Append(HtmlTag.Li, li => + { + li.AppendText(message); + }); + } + }); + } + else if(Messages.Count == 1) + { + div.AppendText(Messages[0]); + } + }); + return html; } + private void AppendAlertIcon(HtmlBuilder div) + { + if (Icon == null) + return; + + var icon = new JJIcon(Icon.Value); + icon.CssClass += $"{BootstrapHelper.MarginRight}-{1}"; + div.AppendComponent(icon); + } + private string GetClassType() { if (Color == BootstrapColor.Default) @@ -105,12 +116,12 @@ private string GetClassType() return $"alert-{Color.ToColorString()}"; } - internal static HtmlBuilder GetCloseButton(string dimissValue) + internal static HtmlBuilder GetCloseButton(string dismissValue) { var btn = new HtmlBuilder(HtmlTag.Button) .WithAttribute("type", "button") .WithAttribute("aria-label", "Close") - .WithDataAttribute("dismiss", dimissValue) + .WithDataAttribute("dismiss", dismissValue) .WithCssClass(BootstrapHelper.Close) .AppendIf(BootstrapHelper.Version == 3, HtmlTag.Span, span => { diff --git a/src/Core/UI/Components/Html/Breadcrumb/BreadcrumbFactory.cs b/src/Core/UI/Components/Html/Breadcrumb/BreadcrumbFactory.cs index 6060fb532..6717dcbac 100644 --- a/src/Core/UI/Components/Html/Breadcrumb/BreadcrumbFactory.cs +++ b/src/Core/UI/Components/Html/Breadcrumb/BreadcrumbFactory.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace JJMasterData.Core.UI.Components; +[SuppressMessage("Performance", "CA1822:Mark members as static")] public class BreadcrumbFactory : IComponentFactory { public JJBreadcrumb Create() diff --git a/src/Core/UI/Components/Html/Breadcrumb/JJBreadcrumb.cs b/src/Core/UI/Components/Html/Breadcrumb/JJBreadcrumb.cs index ec8198053..7e4f73949 100644 --- a/src/Core/UI/Components/Html/Breadcrumb/JJBreadcrumb.cs +++ b/src/Core/UI/Components/Html/Breadcrumb/JJBreadcrumb.cs @@ -15,7 +15,7 @@ public JJBreadcrumb() internal override HtmlBuilder BuildHtml() { - var html = new Div() + var html = new HtmlBuilder(HtmlTag.Div) .WithNameAndId(Name) .WithAttributes(Attributes) .WithCssClass(CssClass) @@ -30,7 +30,7 @@ internal override HtmlBuilder BuildHtml() private HtmlBuilder GetHtmlOlItems() { - var ol = new Ol(); + var ol = new HtmlBuilder(HtmlTag.Ol); ol.WithCssClass("breadcrumb mb-0"); var totalItems = Items.Count; @@ -46,7 +46,7 @@ private HtmlBuilder GetHtmlOlItems() private static HtmlBuilder GetHtmlItem(BreadcrumbItem item, bool isLast) { - var li = new Li(); + var li = new HtmlBuilder(HtmlTag.Li); li.WithCssClass("breadcrumb-item"); li.WithCssClassIf(isLast, "active"); diff --git a/src/Core/UI/Components/Html/CollapsePanel/JJCollapsePanel.cs b/src/Core/UI/Components/Html/CollapsePanel/JJCollapsePanel.cs index dba241e19..a18d845b4 100644 --- a/src/Core/UI/Components/Html/CollapsePanel/JJCollapsePanel.cs +++ b/src/Core/UI/Components/Html/CollapsePanel/JJCollapsePanel.cs @@ -58,7 +58,7 @@ public JJCollapsePanel(IFormValues formValues) internal override HtmlBuilder BuildHtml() { - var root = new Div(); + var root = new HtmlBuilder(HtmlTag.Div); root.Append(HtmlTag.Input, input => { @@ -165,7 +165,7 @@ private HtmlBuilder GetPanelHeading() private HtmlBuilder GetBody() { - var panelBody = new Div(); + var panelBody = new HtmlBuilder(HtmlTag.Div); if (!string.IsNullOrEmpty(SubTitle)) { diff --git a/src/Core/UI/Components/Html/HtmlComponentFactory.cs b/src/Core/UI/Components/Html/HtmlComponentFactory.cs index fc5a20fbe..549c1244e 100644 --- a/src/Core/UI/Components/Html/HtmlComponentFactory.cs +++ b/src/Core/UI/Components/Html/HtmlComponentFactory.cs @@ -1,6 +1,7 @@ # nullable enable using System; +using System.Diagnostics.CodeAnalysis; using JJMasterData.Commons.Localization; using JJMasterData.Core.DataManager.Expressions; using JJMasterData.Core.Http.Abstractions; @@ -9,6 +10,7 @@ namespace JJMasterData.Core.UI.Components; +[SuppressMessage("Performance", "CA1822:Mark members as static")] public class HtmlComponentFactory( ExpressionsService expressionsService, IStringLocalizer stringLocalizer, diff --git a/src/Core/UI/Components/Html/Icon/IconFactory.cs b/src/Core/UI/Components/Html/Icon/IconFactory.cs index 644559ecf..6d608b6df 100644 --- a/src/Core/UI/Components/Html/Icon/IconFactory.cs +++ b/src/Core/UI/Components/Html/Icon/IconFactory.cs @@ -1,14 +1,17 @@ +using System.Diagnostics.CodeAnalysis; using JJMasterData.Core.DataDictionary; namespace JJMasterData.Core.UI.Components; -public class IconFactory +[SuppressMessage("Performance", "CA1822:Mark members as static")] +public class IconFactory : IComponentFactory { public JJIcon Create() { return new JJIcon(); } + // ReSharper disable once MemberCanBeMadeStatic.Global public JJIcon Create(IconType icon) { return new JJIcon(icon); diff --git a/src/Core/UI/Components/Html/Icon/JJIcon.cs b/src/Core/UI/Components/Html/Icon/JJIcon.cs index bd9ca4a2d..b2609fa13 100644 --- a/src/Core/UI/Components/Html/Icon/JJIcon.cs +++ b/src/Core/UI/Components/Html/Icon/JJIcon.cs @@ -14,8 +14,10 @@ public class JJIcon : HtmlComponent public string Color { get; set; } public string Tooltip { get; set; } - public JJIcon() { } - + public JJIcon() + { + } + public JJIcon(IconType icon) { IconClass = icon.GetCssClass(); @@ -45,10 +47,10 @@ public JJIcon(string iconClass, string color, string tooltip) : this(iconClass, { Tooltip = tooltip; } - + internal override HtmlBuilder BuildHtml() { - var element = new HtmlBuilder(HtmlTag.Span) + var span = new HtmlBuilder(HtmlTag.Span) .WithNameAndId(Name) .WithAttributes(Attributes) .WithCssClass(IconClass) @@ -56,6 +58,6 @@ internal override HtmlBuilder BuildHtml() .WithToolTip(Tooltip) .WithAttributeIf(!string.IsNullOrEmpty(Color), "style",$"color:{Color}"); - return element; + return span; } } diff --git a/src/Core/UI/Components/Html/Image/ImageFactory.cs b/src/Core/UI/Components/Html/Image/ImageFactory.cs index 4700182b7..69d8c061d 100644 --- a/src/Core/UI/Components/Html/Image/ImageFactory.cs +++ b/src/Core/UI/Components/Html/Image/ImageFactory.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace JJMasterData.Core.UI.Components; +[SuppressMessage("Performance", "CA1822:Mark members as static")] public class ImageFactory : IComponentFactory { JJImage IComponentFactory.Create() diff --git a/src/Core/UI/Components/Html/LinkButton/JJLinkButton.cs b/src/Core/UI/Components/Html/LinkButton/JJLinkButton.cs index 96c98bbca..dba0b3b33 100644 --- a/src/Core/UI/Components/Html/LinkButton/JJLinkButton.cs +++ b/src/Core/UI/Components/Html/LinkButton/JJLinkButton.cs @@ -185,9 +185,7 @@ private JJIcon GetIcon() { icon.CssClass = "fa-fw"; } - - icon.CssClass += " bi"; - + return icon; } diff --git a/src/Core/UI/Components/Html/LinkButton/JJLinkButtonGroup.cs b/src/Core/UI/Components/Html/LinkButton/JJLinkButtonGroup.cs index 920ccdb1c..5dd12ffe0 100644 --- a/src/Core/UI/Components/Html/LinkButton/JJLinkButtonGroup.cs +++ b/src/Core/UI/Components/Html/LinkButton/JJLinkButtonGroup.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using JJMasterData.Commons.Localization; using JJMasterData.Core.UI.Html; using Microsoft.Extensions.Localization; @@ -44,25 +43,25 @@ internal override HtmlBuilder BuildHtml() internal void AddActionsAt(HtmlBuilder html) { - var listAction = Actions.Where(x => !x.IsGroup && x.Visible).ToList(); - var listActionGroup = Actions.Where(x => x.IsGroup && x.Visible).ToList(); + var actionList = Actions.FindAll(x => !x.IsGroup && x.Visible); + var actionListGroup = Actions.FindAll(x => x.IsGroup && x.Visible); - if (listAction.Count == 0 && listActionGroup.Count == 0) + if (actionList.Count == 0 && actionListGroup.Count == 0) return; - foreach (var action in listAction) + foreach (var action in actionList) { action.ShowAsButton = ShowAsButton; html.AppendComponent(action); } - if (listActionGroup.Count > 0) + if (actionListGroup.Count > 0) { html.Append(GetHtmlCaretButton()); html.Append(HtmlTag.Ul, ul => { ul.WithCssClass("dropdown-menu dropdown-menu-right dropdown-menu-end"); - AddGroupActions(ul, listActionGroup); + AddGroupActions(ul, actionListGroup); }); } } diff --git a/src/Core/UI/Components/Html/MessageToast/MessageToastFactory.cs b/src/Core/UI/Components/Html/MessageToast/MessageToastFactory.cs index 27bd22b4c..a6b3ce44c 100644 --- a/src/Core/UI/Components/Html/MessageToast/MessageToastFactory.cs +++ b/src/Core/UI/Components/Html/MessageToast/MessageToastFactory.cs @@ -31,6 +31,10 @@ public JJMessageToast Create(string message, BootstrapColor color = BootstrapCol messageToast.Icon.IconClass = IconType.Warning.GetCssClass(); messageToast.Title = stringLocalizer["Warning"]; break; + case BootstrapColor.Info: + messageToast.Icon.IconClass = IconType.SolidCircleInfo.GetCssClass(); + messageToast.Title = stringLocalizer["Operation Performed"]; + break; default: messageToast.Icon.IconClass = IconType.Check.GetCssClass(); messageToast.Title = stringLocalizer["Operation Performed"]; diff --git a/src/Core/UI/Components/Html/ModalDialog/JJModalDialog.cs b/src/Core/UI/Components/Html/ModalDialog/JJModalDialog.cs index e4d206b14..eeff2138b 100644 --- a/src/Core/UI/Components/Html/ModalDialog/JJModalDialog.cs +++ b/src/Core/UI/Components/Html/ModalDialog/JJModalDialog.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using JJMasterData.Commons.Extensions; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.UI.Html; @@ -31,7 +30,7 @@ internal override HtmlBuilder BuildHtml() var html = new HtmlBuilder(HtmlTag.Div) .WithAttributes(Attributes) .WithId(Name) - .WithCssClass("modal") + .WithCssClass("modal fade") .WithCssClass(CssClass) .WithAttribute("role", "dialog") .WithAttribute("aria-hidden", "true") diff --git a/src/Core/UI/Components/Html/Offcanvas/JJOffcanvas.cs b/src/Core/UI/Components/Html/Offcanvas/JJOffcanvas.cs index 1a0c04437..fd44da9da 100644 --- a/src/Core/UI/Components/Html/Offcanvas/JJOffcanvas.cs +++ b/src/Core/UI/Components/Html/Offcanvas/JJOffcanvas.cs @@ -11,7 +11,7 @@ public class JJOffcanvas : HtmlComponent internal override HtmlBuilder BuildHtml() { - var offcanvas = new Div() + var offcanvas = new HtmlBuilder(HtmlTag.Div) .WithCssClass($"offcanvas {Position.GetCssClass()}") .WithAttribute("tabindex", "-1") .WithId(Name) diff --git a/src/Core/UI/Components/Html/TabNav/JJTabNav.cs b/src/Core/UI/Components/Html/TabNav/JJTabNav.cs index 28d701a4b..66ed855ca 100644 --- a/src/Core/UI/Components/Html/TabNav/JJTabNav.cs +++ b/src/Core/UI/Components/Html/TabNav/JJTabNav.cs @@ -51,7 +51,7 @@ internal override HtmlBuilder BuildHtml() private HtmlBuilder GetNavTabs() { - var ul = new Ul() + var ul = new HtmlBuilder(HtmlTag.Ul) .WithAttribute("role", "tablist") .WithCssClass("nav nav-tabs"); diff --git a/src/Core/UI/Components/Html/Title/TitleFactory.cs b/src/Core/UI/Components/Html/Title/TitleFactory.cs index 9f40d512c..baaeaf437 100644 --- a/src/Core/UI/Components/Html/Title/TitleFactory.cs +++ b/src/Core/UI/Components/Html/Title/TitleFactory.cs @@ -1,7 +1,6 @@ #nullable enable using System; using System.Collections.Generic; -using System.Drawing; using JJMasterData.Core.DataDictionary; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataManager.Expressions; diff --git a/src/Core/UI/Components/Html/ValidationSummary/JJValidationSummary.cs b/src/Core/UI/Components/Html/ValidationSummary/JJValidationSummary.cs index c0fb681ec..48fa7562f 100644 --- a/src/Core/UI/Components/Html/ValidationSummary/JJValidationSummary.cs +++ b/src/Core/UI/Components/Html/ValidationSummary/JJValidationSummary.cs @@ -56,10 +56,7 @@ internal override HtmlBuilder BuildHtml() ShowCloseButton = ShowCloseButton, }; - foreach (var message in Errors) - { - alert.Messages.Add(message); - } + alert.Messages.AddRange(Errors); return alert.BuildHtml(); } diff --git a/src/Core/UI/Components/IComponentFactoryOfT.cs b/src/Core/UI/Components/IComponentFactoryOfT.cs index 46691ed34..3ffdc92af 100644 --- a/src/Core/UI/Components/IComponentFactoryOfT.cs +++ b/src/Core/UI/Components/IComponentFactoryOfT.cs @@ -13,5 +13,5 @@ public interface IFormElementComponentFactory where TComponent : Co { TComponent Create(FormElement formElement); - Task CreateAsync(string elementName); + ValueTask CreateAsync(string elementName); } \ No newline at end of file diff --git a/src/Core/UI/Components/IO/FileDownloader/FileDownloaderFactory.cs b/src/Core/UI/Components/IO/FileDownloader/FileDownloaderFactory.cs index 563948532..8545b7f60 100644 --- a/src/Core/UI/Components/IO/FileDownloader/FileDownloaderFactory.cs +++ b/src/Core/UI/Components/IO/FileDownloader/FileDownloaderFactory.cs @@ -6,21 +6,16 @@ namespace JJMasterData.Core.UI.Components; -internal class FileDownloaderFactory(IHttpContext httpContext, +internal sealed class FileDownloaderFactory(IHttpContext httpContext, IEncryptionService encryptionService, IStringLocalizer stringLocalizer, ILoggerFactory loggerFactory) : IComponentFactory { - private IHttpContext HttpContext { get; } = httpContext; - private IEncryptionService EncryptionService { get; } = encryptionService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private ILoggerFactory LoggerFactory { get; } = loggerFactory; - public JJFileDownloader Create() { - return new JJFileDownloader(HttpContext, EncryptionService, StringLocalizer, - LoggerFactory.CreateLogger()); + return new JJFileDownloader(httpContext, encryptionService, stringLocalizer, + loggerFactory.CreateLogger()); } } \ No newline at end of file diff --git a/src/Core/UI/Components/IO/FileDownloader/JJFileDownloader.cs b/src/Core/UI/Components/IO/FileDownloader/JJFileDownloader.cs index 31309130a..a5dbf5c1b 100644 --- a/src/Core/UI/Components/IO/FileDownloader/JJFileDownloader.cs +++ b/src/Core/UI/Components/IO/FileDownloader/JJFileDownloader.cs @@ -147,7 +147,7 @@ public string GetDownloadUrl() var uriBuilder = new UriBuilder(url); var query = HttpUtility.ParseQueryString(uriBuilder.Query); var routeContext = new RouteContext( ComponentContext.DownloadFile); - var encryptedRouteContext = EncryptionService.EncryptRouteContext(routeContext); + var encryptedRouteContext = EncryptionService.EncryptObject(routeContext); query["routeContext"] = encryptedRouteContext; query[DirectDownloadParameter] = encryptedFilePath; @@ -162,7 +162,7 @@ public static string GetExternalDownloadLink(IEncryptionService encryptionServic var uriBuilder = new UriBuilder(absoluteUri); var query = HttpUtility.ParseQueryString(uriBuilder.Query); var routeContext = new RouteContext( ComponentContext.DownloadFile); - var encryptedRouteContext = encryptionService.EncryptRouteContext(routeContext); + var encryptedRouteContext = encryptionService.EncryptObject(routeContext); var encryptedFilePath = encryptionService.EncryptStringWithUrlEscape(filePath); query["routeContext"] = encryptedRouteContext; query[DirectDownloadParameter] = encryptedFilePath; diff --git a/src/Core/UI/Components/IO/UploadArea/JJUploadArea.cs b/src/Core/UI/Components/IO/UploadArea/JJUploadArea.cs index 6835fe7a1..8b6210789 100644 --- a/src/Core/UI/Components/IO/UploadArea/JJUploadArea.cs +++ b/src/Core/UI/Components/IO/UploadArea/JJUploadArea.cs @@ -174,7 +174,7 @@ internal HtmlBuilder GetUploadAreaHtmlBuilder() div.WithAttributes(Attributes); div.WithAttributeIf(Url is not null,"upload-url", Url!); div.WithAttribute("js-callback",JsCallback); - div.WithAttribute((string)"route-context", EncryptionService.EncryptRouteContext(RouteContext)); + div.WithAttribute((string)"route-context", EncryptionService.EncryptObject(RouteContext)); div.WithAttribute("allow-multiple-files", Multiple.ToString().ToLower()); div.WithAttribute("query-string-params", GetQueryStringParams()); div.WithAttribute("max-file-size", MaxFileSize.ToString()); @@ -226,20 +226,17 @@ private string GetAllowedTypes() /// To change this in .NET Framework, change web.config in system.web/httpRuntime /// Measured in bytes /// - public int GetMaxRequestLength() + public static int GetMaxRequestLength() { - int maxRequestLength; - #if NETFRAMEWORK - maxRequestLength = 4194304; //4mb + var maxRequestLength = 4194304; //4mb if (System.Configuration.ConfigurationManager.GetSection("system.web/httpRuntime") is System.Web.Configuration.HttpRuntimeSection section) maxRequestLength = section.MaxRequestLength * 1024; #else // ASP.NET Core enforces 30MB (~28.6 MiB) max request body size limit, be it Kestrel and HttpSys. // Under normal circumstances, there is no need to increase the size of the HTTP request. - - maxRequestLength = 30720000; + const int maxRequestLength = 30720000; #endif return maxRequestLength; diff --git a/src/Core/UI/Components/IO/UploadArea/UploadAreaFactory.cs b/src/Core/UI/Components/IO/UploadArea/UploadAreaFactory.cs index 3766922d9..ac2fa831a 100644 --- a/src/Core/UI/Components/IO/UploadArea/UploadAreaFactory.cs +++ b/src/Core/UI/Components/IO/UploadArea/UploadAreaFactory.cs @@ -6,19 +6,14 @@ namespace JJMasterData.Core.UI.Components; -internal class UploadAreaFactory(IHttpContext httpContext, +internal sealed class UploadAreaFactory(IHttpContext httpContext, UploadAreaService uploadAreaService, IEncryptionService encryptionService, IStringLocalizer stringLocalizer) : IComponentFactory { - private IHttpContext HttpContext { get; } = httpContext; - private UploadAreaService UploadAreaService { get; } = uploadAreaService; - private IEncryptionService EncryptionService { get; } = encryptionService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - public JJUploadArea Create() { - return new JJUploadArea(HttpContext,UploadAreaService,EncryptionService, StringLocalizer); + return new JJUploadArea(httpContext,uploadAreaService,encryptionService, stringLocalizer); } } \ No newline at end of file diff --git a/src/Core/UI/Components/IO/UploadArea/UploadAreaResultDto.cs b/src/Core/UI/Components/IO/UploadArea/UploadAreaResultDto.cs index e662d67ba..600e7aa8e 100644 --- a/src/Core/UI/Components/IO/UploadArea/UploadAreaResultDto.cs +++ b/src/Core/UI/Components/IO/UploadArea/UploadAreaResultDto.cs @@ -5,9 +5,9 @@ namespace JJMasterData.Core.UI.Components; public record UploadAreaResultDto { - [JsonProperty("message",NullValueHandling=NullValueHandling.Ignore)] + [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] public string? SuccessMessage { get; set; } - - [JsonProperty("error",NullValueHandling=NullValueHandling.Ignore)] + + [JsonProperty("error", NullValueHandling = NullValueHandling.Ignore)] public string? ErrorMessage { get; set; } } \ No newline at end of file diff --git a/src/Core/UI/Components/IO/UploadView/JJUploadView.cs b/src/Core/UI/Components/IO/UploadView/JJUploadView.cs index 5aa0408fd..3ec3e1fe5 100644 --- a/src/Core/UI/Components/IO/UploadView/JJUploadView.cs +++ b/src/Core/UI/Components/IO/UploadView/JJUploadView.cs @@ -19,6 +19,7 @@ using JJMasterData.Core.DataManager.Models; using JJMasterData.Core.Extensions; using JJMasterData.Core.Http.Abstractions; +using JJMasterData.Core.Tasks; using JJMasterData.Core.UI.Events.Args; using JJMasterData.Core.UI.Html; using JJMasterData.Core.UI.Routing; @@ -135,7 +136,7 @@ public JJGridView GridView _gridView.Name = $"{Name}-grid-view"; _gridView.UserValues = UserValues; - _gridView.ShowPagging = false; + _gridView.ShowPaging = false; _gridView.ShowTitle = false; @@ -173,7 +174,7 @@ public JJGridView GridView } } - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; }; _gridView.GridTableActions.Add(RenameAction); @@ -321,7 +322,7 @@ public async Task GetUploadViewResult() if (!string.IsNullOrEmpty(previewVideo)) return new ContentComponentResult(GetHtmlPreviewVideo(previewVideo)); - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); var uploadAction = CurrentContext.Request.Form[$"upload-view-action-{Name}"]; if (!string.IsNullOrEmpty(uploadAction)) @@ -387,7 +388,7 @@ private HtmlBuilder GetHtmlPreviewVideo(string previewVideo) script.AppendLine(" $('#video').css('max-height',window.innerHeight);"); script.AppendLine(" }); "); - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); html.Append(HtmlTag.Center, c => { c.Append(HtmlTag.Video, video => @@ -415,7 +416,7 @@ private HtmlBuilder GetHtmlPreviewImage(string previewImage) if (file.IsInMemory) { var base64 = Convert.ToBase64String(file.Content.Bytes.ToArray()); - src = $"data:image/{Path.GetExtension((string)fileName).Replace(".", "")};base64,{base64}"; + src = $"data:image/{Path.GetExtension(fileName).Replace(".", "")};base64,{base64}"; } else { @@ -433,13 +434,13 @@ private HtmlBuilder GetHtmlPreviewImage(string previewImage) }); """; - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); html.Append(HtmlTag.Center, c => { c.Append(HtmlTag.Img, img => { img.WithAttribute("id", "img") - .WithAttribute("src", src).WithAttribute((string)"alt", (string)fileName) + .WithAttribute("src", src).WithAttribute("alt", fileName) .WithStyle( "max-height:350px;display:none;") .WithCssClass("img-responsive"); }); @@ -533,7 +534,7 @@ private async Task GetGalleryHtml() foreach (var fileInfo in files) { var file = fileInfo.Content; - var col = new Div(); + var col = new HtmlBuilder(HtmlTag.Div); col.WithCssClass("col-sm-3"); await col.AppendAsync(HtmlTag.Ul, async ul => { @@ -564,7 +565,7 @@ await li.AppendAsync(HtmlTag.Table, async table => private HtmlBuilder GetHtmlGalleryListItem(string label, string value) { - return new Li() + return new HtmlBuilder(HtmlTag.Li) .WithCssClass("list-group-item") .Append(HtmlTag.B, b => { @@ -575,7 +576,7 @@ private HtmlBuilder GetHtmlGalleryListItem(string label, string value) private HtmlBuilder GetHtmlGalleryPreview(string fileName) { - var html = new Li() + var html = new HtmlBuilder(HtmlTag.Li) .WithCssClass("list-group-item"); switch (Path.GetExtension(fileName)) @@ -721,7 +722,7 @@ private static Dictionary ConvertFormFileToDictionary(FormFileCo private async Task GetPreviewModalHtml() { - var html = new Div(); + var html = new HtmlBuilder(HtmlTag.Div); var label = ComponentFactory.Html.Label.Create(); label.Text = "File name"; diff --git a/src/Core/UI/Components/IO/UploadView/UploadViewFactory.cs b/src/Core/UI/Components/IO/UploadView/UploadViewFactory.cs index 04c678a90..91c41e661 100644 --- a/src/Core/UI/Components/IO/UploadView/UploadViewFactory.cs +++ b/src/Core/UI/Components/IO/UploadView/UploadViewFactory.cs @@ -6,26 +6,20 @@ namespace JJMasterData.Core.UI.Components; -internal class UploadViewFactory(IHttpContext currentContext, +internal sealed class UploadViewFactory(IHttpContext currentContext, IComponentFactory componentFactory, IEncryptionService encryptionService, IStringLocalizer stringLocalizer, ILoggerFactory loggerFactory) : IComponentFactory { - public ILoggerFactory LoggerFactory { get; } = loggerFactory; - private IEncryptionService EncryptionService { get; } = encryptionService; - private IStringLocalizer StringLocalizer { get; } = stringLocalizer; - private IHttpContext CurrentContext { get; } = currentContext; - private IComponentFactory ComponentFactory { get; } = componentFactory; - public JJUploadView Create() { return new JJUploadView( - CurrentContext, - ComponentFactory, - EncryptionService, - StringLocalizer, - LoggerFactory); + currentContext, + componentFactory, + encryptionService, + stringLocalizer, + loggerFactory); } } \ No newline at end of file diff --git a/src/Core/UI/Components/Importation/DataImportationFactory.cs b/src/Core/UI/Components/Importation/DataImportationFactory.cs index 01104b1bf..2529e5710 100644 --- a/src/Core/UI/Components/Importation/DataImportationFactory.cs +++ b/src/Core/UI/Components/Importation/DataImportationFactory.cs @@ -18,12 +18,12 @@ namespace JJMasterData.Core.UI.Components; -internal class DataImportationFactory( +internal sealed class DataImportationFactory( IDataDictionaryRepository dataDictionaryRepository, IFormEventHandlerResolver formEventHandlerResolver, ExpressionsService expressionsService, + FieldValuesService fieldValuesService, FormService formService, - FieldsService fieldsService, IBackgroundTaskManager backgroundTaskManager, IHttpContext httpContext, IComponentFactory componentFactory, @@ -36,13 +36,22 @@ internal class DataImportationFactory( { public JJDataImportation Create(FormElement formElement) { - return new JJDataImportation(formElement, expressionsService, formService, - fieldsService, backgroundTaskManager, httpContext, componentFactory,dataItemService, - dataImportationWorkerFactory, encryptionService, loggerFactory, + return new JJDataImportation( + formElement, + expressionsService, + formService, + fieldValuesService, + backgroundTaskManager, + httpContext, + componentFactory, + dataItemService, + dataImportationWorkerFactory, + encryptionService, + loggerFactory, stringLocalizer); } - public async Task CreateAsync(string elementName) + public async ValueTask CreateAsync(string elementName) { if (string.IsNullOrEmpty(elementName)) throw new ArgumentNullException(nameof(elementName)); diff --git a/src/Core/UI/Components/Importation/DataImportationHelp.cs b/src/Core/UI/Components/Importation/DataImportationHelp.cs index e49074f45..790868299 100644 --- a/src/Core/UI/Components/Importation/DataImportationHelp.cs +++ b/src/Core/UI/Components/Importation/DataImportationHelp.cs @@ -14,7 +14,7 @@ namespace JJMasterData.Core.UI.Components; -internal class DataImportationHelp +internal sealed class DataImportationHelp { private JJDataImportation DataImportation { get; } private IStringLocalizer StringLocalizer { get; } @@ -232,7 +232,7 @@ private string GetInfoText(int columnsCount) text.Append(", "); text.Append(StringLocalizer["with the maximum size of"]); text.Append(" "); - text.Append(Format.FormatFileSize(upload.GetMaxRequestLength())); + text.Append(Format.FormatFileSize(JJUploadArea.GetMaxRequestLength())); text.Append(""); text.Append(", "); text.Append(StringLocalizer["do not include caption or description in the first line"]); @@ -250,7 +250,7 @@ private string GetInfoText(int columnsCount) private async Task GetHtmlComboHelp(FormElementField field) { - var defaultValues = await DataImportation.FieldsService.GetDefaultValuesAsync(DataImportation.FormElement, + var defaultValues = await DataImportation.FieldValuesService.GetDefaultValuesAsync(DataImportation.FormElement, new FormStateData(new Dictionary(), DataImportation.UserValues, PageState.Import)); var formStateData = new FormStateData(defaultValues, DataImportation.UserValues, PageState.Import); var dataQuery = new DataQuery(formStateData, DataImportation.FormElement.ConnectionId); diff --git a/src/Core/UI/Components/Importation/DataImportationLog.cs b/src/Core/UI/Components/Importation/DataImportationLog.cs index 3192383a1..df99755e6 100644 --- a/src/Core/UI/Components/Importation/DataImportationLog.cs +++ b/src/Core/UI/Components/Importation/DataImportationLog.cs @@ -11,7 +11,7 @@ namespace JJMasterData.Core.UI.Components; -internal class DataImportationLog +internal sealed class DataImportationLog { internal DataImportationReporter Reporter { get; } internal IStringLocalizer StringLocalizer { get; } @@ -51,7 +51,7 @@ public HtmlBuilder GetSummaryHtml() html.Append(HtmlTag.Span, s => { - s.WithCssClass(BootstrapHelper.LabelSucess) + s.WithCssClass(BootstrapHelper.LabelSuccess) .WithAttribute("id", "lblInsert") .WithAttributeIf(Reporter.Insert == 0, "style", "display:none;") .AppendText(StringLocalizer["Inserted:"]) @@ -63,7 +63,7 @@ public HtmlBuilder GetSummaryHtml() }); html.Append(HtmlTag.Span, s => { - s.WithCssClass(BootstrapHelper.LabelSucess) + s.WithCssClass(BootstrapHelper.LabelSuccess) .WithAttribute("id", "lblUpdate") .WithAttributeIf(Reporter.Update == 0, "style", "display:none;") .AppendText(StringLocalizer["Updated:"]) diff --git a/src/Core/UI/Components/Importation/DataImportationScripts.cs b/src/Core/UI/Components/Importation/DataImportationScripts.cs index 74be31b9b..e5f5376ba 100644 --- a/src/Core/UI/Components/Importation/DataImportationScripts.cs +++ b/src/Core/UI/Components/Importation/DataImportationScripts.cs @@ -9,7 +9,7 @@ namespace JJMasterData.Core.UI.Components; -internal class DataImportationScripts( +internal sealed class DataImportationScripts( string name, FormElement formElement, IStringLocalizer stringLocalizer, @@ -21,10 +21,10 @@ internal class DataImportationScripts( private IStringLocalizer StringLocalizer { get; } = stringLocalizer; private IEncryptionService EncryptionService { get; } = encryptionService; - private string GetEncryptedRouteContext(ComponentContext context = ComponentContext.DataImportation) + private string GetEncryptedRouteContext() { - var routeContext = RouteContext.FromFormElement(FormElement, context); - var encryptedRouteContext = EncryptionService.EncryptRouteContext(routeContext); + var routeContext = RouteContext.FromFormElement(FormElement, ComponentContext.DataImportation); + var encryptedRouteContext = EncryptionService.EncryptObject(routeContext); return encryptedRouteContext; } @@ -35,19 +35,19 @@ public DataImportationScripts(JJDataImportation dataImportation) : this(dataImpo public string GetStartProgressVerificationScript() { //language=Javascript - return $"DataImportationHelper.startProgressVerification('{Name}', '{GetEncryptedRouteContext()}', '{GetEncryptedRouteContext(ComponentContext.GridViewReload)}')"; + return $"DataImportationHelper.startProgressVerification('{Name}', '{GetEncryptedRouteContext()}')"; } public string GetBackScript() { //language=Javascript - return $"DataImportationHelper.back('{Name}', '{GetEncryptedRouteContext()}', '{GetEncryptedRouteContext(ComponentContext.GridViewReload)}')"; + return $"DataImportationHelper.back('{Name}', '{GetEncryptedRouteContext()}')"; } public string GetShowScript() { //language=Javascript - return $"DataImportationHelper.show('{Name}','{StringLocalizer[ModalTitle]}','{GetEncryptedRouteContext()}', '{GetEncryptedRouteContext(ComponentContext.GridViewReload)}')"; + return $"DataImportationHelper.show('{Name}','{StringLocalizer[ModalTitle]}','{GetEncryptedRouteContext()}')"; } @@ -72,7 +72,7 @@ public string GetLogScript() public string GetUploadCallbackScript() { //language=Javascript - return $"DataImportationHelper.uploadCallback('{Name}','{GetEncryptedRouteContext()}', '{GetEncryptedRouteContext(ComponentContext.GridViewReload)}')"; + return $"DataImportationHelper.uploadCallback('{Name}','{GetEncryptedRouteContext()}')"; } public static string GetCloseModalScript() diff --git a/src/Core/UI/Components/Importation/JJDataImportation.cs b/src/Core/UI/Components/Importation/JJDataImportation.cs index 3a8f2db4e..db3b92963 100644 --- a/src/Core/UI/Components/Importation/JJDataImportation.cs +++ b/src/Core/UI/Components/Importation/JJDataImportation.cs @@ -72,6 +72,8 @@ public class JJDataImportation : ProcessComponent internal DataItemService DataItemService { get; } + internal FieldValuesService FieldValuesService { get; } + private DataImportationWorkerFactory DataImportationWorkerFactory { get; } private RouteContext RouteContext @@ -103,7 +105,7 @@ public JJDataImportation( FormElement formElement, ExpressionsService expressionsService, FormService formService, - FieldsService fieldsService, + FieldValuesService fieldValuesService, IBackgroundTaskManager backgroundTaskManager, IHttpContext currentContext, IComponentFactory componentFactory, @@ -112,12 +114,13 @@ public JJDataImportation( IEncryptionService encryptionService, ILoggerFactory loggerFactory, IStringLocalizer stringLocalizer) - : base(currentContext, expressionsService, fieldsService, backgroundTaskManager, + : base(currentContext, expressionsService, backgroundTaskManager, loggerFactory.CreateLogger(), encryptionService, stringLocalizer) { CurrentContext = currentContext; DataImportationWorkerFactory = dataImportationWorkerFactory; FormService = formService; + FieldValuesService = fieldValuesService; ComponentFactory = componentFactory; DataItemService = dataItemService; FormElement = formElement; @@ -177,7 +180,7 @@ protected override async Task BuildResultAsync() { if (IsRunning()) { - htmlBuilder = new Div(); + htmlBuilder = new HtmlBuilder(HtmlTag.Div); htmlBuilder.WithId(Name); htmlBuilder.Append(GetLoadingHtml()); htmlBuilder.AppendScript(DataImportationScripts.GetStartProgressVerificationScript()); diff --git a/src/Core/UI/Components/JsonComponentResult.cs b/src/Core/UI/Components/JsonComponentResult.cs index ebae01b24..358cd79b8 100644 --- a/src/Core/UI/Components/JsonComponentResult.cs +++ b/src/Core/UI/Components/JsonComponentResult.cs @@ -7,7 +7,7 @@ namespace JJMasterData.Core.UI.Components; -public class JsonComponentResult(object objectResult) : ComponentResult +public sealed class JsonComponentResult(object objectResult) : ComponentResult #if NET ,IActionResult #endif diff --git a/src/Core/UI/Components/ProcessComponent.cs b/src/Core/UI/Components/ProcessComponent.cs index caffb9da9..c26ff635f 100644 --- a/src/Core/UI/Components/ProcessComponent.cs +++ b/src/Core/UI/Components/ProcessComponent.cs @@ -6,7 +6,6 @@ using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataManager; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.Http.Abstractions; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; @@ -16,7 +15,6 @@ namespace JJMasterData.Core.UI.Components; public abstract class ProcessComponent( IHttpContext currentContext, ExpressionsService expressionsService, - FieldsService fieldsService, IBackgroundTaskManager backgroundTaskManager, ILogger logger, IEncryptionService encryptionService, @@ -62,7 +60,6 @@ public ProcessOptions ProcessOptions /// public FormElement FormElement { get; set; } - internal FieldsService FieldsService { get; } = fieldsService; internal IBackgroundTaskManager BackgroundTaskManager { get; } = backgroundTaskManager; private ILogger Logger { get; } = logger; internal IEncryptionService EncryptionService { get; } = encryptionService; @@ -86,7 +83,7 @@ private string BuildProcessKey() break; } - processKey.Append((string)FormElement.Name); + processKey.Append(FormElement.Name); if (ProcessOptions.Scope != ProcessScope.User) return processKey.ToString(); diff --git a/src/Core/UI/Components/RenderedComponentResult.cs b/src/Core/UI/Components/RenderedComponentResult.cs index af270d9ca..bf9ad00fc 100644 --- a/src/Core/UI/Components/RenderedComponentResult.cs +++ b/src/Core/UI/Components/RenderedComponentResult.cs @@ -4,4 +4,4 @@ namespace JJMasterData.Core.UI.Components; -public class RenderedComponentResult(HtmlBuilder htmlBuilder) : HtmlComponentResult(htmlBuilder); \ No newline at end of file +public sealed class RenderedComponentResult(HtmlBuilder htmlBuilder) : HtmlComponentResult(htmlBuilder); \ No newline at end of file diff --git a/src/Core/UI/Events/Abstractions/GridEventHandlerBase.cs b/src/Core/UI/Events/Abstractions/GridEventHandlerBase.cs index 1620eb62c..4bdcb14bb 100644 --- a/src/Core/UI/Events/Abstractions/GridEventHandlerBase.cs +++ b/src/Core/UI/Events/Abstractions/GridEventHandlerBase.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; +using JJMasterData.Core.Tasks; using JJMasterData.Core.UI.Events.Args; namespace JJMasterData.Core.UI.Events.Abstractions; @@ -7,11 +8,11 @@ namespace JJMasterData.Core.UI.Events.Abstractions; public abstract class GridEventHandlerBase : IGridEventHandler { public abstract string ElementName { get; } - public virtual Task OnFilterLoadAsync(object sender, GridFilterLoadEventArgs eventArgs) => Task.CompletedTask; - public virtual Task OnRenderCellAsync(object sender, GridCellEventArgs eventArgs) => Task.CompletedTask; - public virtual Task OnRenderSelectedCellAsync(object sender, GridSelectedCellEventArgs eventArgs) => Task.CompletedTask; + public virtual ValueTask OnFilterLoadAsync(object sender, GridFilterLoadEventArgs eventArgs) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnRenderCellAsync(object sender, GridCellEventArgs eventArgs) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnRenderSelectedCellAsync(object sender, GridSelectedCellEventArgs eventArgs) => ValueTaskHelper.CompletedTask; public virtual Task OnDataLoadAsync(object sender, GridDataLoadEventArgs eventArgs) => Task.CompletedTask; - public virtual Task OnRenderActionAsync(object sender, ActionEventArgs eventArgs) => Task.CompletedTask; - public virtual Task OnRenderToolbarActionAsync(object sender, GridToolbarActionEventArgs eventArgs) => Task.CompletedTask; - public virtual Task OnRenderRowAsync(object sender, GridRowEventArgs eventArgs) => Task.CompletedTask; + public virtual ValueTask OnRenderActionAsync(object sender, ActionEventArgs eventArgs) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnRenderToolbarActionAsync(object sender, GridToolbarActionEventArgs eventArgs) => ValueTaskHelper.CompletedTask; + public virtual ValueTask OnRenderRowAsync(object sender, GridRowEventArgs eventArgs) => ValueTaskHelper.CompletedTask; } diff --git a/src/Core/UI/Events/Abstractions/IGridEventHandler.cs b/src/Core/UI/Events/Abstractions/IGridEventHandler.cs index b84447733..e9b85f9f4 100644 --- a/src/Core/UI/Events/Abstractions/IGridEventHandler.cs +++ b/src/Core/UI/Events/Abstractions/IGridEventHandler.cs @@ -1,33 +1,34 @@ using System.Threading.Tasks; using JJMasterData.Core.Events.Abstractions; +using JJMasterData.Core.Tasks; using JJMasterData.Core.UI.Events.Args; namespace JJMasterData.Core.UI.Events.Abstractions; public interface IGridEventHandler : IEventHandler { - public Task OnFilterLoadAsync(object sender, GridFilterLoadEventArgs eventArgs) + public ValueTask OnFilterLoadAsync(object sender, GridFilterLoadEventArgs eventArgs) #if NET { - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; } #else ; #endif - public Task OnRenderCellAsync(object sender, GridCellEventArgs eventArgs) + public ValueTask OnRenderCellAsync(object sender, GridCellEventArgs eventArgs) #if NET { - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; } #else ; #endif - public Task OnRenderSelectedCellAsync(object sender, GridSelectedCellEventArgs eventArgs) + public ValueTask OnRenderSelectedCellAsync(object sender, GridSelectedCellEventArgs eventArgs) #if NET { - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; } #else ; @@ -41,26 +42,26 @@ public Task OnDataLoadAsync(object sender, GridDataLoadEventArgs eventArgs) #else ; #endif - public Task OnRenderActionAsync(object sender, ActionEventArgs eventArgs) + public ValueTask OnRenderActionAsync(object sender, ActionEventArgs eventArgs) #if NET { - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; } #else ; #endif - Task OnRenderToolbarActionAsync(object sender, GridToolbarActionEventArgs e) + ValueTask OnRenderToolbarActionAsync(object sender, GridToolbarActionEventArgs e) #if NET { - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; } #else ; #endif - Task OnRenderRowAsync(object sender, GridRowEventArgs e) + ValueTask OnRenderRowAsync(object sender, GridRowEventArgs e) #if NET { - return Task.CompletedTask; + return ValueTaskHelper.CompletedTask; } #else ; diff --git a/src/Core/UI/Events/GridDataLoadEventHandler.cs b/src/Core/UI/Events/GridDataLoadEventHandler.cs new file mode 100644 index 000000000..e7761293c --- /dev/null +++ b/src/Core/UI/Events/GridDataLoadEventHandler.cs @@ -0,0 +1,6 @@ +using System.Threading.Tasks; +using JJMasterData.Core.UI.Events.Args; + +namespace JJMasterData.Core.UI.Events; + +public delegate Task GridDataLoadEventHandler(object sender, GridDataLoadEventArgs e); diff --git a/src/Core/UI/Html/HtmlBuilder.Attributes.cs b/src/Core/UI/Html/HtmlBuilder.Attributes.cs index ac59dcb8d..43e4a27f9 100644 --- a/src/Core/UI/Html/HtmlBuilder.Attributes.cs +++ b/src/Core/UI/Html/HtmlBuilder.Attributes.cs @@ -93,12 +93,12 @@ public HtmlBuilder WithCssClass(string? classes) { if (classes == null || string.IsNullOrWhiteSpace(classes)) return this; - - if (!_attributes.ContainsKey("class")) + + if (!_attributes.TryGetValue("class", out var value)) return WithAttribute("class", classes); var classList = new List(); - classList.AddRange(_attributes["class"].Split(' ')); + classList.AddRange(value.Split(' ')); foreach (var cssClass in classes.Split(' ')) { diff --git a/src/Core/UI/Html/HtmlBuilder.Children.cs b/src/Core/UI/Html/HtmlBuilder.Children.cs index 02de98e0c..2918cf021 100644 --- a/src/Core/UI/Html/HtmlBuilder.Children.cs +++ b/src/Core/UI/Html/HtmlBuilder.Children.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using JJMasterData.Core.UI.Components; @@ -41,16 +42,7 @@ public HtmlBuilder Append(HtmlBuilder? builder) /// public HtmlBuilder AppendRange(IEnumerable htmlEnumerable) { - foreach (var item in htmlEnumerable) - Append(item); - - return this; - } - - public async Task AppendRangeAsync(IAsyncEnumerable htmlAsyncEnumerable) - { - await foreach (var item in htmlAsyncEnumerable) - Append(item); + _children.AddRange(htmlEnumerable); return this; } @@ -68,7 +60,7 @@ public HtmlBuilder Append(HtmlTag tag, Action? builderAction = null public HtmlBuilder AppendBr() { - var child = new Br(); + var child = new HtmlBuilder(HtmlTag.Br); Append(child); return this; } @@ -244,7 +236,7 @@ public HtmlBuilder AppendScriptIf(bool condition, string rawScript) public HtmlBuilder AppendComponent(HtmlComponent? component) { if (component != null) - Append((HtmlBuilder?)component.GetHtmlBuilder()); + Append(component.GetHtmlBuilder()); return this; } @@ -257,7 +249,7 @@ public HtmlBuilder AppendComponentIf(bool condition, Func compon return this; } - public async Task AppendControlAsync(ControlBase? control) + public async ValueTask AppendControlAsync(ControlBase? control) { if (control != null) Append(await control.GetHtmlBuilderAsync()); diff --git a/src/Core/UI/Html/HtmlBuilder.cs b/src/Core/UI/Html/HtmlBuilder.cs index 55fe0923f..93e0af59e 100644 --- a/src/Core/UI/Html/HtmlBuilder.cs +++ b/src/Core/UI/Html/HtmlBuilder.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Text; - +using Microsoft.Extensions.ObjectPool; namespace JJMasterData.Core.UI.Html; @@ -13,18 +13,21 @@ namespace JJMasterData.Core.UI.Html; /// /// [!include[Example](../../../doc/Documentation/articles/usages/htmlbuilder.md)] /// -public partial class HtmlBuilder +public sealed partial class HtmlBuilder { private readonly string? _rawText; private readonly bool _hasRawText; private readonly Dictionary _attributes; - private readonly List _children; - - /// - /// Tag of the current builder. - /// - private HtmlBuilderTag? Tag { get; } - + private readonly List _children; + private readonly HtmlBuilderTag? _tag; + + private static readonly ObjectPool StringBuilderPool; + + static HtmlBuilder() + { + StringBuilderPool = new DefaultObjectPoolProvider().CreateStringBuilderPool(); + } + /// /// Initializes a new instance of the class. /// @@ -49,7 +52,7 @@ public HtmlBuilder(string rawText) : this() /// public HtmlBuilder(HtmlTag tag) : this() { - Tag = new HtmlBuilderTag(tag); + _tag = new HtmlBuilderTag(tag); } /// @@ -63,7 +66,7 @@ public override string ToString() /// /// Gets current builder HTML. /// - /// Generate html with indentation? + /// Generate html with indentation if true. public string ToString(bool indentHtml) { var tabCount = indentHtml ? 1 : 0; @@ -72,50 +75,65 @@ public string ToString(bool indentHtml) private string ParseHtmlAsString(int tabCount) { - var html = new StringBuilder(); + string html; + + var htmlBuilder = StringBuilderPool.Get(); if (tabCount > 0 && !_hasRawText) { - html.AppendLine().Append(' ', tabCount * 2); + htmlBuilder.AppendLine().Append(' ', tabCount * 2); } - if (_hasRawText || Tag == null) + if (_hasRawText || _tag is null) { - html.Append(_rawText); - html.Append(GetHtmlContent(tabCount)); + htmlBuilder.Append(_rawText); + htmlBuilder.Append(GetHtmlContent(tabCount)); - return html.ToString(); + html = htmlBuilder.ToString(); + + StringBuilderPool.Return(htmlBuilder); + + return html; } - html.Append('<'); - html.Append(Tag.TagName.GetTagName()); - html.Append(GetAttributesHtml()); + htmlBuilder.Append('<'); + htmlBuilder.Append(_tag.GetTagName()); + htmlBuilder.Append(GetAttributesHtml()); - if (!Tag.HasClosingTag) + if (!_tag.HasClosingTag) { - html.Append(" />"); - return html.ToString(); + htmlBuilder.Append(" />"); + + html = htmlBuilder.ToString(); + + StringBuilderPool.Return(htmlBuilder); + + return html; } - html.Append('>'); + htmlBuilder.Append('>'); - if (Tag.TagName == HtmlTag.TextArea) + if (_tag.HtmlTag is HtmlTag.TextArea) { - html.Append(GetHtmlContent(0)); + htmlBuilder.Append(GetHtmlContent(0)); } else { - html.Append(GetHtmlContent(tabCount)); + htmlBuilder.Append(GetHtmlContent(tabCount)); if (tabCount > 0 && !_hasRawText) - html.AppendLine().Append(' ', tabCount * 2); + htmlBuilder.AppendLine().Append(' ', tabCount * 2); } - html.Append("'); + htmlBuilder.Append("'); + + html = htmlBuilder.ToString(); - return html.ToString(); + StringBuilderPool.Return(htmlBuilder); + + return html; } private string GetHtmlContent(int tabCount) @@ -123,23 +141,31 @@ private string GetHtmlContent(int tabCount) if (tabCount > 0) tabCount++; - var content = new StringBuilder(); + var contentBuilder = StringBuilderPool.Get(); foreach (var child in _children) { - content.Append(child.ParseHtmlAsString(tabCount)); + contentBuilder.Append(child?.ParseHtmlAsString(tabCount)); } - return content.ToString(); + var content = contentBuilder.ToString(); + + StringBuilderPool.Return(contentBuilder); + + return content; } private string GetAttributesHtml() { - var attributes = new StringBuilder(); + var attributesBuilder = StringBuilderPool.Get(); foreach (var item in _attributes) { - attributes.AppendFormat(" {0}=\"{1}\"", item.Key, item.Value); + attributesBuilder.Append($" {item.Key}=\"{item.Value}\""); } - return attributes.ToString(); + var attributes = attributesBuilder.ToString(); + + StringBuilderPool.Return(attributesBuilder); + + return attributes; } public string GetAttribute(string key) diff --git a/src/Core/UI/Html/HtmlBuilderTag.cs b/src/Core/UI/Html/HtmlBuilderTag.cs index d651382be..2d8ae771b 100644 --- a/src/Core/UI/Html/HtmlBuilderTag.cs +++ b/src/Core/UI/Html/HtmlBuilderTag.cs @@ -1,37 +1,69 @@ -namespace JJMasterData.Core.UI.Html; +using System; + +namespace JJMasterData.Core.UI.Html; /// -/// Implementation of HTML tag. +/// Represents a HTML tag of a /// -public class HtmlBuilderTag(HtmlTag tag, bool hasClosingTag) +public sealed class HtmlBuilderTag(HtmlTag htmlTag) { - /// - /// Initializes a new instance of the class. - /// - public HtmlBuilderTag(HtmlTag tag) : this(tag, CheckClosingTagPresence(tag)) - { - } - - /// - /// Name of the tag. - /// - public HtmlTag TagName { get;} = tag; + public HtmlTag HtmlTag { get; } = htmlTag; - /// - /// Flag that indicates whether the tag is self closing (false) or not (true). - /// - public bool HasClosingTag { get; } = hasClosingTag; + public bool HasClosingTag { get; } = htmlTag switch + { + HtmlTag.Area or HtmlTag.Br or HtmlTag.Hr or HtmlTag.Img or HtmlTag.Input => false, + _ => true + }; - private static bool CheckClosingTagPresence(HtmlTag htmlTag) + public string GetTagName() => HtmlTag switch { - return htmlTag switch - { - HtmlTag.Area => false, - HtmlTag.Br => false, - HtmlTag.Hr => false, - HtmlTag.Img => false, - HtmlTag.Input => false, - _ => true - }; - } -} + HtmlTag.A => "a", + HtmlTag.B => "b", + HtmlTag.Blockquote => "blockquote", + HtmlTag.Area => "area", + HtmlTag.Br => "br", + HtmlTag.Div => "div", + HtmlTag.Span => "span", + HtmlTag.Label => "label", + HtmlTag.Input => "input", + HtmlTag.Strong => "strong", + HtmlTag.Button => "button", + HtmlTag.H1 => "h1", + HtmlTag.H2 => "h2", + HtmlTag.H3 => "h3", + HtmlTag.H4 => "h4", + HtmlTag.H5 => "h5", + HtmlTag.H6 => "h6", + HtmlTag.Small => "small", + HtmlTag.Ul => "ul", + HtmlTag.Li => "li", + HtmlTag.TextArea => "textarea", + HtmlTag.Script => "script", + HtmlTag.Select => "select", + HtmlTag.Option => "option", + HtmlTag.Table => "table", + HtmlTag.Tr => "tr", + HtmlTag.Th => "th", + HtmlTag.Td => "td", + HtmlTag.Thead => "thead", + HtmlTag.Tbody => "tbody", + HtmlTag.Hr => "hr", + HtmlTag.I => "i", + HtmlTag.Section => "section", + HtmlTag.P => "p", + HtmlTag.Footer => "footer", + HtmlTag.Img => "img", + HtmlTag.Center => "center", + HtmlTag.Video => "video", + HtmlTag.Form => "form", + HtmlTag.FieldSet => "fieldset", + HtmlTag.Legend => "legend", + HtmlTag.U => "u", + HtmlTag.OptGroup => "optgroup", + HtmlTag.Nav => "nav", + HtmlTag.Ol => "ol", + HtmlTag.Style => "style", + HtmlTag.Iframe => "iframe", + _ => throw new ArgumentOutOfRangeException(nameof(HtmlTag), HtmlTag, "HTML tag not implemented.") + }; +} \ No newline at end of file diff --git a/src/Core/UI/Html/HtmlBuilderTags.cs b/src/Core/UI/Html/HtmlBuilderTags.cs deleted file mode 100644 index 16b5fa4b6..000000000 --- a/src/Core/UI/Html/HtmlBuilderTags.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace JJMasterData.Core.UI.Html; - -public sealed class A() : HtmlBuilder(HtmlTag.A); -public sealed class B() : HtmlBuilder(HtmlTag.B); -public sealed class Blockquote() : HtmlBuilder(HtmlTag.Blockquote); -public sealed class Area() : HtmlBuilder(HtmlTag.Area); -public sealed class Br() : HtmlBuilder(HtmlTag.Br); -public sealed class Div() : HtmlBuilder(HtmlTag.Div); -public sealed class Span() : HtmlBuilder(HtmlTag.Span); -public sealed class Label() : HtmlBuilder(HtmlTag.Label); -public sealed class Input() : HtmlBuilder(HtmlTag.Input); -public sealed class Strong() : HtmlBuilder(HtmlTag.Strong); -public sealed class Button() : HtmlBuilder(HtmlTag.Button); -public sealed class H1() : HtmlBuilder(HtmlTag.H1); -public sealed class H2() : HtmlBuilder(HtmlTag.H2); -public sealed class H3() : HtmlBuilder(HtmlTag.H3); -public sealed class H4() : HtmlBuilder(HtmlTag.H4); -public sealed class H5() : HtmlBuilder(HtmlTag.H5); -public sealed class H6() : HtmlBuilder(HtmlTag.H6); -public sealed class Ol() : HtmlBuilder(HtmlTag.Ol); -public sealed class Small() : HtmlBuilder(HtmlTag.Small); -public sealed class Ul() : HtmlBuilder(HtmlTag.Ul); -public sealed class Li() : HtmlBuilder(HtmlTag.Li); -public sealed class TextArea() : HtmlBuilder(HtmlTag.TextArea); -public sealed class Script() : HtmlBuilder(HtmlTag.Script); -public sealed class Select() : HtmlBuilder(HtmlTag.Select); -public sealed class Option() : HtmlBuilder(HtmlTag.Option); -public sealed class Table() : HtmlBuilder(HtmlTag.Table); -public sealed class Tr() : HtmlBuilder(HtmlTag.Tr); -public sealed class Th() : HtmlBuilder(HtmlTag.Th); -public sealed class Td() : HtmlBuilder(HtmlTag.Td); -public sealed class Thead() : HtmlBuilder(HtmlTag.Thead); -public sealed class Tbody() : HtmlBuilder(HtmlTag.Tbody); -public sealed class Hr() : HtmlBuilder(HtmlTag.Hr); -public sealed class I() : HtmlBuilder(HtmlTag.I); -public sealed class Section() : HtmlBuilder(HtmlTag.Section); -public sealed class P() : HtmlBuilder(HtmlTag.P); -public sealed class Footer() : HtmlBuilder(HtmlTag.Footer); -public sealed class Img() : HtmlBuilder(HtmlTag.Img); -public sealed class Center() : HtmlBuilder(HtmlTag.Center); -public sealed class Video() : HtmlBuilder(HtmlTag.Video); -public sealed class Form() : HtmlBuilder(HtmlTag.Form); -public sealed class FieldSet() : HtmlBuilder(HtmlTag.FieldSet); -public sealed class Legend() : HtmlBuilder(HtmlTag.Legend); -public sealed class U() : HtmlBuilder(HtmlTag.U); -public sealed class OptGroup() : HtmlBuilder(HtmlTag.OptGroup); \ No newline at end of file diff --git a/src/Core/UI/Html/HtmlTag.cs b/src/Core/UI/Html/HtmlTag.cs index 55ab8df32..98dc3eedd 100644 --- a/src/Core/UI/Html/HtmlTag.cs +++ b/src/Core/UI/Html/HtmlTag.cs @@ -48,61 +48,7 @@ public enum HtmlTag U, OptGroup, Nav, - Ol -} - -public static class HtmlTagExtensions -{ - public static string GetTagName(this HtmlTag tag) - { - return tag switch - { - HtmlTag.A => "a", - HtmlTag.B => "b", - HtmlTag.Blockquote => "blockquote", - HtmlTag.Area => "area", - HtmlTag.Br => "br", - HtmlTag.Div => "div", - HtmlTag.Span => "span", - HtmlTag.Label => "label", - HtmlTag.Input => "input", - HtmlTag.Strong => "strong", - HtmlTag.Button => "button", - HtmlTag.H1 => "h1", - HtmlTag.H2 => "h2", - HtmlTag.H3 => "h3", - HtmlTag.H4 => "h4", - HtmlTag.H5 => "h5", - HtmlTag.H6 => "h6", - HtmlTag.Small => "small", - HtmlTag.Ul => "ul", - HtmlTag.Li => "li", - HtmlTag.TextArea => "textarea", - HtmlTag.Script => "script", - HtmlTag.Select => "select", - HtmlTag.Option => "option", - HtmlTag.Table => "table", - HtmlTag.Tr => "tr", - HtmlTag.Th => "th", - HtmlTag.Td => "td", - HtmlTag.Thead => "thead", - HtmlTag.Tbody => "tbody", - HtmlTag.Hr => "hr", - HtmlTag.I => "i", - HtmlTag.Section => "section", - HtmlTag.P => "p", - HtmlTag.Footer => "footer", - HtmlTag.Img => "img", - HtmlTag.Center => "center", - HtmlTag.Video => "video", - HtmlTag.Form => "form", - HtmlTag.FieldSet => "fieldset", - HtmlTag.Legend => "legend", - HtmlTag.U => "u", - HtmlTag.OptGroup => "optgroup", - HtmlTag.Nav => "nav", - HtmlTag.Ol => "ol", - _ => throw new ArgumentOutOfRangeException(nameof(tag), tag, null) - }; - } + Ol, + Style, + Iframe } \ No newline at end of file diff --git a/src/Core/UI/Routing/RouteContextFactory.cs b/src/Core/UI/Routing/RouteContextFactory.cs index a4e1d1c8c..864c94493 100644 --- a/src/Core/UI/Routing/RouteContextFactory.cs +++ b/src/Core/UI/Routing/RouteContextFactory.cs @@ -6,14 +6,11 @@ namespace JJMasterData.Core.UI.Routing; public class RouteContextFactory(IQueryString queryString, IEncryptionService encryptionService) { - private IQueryString QueryString { get; } = queryString; - private IEncryptionService EncryptionService { get; } = encryptionService; - public RouteContext Create() { - if (QueryString.TryGetValue("routeContext", out var encryptedQueryString)) + if (queryString.TryGetValue("routeContext", out var encryptedQueryString)) { - return EncryptionService.DecryptRouteContext(encryptedQueryString); + return encryptionService.DecryptRouteContext(encryptedQueryString); } return new RouteContext(); diff --git a/src/Plugins/Brasil/Actions/BrasilPluginActionHandler.cs b/src/Plugins/Brasil/Actions/BrasilPluginActionHandler.cs index 72d887f47..da63eee7e 100644 --- a/src/Plugins/Brasil/Actions/BrasilPluginActionHandler.cs +++ b/src/Plugins/Brasil/Actions/BrasilPluginActionHandler.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.UI.Html; namespace JJMasterData.Brasil.Actions; @@ -37,7 +36,7 @@ public IEnumerable FieldMapKeys } } - public virtual IEnumerable ConfigurationFields + public IEnumerable ConfigurationFields { get { @@ -63,35 +62,32 @@ public virtual IEnumerable ConfigurationFields } } - public abstract HtmlBuilder? AdditionalInformationHtml { get; } - private static void ClearFields(PluginFieldActionContext context) { foreach (var parameter in context.FieldMap) + { context.Values[parameter.Value] = null; + context.SecretValues[parameter.Value] = null; + } } private PluginActionResult OnResultFound(PluginFieldActionContext context, Dictionary result) { foreach (var parameter in context.FieldMap) { - if (context.ActionContext.FormElement.Fields.TryGetField(parameter.Value, out var field)) - { - var isEnabled = ExpressionsService.GetBoolValue(field.EnableExpression, context.ActionContext.FormStateData); + if (!context.ActionContext.FormElement.Fields.TryGetField(parameter.Value, out var field)) + continue; + + var isEnabled = ExpressionsService.GetBoolValue(field.EnableExpression, context.ActionContext.FormStateData); - if (!isEnabled) - { - field.SetEnabled(true); - field.SetReadOnly(true); - } - } + if (!isEnabled) + context.SecretValues[parameter.Value] = result[parameter.Key]; + + context.Values[parameter.Value] = result[parameter.Key]; } result[IsResultValidKey] = true; - foreach (var parameter in context.FieldMap) - context.Values[parameter.Value] = result[parameter.Key]; - return PluginActionResult.Success(); } diff --git a/src/Plugins/Brasil/Actions/CepPluginActionHandler.cs b/src/Plugins/Brasil/Actions/CepPluginActionHandler.cs index 83c5a7d35..e0ea88851 100644 --- a/src/Plugins/Brasil/Actions/CepPluginActionHandler.cs +++ b/src/Plugins/Brasil/Actions/CepPluginActionHandler.cs @@ -7,14 +7,11 @@ using JJMasterData.Commons.Util; using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.UI.Components; -using JJMasterData.Core.UI.Html; namespace JJMasterData.Brasil.Actions; public class CepPluginActionHandler(ICepService cepService, - ExpressionsService expressionService, - HtmlComponentFactory htmlComponentFactory) + ExpressionsService expressionService) : BrasilPluginActionHandler(expressionService) { private ICepService CepService { get; } = cepService; @@ -45,8 +42,4 @@ protected override IEnumerable CustomFieldMapKeys return cepResult.ToDictionary(); } - - public override HtmlBuilder AdditionalInformationHtml { get; } = new(); - - private MessageBoxFactory MessageBoxFactory { get; } = htmlComponentFactory.MessageBox; } \ No newline at end of file diff --git a/src/Plugins/Brasil/Actions/CnpjPluginActionHandler.cs b/src/Plugins/Brasil/Actions/CnpjPluginActionHandler.cs index dd6987b6f..608c1668f 100644 --- a/src/Plugins/Brasil/Actions/CnpjPluginActionHandler.cs +++ b/src/Plugins/Brasil/Actions/CnpjPluginActionHandler.cs @@ -7,7 +7,6 @@ using JJMasterData.Commons.Util; using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.UI.Html; namespace JJMasterData.Brasil.Actions; @@ -20,7 +19,6 @@ public class CnpjPluginActionHandler(IReceitaFederalService receitaFederalServic private IReceitaFederalService ReceitaFederalService { get; } = receitaFederalService; public override Guid Id => GuidGenerator.FromValue(nameof(CnpjPluginActionHandler)); public override string Title => "Cnpj"; - public override HtmlBuilder? AdditionalInformationHtml => null; protected override IEnumerable CustomFieldMapKeys { diff --git a/src/Plugins/Brasil/Actions/CpfPluginActionHandler.cs b/src/Plugins/Brasil/Actions/CpfPluginActionHandler.cs index 9cc9dbf89..eac231470 100644 --- a/src/Plugins/Brasil/Actions/CpfPluginActionHandler.cs +++ b/src/Plugins/Brasil/Actions/CpfPluginActionHandler.cs @@ -7,7 +7,6 @@ using JJMasterData.Commons.Util; using JJMasterData.Core.DataDictionary.Models.Actions; using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.UI.Html; namespace JJMasterData.Brasil.Actions; @@ -20,7 +19,6 @@ public class CpfPluginActionHandler(IReceitaFederalService receitaFederalService private const string IgnoreDbFieldKey = "IgnoreDb"; public override Guid Id => GuidGenerator.FromValue(nameof(CpfPluginActionHandler)); public override string Title => "Cpf"; - public override HtmlBuilder? AdditionalInformationHtml => null; protected override IEnumerable CustomFieldMapKeys { get diff --git a/src/Plugins/Brasil/Configuration/MasterDataServiceBuilderExtensions.cs b/src/Plugins/Brasil/Configuration/MasterDataServiceBuilderExtensions.cs index 226f4d383..412d509fb 100644 --- a/src/Plugins/Brasil/Configuration/MasterDataServiceBuilderExtensions.cs +++ b/src/Plugins/Brasil/Configuration/MasterDataServiceBuilderExtensions.cs @@ -109,7 +109,7 @@ public static MasterDataServiceBuilder WithBrasilActionPlugins(this MasterDataSe builder.Services.TryAddSingleton(); builder.WithHubDevCnpjActionPlugin(); builder.WithHubDevCpfActionPlugin(); - builder.WithHubDevCepActionPlugin(); + builder.WithCepActionPlugin(); return builder; } diff --git a/src/Plugins/Brasil/Models/SintegraCnpjDto.cs b/src/Plugins/Brasil/Models/SintegraCnpjDto.cs index a7f1d5687..7a466fe44 100644 --- a/src/Plugins/Brasil/Models/SintegraCnpjDto.cs +++ b/src/Plugins/Brasil/Models/SintegraCnpjDto.cs @@ -4,7 +4,7 @@ namespace JJMasterData.Brasil.Models; -internal class SintegraCnpjDto +internal sealed class SintegraCnpjDto { [JsonProperty("code")] public required string Code { get; set; } diff --git a/src/Plugins/Brasil/Models/SintegraCpfDto.cs b/src/Plugins/Brasil/Models/SintegraCpfDto.cs index 17fef8505..f45df0c8f 100644 --- a/src/Plugins/Brasil/Models/SintegraCpfDto.cs +++ b/src/Plugins/Brasil/Models/SintegraCpfDto.cs @@ -2,7 +2,7 @@ using Newtonsoft.Json; -internal class SintegraCpfDto +internal sealed class SintegraCpfDto { [JsonProperty("code")] public required string Code { get; set; } diff --git a/src/Plugins/Brasil/Services/HubDevService.cs b/src/Plugins/Brasil/Services/HubDevService.cs index 8f0ff3600..530a06674 100644 --- a/src/Plugins/Brasil/Services/HubDevService.cs +++ b/src/Plugins/Brasil/Services/HubDevService.cs @@ -14,16 +14,11 @@ namespace JJMasterData.Brasil.Services; -public class HubDevService(HttpClient httpClient, - IMemoryCache memoryCache, - IOptions options) +public class HubDevService(HttpClient httpClient, IOptions options) : IReceitaFederalService { - private HttpClient HttpClient { get; } = httpClient; - private IMemoryCache MemoryCache { get; } = memoryCache; - private HubDevSettings Settings { get; } = options.Value; - - + private readonly HubDevSettings _settings = options.Value; + public bool IsHttps { get; set; } = true; public bool IgnoreDb { get; set; } @@ -34,13 +29,10 @@ private async Task Search(string endpoint, string identifier, Dictionary 0 }) { @@ -48,7 +40,7 @@ private async Task Search(string endpoint, string identifier, Dictionary(content, new JsonSerializerSettings() @@ -57,9 +49,7 @@ private async Task Search(string endpoint, string identifier, Dictionary()!; - - MemoryCache.Set(identifier, result, TimeSpan.FromMinutes(30)); - + return result; } catch (Exception ex) @@ -77,7 +67,7 @@ public Task SearchCpfAsync(string cpf, DateTime birthDate) { return Search("cpf", cpf, new Dictionary { - {"data", birthDate.ToString("ddMMyyyy")} + {"data", birthDate.ToString("dd/MM/yyyy")} }); } diff --git a/src/Plugins/Brasil/Services/ViaCepService.cs b/src/Plugins/Brasil/Services/ViaCepService.cs index fa1eb824d..ed827e8eb 100644 --- a/src/Plugins/Brasil/Services/ViaCepService.cs +++ b/src/Plugins/Brasil/Services/ViaCepService.cs @@ -9,38 +9,31 @@ namespace JJMasterData.Brasil.Services; -public class ViaCepService(HttpClient httpClient, IMemoryCache memoryCache) : ICepService +public class ViaCepService(HttpClient httpClient) : ICepService { private const string ViaCepUrl = "https://viacep.com.br/ws/"; - private HttpClient HttpClient { get; } = httpClient; - private IMemoryCache MemoryCache { get; } = memoryCache; public async Task SearchCepAsync(string cep) { - try - { - if (string.IsNullOrEmpty(cep)) - throw new ArgumentNullException(nameof(cep)); + try + { + if (string.IsNullOrEmpty(cep)) + throw new ArgumentNullException(nameof(cep)); - if (MemoryCache.TryGetValue(cep, out CepResult cepResult)) - return cepResult; - - var url = $"{ViaCepUrl}{StringManager.ClearCpfCnpjChars(cep)}/json"; - - var response = await HttpClient.GetAsync(url); - var content = await response.Content.ReadAsStringAsync(); - var result = CepResult.FromJson(content); + var url = $"{ViaCepUrl}{StringManager.ClearCpfCnpjChars(cep)}/json"; - if (result.Erro) - throw new ViaCepException("CEP inválido."); + var response = await httpClient.GetAsync(url); + var content = await response.Content.ReadAsStringAsync(); + var result = CepResult.FromJson(content); - MemoryCache.Set(cep, cepResult, TimeSpan.FromMinutes(30)); - - return result; - } - catch (Exception ex) - { - throw new ViaCepException(ex.Message, ex); - } + if (result.Erro) + throw new ViaCepException("CEP inválido."); + + return result; + } + catch (Exception ex) + { + throw new ViaCepException(ex.Message, ex); + } } } \ No newline at end of file diff --git a/src/Plugins/Hangfire/BackgroundTaskManager.cs b/src/Plugins/Hangfire/BackgroundTaskManager.cs index 964208028..4d8332499 100644 --- a/src/Plugins/Hangfire/BackgroundTaskManager.cs +++ b/src/Plugins/Hangfire/BackgroundTaskManager.cs @@ -2,10 +2,8 @@ using Hangfire; using Hangfire.States; using JJMasterData.Commons.Exceptions; -using JJMasterData.Commons.Localization; using JJMasterData.Commons.Tasks; using JJMasterData.Commons.Tasks.Progress; -using Microsoft.Extensions.Localization; namespace JJMasterData.Hangfire; diff --git a/src/Plugins/Hangfire/Hangfire.csproj b/src/Plugins/Hangfire/Hangfire.csproj index a2bae392c..e4f3a9ab7 100644 --- a/src/Plugins/Hangfire/Hangfire.csproj +++ b/src/Plugins/Hangfire/Hangfire.csproj @@ -7,6 +7,7 @@ https://www.github.com/jjconsulting/jjmasterdata 2.0.3 2.0.3 + 2.0.3.0-rc 2.0.3 hangfire,jjmasterdata JJMasterData.Hangfire diff --git a/src/Plugins/Hangfire/MasterDataServiceBuilderExtensions.cs b/src/Plugins/Hangfire/MasterDataServiceBuilderExtensions.cs index 52d79291a..c09bf90bd 100644 --- a/src/Plugins/Hangfire/MasterDataServiceBuilderExtensions.cs +++ b/src/Plugins/Hangfire/MasterDataServiceBuilderExtensions.cs @@ -1,5 +1,4 @@ using JJMasterData.Commons.Configuration; -using Microsoft.Extensions.DependencyInjection; namespace JJMasterData.Hangfire; public static class MasterDataServiceBuilderExtensions diff --git a/src/Plugins/Hangfire/TaskTrigger.cs b/src/Plugins/Hangfire/TaskTrigger.cs index bead20c59..82f73de63 100644 --- a/src/Plugins/Hangfire/TaskTrigger.cs +++ b/src/Plugins/Hangfire/TaskTrigger.cs @@ -6,15 +6,13 @@ using Hangfire.Console.Progress; using Hangfire.Server; using JJMasterData.Commons.Exceptions; -using JJMasterData.Commons.Localization; using JJMasterData.Commons.Tasks; using JJMasterData.Core.DataManager.Exportation.Abstractions; using JJMasterData.Core.DataManager.Importation; -using Microsoft.Extensions.Localization; namespace JJMasterData.Hangfire; -internal class TaskTrigger +internal sealed class TaskTrigger { public string RunInBackground(string key, IBackgroundTaskWorker worker) { diff --git a/src/Plugins/Hangfire/TaskWrapper.cs b/src/Plugins/Hangfire/TaskWrapper.cs index 671c3f8f4..76adb59e2 100644 --- a/src/Plugins/Hangfire/TaskWrapper.cs +++ b/src/Plugins/Hangfire/TaskWrapper.cs @@ -3,7 +3,7 @@ namespace JJMasterData.Hangfire; -internal class TaskWrapper +internal sealed class TaskWrapper { public string Key { get; set; } diff --git a/src/Plugins/MongoDB/Models/MongoDBFormElement.cs b/src/Plugins/MongoDB/Models/MongoDBFormElement.cs index 18914070c..229ae2823 100644 --- a/src/Plugins/MongoDB/Models/MongoDBFormElement.cs +++ b/src/Plugins/MongoDB/Models/MongoDBFormElement.cs @@ -5,7 +5,7 @@ namespace JJMasterData.MongoDB.Models; [BsonIgnoreExtraElements] [BsonNoId] -internal class MongoDBFormElement(FormElement formElement) +internal sealed class MongoDBFormElement(FormElement formElement) { public FormElement FormElement { get; set; } = formElement; public DateTime LastModified { get; set; } = DateTime.Now; diff --git a/src/Plugins/MongoDB/Repository/MongoDbDataDictionaryRepository.cs b/src/Plugins/MongoDB/Repository/MongoDbDataDictionaryRepository.cs index 2450d0503..ec96ea055 100644 --- a/src/Plugins/MongoDB/Repository/MongoDbDataDictionaryRepository.cs +++ b/src/Plugins/MongoDB/Repository/MongoDbDataDictionaryRepository.cs @@ -34,7 +34,7 @@ public FormElement GetFormElement(string elementName) return formElementQuery.First().FormElement; } - public async Task GetFormElementAsync(string elementName) + public async ValueTask GetFormElementAsync(string elementName) { var formElementQuery = await _formElementCollection.FindAsync(formElement => formElement.FormElement.Name == elementName); @@ -44,14 +44,14 @@ public async Task> GetFormElementListAsync(bool? apiSync = nul { var formElements = await _formElementCollection.FindAsync(_ => true); var formElementEntities = await formElements.ToListAsync(); - return formElementEntities.Select(f => f.FormElement).ToList(); + return formElementEntities.ConvertAll(f => f.FormElement); } public List GetFormElementList(bool? apiSync = null) { var formElements = _formElementCollection.Find(_ => true); var formElementEntities = formElements.ToList(); - return formElementEntities.Select(f => f.FormElement).ToList(); + return formElementEntities.ConvertAll(f => f.FormElement); } public async Task> GetNameListAsync() @@ -75,7 +75,7 @@ public async Task> GetFormElementInfoListAsync(DataD var list = await query.ToListAsync(); - return new ListResult(list.Select(formElement => new FormElementInfo(formElement.FormElement, formElement.LastModified)).ToList(),totalRecords); + return new ListResult(list.ConvertAll(formElement => new FormElementInfo(formElement.FormElement, formElement.LastModified)),totalRecords); } diff --git a/src/Plugins/NCalc/Configuration/MasterDataServiceBuilderExtensions.cs b/src/Plugins/NCalc/Configuration/MasterDataServiceBuilderExtensions.cs deleted file mode 100644 index ab6d43717..000000000 --- a/src/Plugins/NCalc/Configuration/MasterDataServiceBuilderExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -using JJMasterData.Commons.Configuration; -using JJMasterData.Core.DataManager.Expressions.Abstractions; -using JJMasterData.Core.DataManager.Expressions.Providers; -using Microsoft.Extensions.DependencyInjection; - -namespace JJMasterData.NCalc.Configuration; - -public static class MasterDataServiceBuilderExtensions -{ - public static MasterDataServiceBuilder WithNCalcExpressionProvider(this MasterDataServiceBuilder builder, NCalcExpressionProviderOptions? options = null) - { - options ??= new NCalcExpressionProviderOptions(); - - if (options.ReplaceDefaultExpressionProvider) - { - var defaultExpressionProvider = builder.Services.FirstOrDefault(descriptor => descriptor.ImplementationType == typeof(DefaultExpressionProvider)); - - builder.Services.Remove(defaultExpressionProvider); - } - - builder.Services.AddScoped(); - - builder.Services.PostConfigure(o => - { - o.ReplaceDefaultExpressionProvider = options.ReplaceDefaultExpressionProvider; - o.ExpressionOptions = options.ExpressionOptions; - o.AdditionalFunctions = options.AdditionalFunctions; - }); - - return builder; - } - - public static MasterDataServiceBuilder WithNCalcExpressions(this MasterDataServiceBuilder builder) - { - builder.WithNCalcExpressionProvider(new NCalcExpressionProviderOptions - { - ReplaceDefaultExpressionProvider = true, - AdditionalFunctions = - [ - (name, args) => - { - if (name == "now") - args.Result = DateTime.Now; - } - ] - }); - - return builder; - } - -} \ No newline at end of file diff --git a/src/Plugins/NCalc/Configuration/NCalcExpressionProviderOptions.cs b/src/Plugins/NCalc/Configuration/NCalcExpressionProviderOptions.cs deleted file mode 100644 index 4559637bb..000000000 --- a/src/Plugins/NCalc/Configuration/NCalcExpressionProviderOptions.cs +++ /dev/null @@ -1,22 +0,0 @@ -using NCalc; -using NCalc.Handlers; - -namespace JJMasterData.NCalc.Configuration; - -public class NCalcExpressionProviderOptions -{ - /// - /// Expressions starting with exp: will be executed by NCalc - /// - public bool ReplaceDefaultExpressionProvider { get; set; } = false; - - /// - /// ExpressionOptions [Flags] enum. Check NCalc wiki for more information - /// - public ExpressionOptions ExpressionOptions { get; set; } = ExpressionOptions.IgnoreCase | ExpressionOptions.CaseInsensitiveComparer; - - /// - /// Additional functions to be used at expressions. Check NCalc wiki for more information - /// - public List AdditionalFunctions { get; set; } = []; -} \ No newline at end of file diff --git a/src/Plugins/NCalc/NCalc.csproj b/src/Plugins/NCalc/NCalc.csproj deleted file mode 100644 index 3183ac58d..000000000 --- a/src/Plugins/NCalc/NCalc.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - netstandard2.0 - enable - 12 - enable - JJMasterData.NCalc - JJMasterData.NCalc - JJMasterData NCalc expressions support. - JJMasterData.png - JJMasterData.NCalc - ncalc,jjmasterdata - true - Update NCalc to 4.0. - 2.0.0 - 2.0.0 - 2.0.0 - - - - - - - - - - - - diff --git a/src/Plugins/NCalc/NCalcExpressionProvider.cs b/src/Plugins/NCalc/NCalcExpressionProvider.cs deleted file mode 100644 index 8fa1d0fbe..000000000 --- a/src/Plugins/NCalc/NCalcExpressionProvider.cs +++ /dev/null @@ -1,30 +0,0 @@ -using JJMasterData.Core.DataManager.Expressions; -using JJMasterData.Core.DataManager.Expressions.Abstractions; -using JJMasterData.NCalc.Configuration; -using Microsoft.Extensions.Options; -using NCalc; - -namespace JJMasterData.NCalc; - -public sealed class NCalcExpressionProvider(IOptionsSnapshot options) : - IAsyncExpressionProvider, - ISyncExpressionProvider -{ - public string Prefix => Options.ReplaceDefaultExpressionProvider ? "exp" : "ncalc"; - public string Title => Options.ReplaceDefaultExpressionProvider ? "Expression" : "NCalc"; - private NCalcExpressionProviderOptions Options { get; } = options.Value; - - public object? Evaluate(string expression, Dictionary parsedValues) - { - var replacedExpression = ExpressionHelper.ReplaceExpression(expression, parsedValues); - var ncalcExpression = new Expression(replacedExpression, Options.ExpressionOptions); - - foreach (var function in Options.AdditionalFunctions) - ncalcExpression.EvaluateFunction += function; - - return ncalcExpression.Evaluate(); - } - - public Task EvaluateAsync(string expression, Dictionary parsedValues) - => Task.FromResult(Evaluate(expression,parsedValues)); -} \ No newline at end of file diff --git a/src/Plugins/NCalc/README.MD b/src/Plugins/NCalc/README.MD deleted file mode 100644 index a8d99eae7..000000000 --- a/src/Plugins/NCalc/README.MD +++ /dev/null @@ -1,70 +0,0 @@ -# JJMasterData.NCalc - -## Configuration -Add the following method to `MasterDataServiceBuilder`: -```cs -builder.AddJJMasterDataWeb().WithNCalcExpressionProvider(); -``` - -## Options -- Check - -## Usage -Expressions are pre-parsed just like the Value and DataTable providers. Using {}. - -NCalc support more operators and alias than DataTable.Compute, like "if" statements or ternary operators. - -``` -if('{MyColumn}',1,0) -``` - -``` -'{MyColumn}' ? 'true result' : 'false result' -``` - -``` -'{MyColumn}' != 1 AND {'MyColumn'} <> 1 -``` - -You can also create your own custom functions. - -Check [NCalc wiki](https://github.com/ncalc/ncalc/wiki) for more information. - -## Differences from DataTable.Compute -- NCalc is a mathematical expressions evaluator not a SQL-like syntax parser -- Better performance is expected - -## Executing C# code at your expressions -You can execute C# code using the following example; - -Program.cs: - -``` -builder.Services.AddJJMasterDataWeb() -.WithNCalcExpressionProvider( - new NCalcExpressionProviderOptions - { - ReplaceDefaultExpressionProvider = true, - AdditionalFunctions = [ - (name, args) => - { - args.Result = name switch - { - "now" => DateTime.Now, - "myawesomefunction" => MyCustomClass.Execute(args.Parameters[0], args.Parameters[1]), - _ => args.Result - }; - } - ] - } -); - -``` - -> [!WARNING] -> NCalc sends function names in lowercase. - -At your expression: -``` -exp: myAwesomeFunction('{StringValue}',{IntValue}) -``` diff --git a/src/Plugins/Pdf/Pdf.csproj b/src/Plugins/Pdf/Pdf.csproj index c9dfd67ea..d322fc348 100644 --- a/src/Plugins/Pdf/Pdf.csproj +++ b/src/Plugins/Pdf/Pdf.csproj @@ -8,6 +8,7 @@ https://www.github.com/jjconsulting/jjmasterdata JJMasterData.png pdf,jjmasterdata + 2.0.4.0-rc 2.0.4 2.0.4 2.0.4 diff --git a/src/Plugins/Python/Configuration/MasterDataServiceBuilderExtensions.cs b/src/Plugins/Python/Configuration/MasterDataServiceBuilderExtensions.cs index a18bdd758..baea213dc 100644 --- a/src/Plugins/Python/Configuration/MasterDataServiceBuilderExtensions.cs +++ b/src/Plugins/Python/Configuration/MasterDataServiceBuilderExtensions.cs @@ -9,7 +9,6 @@ using JJMasterData.Python.Events; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Microsoft.Scripting.Hosting; namespace JJMasterData.Python.Configuration; public static class MasterDataServiceBuilderExtensions diff --git a/src/Plugins/Python/Engine/PythonEngineFactory.cs b/src/Plugins/Python/Engine/PythonEngineFactory.cs index 93bdc7109..3f7793828 100644 --- a/src/Plugins/Python/Engine/PythonEngineFactory.cs +++ b/src/Plugins/Python/Engine/PythonEngineFactory.cs @@ -11,7 +11,7 @@ namespace JJMasterData.Python.Engine; /// /// Gustavo Barros 05/01/2022 /// -public class PythonEngineFactory +public static class PythonEngineFactory { public static ScriptEngine CreateScriptEngine(IEnumerable additionalPaths) { diff --git a/src/Plugins/Swagger.AspNetCore/DataDictionaryDocumentFilter.cs b/src/Plugins/Swagger.AspNetCore/DataDictionaryDocumentFilter.cs index 613d0af78..5be25480a 100644 --- a/src/Plugins/Swagger.AspNetCore/DataDictionaryDocumentFilter.cs +++ b/src/Plugins/Swagger.AspNetCore/DataDictionaryDocumentFilter.cs @@ -3,7 +3,6 @@ using System.Reflection; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Repository.Abstractions; -using JJMasterData.Core.Tasks; using Microsoft.Extensions.DependencyInjection; namespace JJMasterData.Swagger.AspNetCore; diff --git a/src/Plugins/Swagger.AspNetCore/DataDictionaryOperationFactory.cs b/src/Plugins/Swagger.AspNetCore/DataDictionaryOperationFactory.cs index bdead0c92..b67bf1c2c 100644 --- a/src/Plugins/Swagger.AspNetCore/DataDictionaryOperationFactory.cs +++ b/src/Plugins/Swagger.AspNetCore/DataDictionaryOperationFactory.cs @@ -8,7 +8,7 @@ namespace JJMasterData.Swagger.AspNetCore; -internal class DataDictionaryOperationFactory +internal sealed class DataDictionaryOperationFactory { private FormElement FormElement { get; } private FormElementApiOptions Options { get; } diff --git a/src/Plugins/Swagger.AspNetCore/DataDictionaryPathItem.cs b/src/Plugins/Swagger.AspNetCore/DataDictionaryPathItem.cs index 2e3893013..3c33a5085 100644 --- a/src/Plugins/Swagger.AspNetCore/DataDictionaryPathItem.cs +++ b/src/Plugins/Swagger.AspNetCore/DataDictionaryPathItem.cs @@ -2,7 +2,7 @@ namespace JJMasterData.Swagger.AspNetCore; -internal class DataDictionaryPathItem +internal sealed class DataDictionaryPathItem { internal string Key { get; } internal OpenApiPathItem PathItem { get; } diff --git a/src/Plugins/Swagger.AspNetCore/Swagger.AspNetCore.csproj b/src/Plugins/Swagger.AspNetCore/Swagger.AspNetCore.csproj index 430189dab..27fd9f729 100644 --- a/src/Plugins/Swagger.AspNetCore/Swagger.AspNetCore.csproj +++ b/src/Plugins/Swagger.AspNetCore/Swagger.AspNetCore.csproj @@ -11,6 +11,7 @@ Swagger routes for JJMasterData API Data Dictionaries. https://www.github.com/jjconsulting/jjmasterdata swagger,jjmasterdata + 1.0.0.0-rc 1.0.0 false diff --git a/src/Plugins/Swagger/DataDictionaryDocumentFilter.cs b/src/Plugins/Swagger/DataDictionaryDocumentFilter.cs index 1db8b4d2d..f14c2f87a 100644 --- a/src/Plugins/Swagger/DataDictionaryDocumentFilter.cs +++ b/src/Plugins/Swagger/DataDictionaryDocumentFilter.cs @@ -4,11 +4,9 @@ using System.Text; using System.Threading; using System.Web.Http.Description; -using JJMasterData.Commons.Configuration; using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataDictionary.Repository.Abstractions; -using Microsoft.Extensions.DependencyInjection; namespace JJMasterData.Swagger { diff --git a/src/Web/Areas/DataDictionary/Controllers/ActionsController.cs b/src/Web/Areas/DataDictionary/Controllers/ActionsController.cs index 821170327..47944ee28 100644 --- a/src/Web/Areas/DataDictionary/Controllers/ActionsController.cs +++ b/src/Web/Areas/DataDictionary/Controllers/ActionsController.cs @@ -66,6 +66,7 @@ public async Task Add( nameof(UrlRedirectAction) => new UrlRedirectAction(), nameof(InternalAction) => new InternalAction(), nameof(SqlCommandAction) => new SqlCommandAction(), + nameof(HtmlTemplateAction) => new HtmlTemplateAction(), nameof(PluginAction) => new PluginAction { PluginId = pluginId!.Value @@ -78,7 +79,7 @@ public async Task Add( }; await PopulateViewBag(elementName, action, context, fieldName); - return View(action.GetType().Name, action); + return View(actionType, action); } private async Task EditActionResult( @@ -263,12 +264,24 @@ public Task ScriptAction(string elementName, ScriptAction scriptA } [HttpPost] - public Task SqlCommandAction(string elementName, SqlCommandAction sqlAction, + public Task SqlCommandAction( + string elementName, + SqlCommandAction sqlAction, ActionSource context, string? originalName, bool isActionSave, string? fieldName) { return EditActionResult(elementName, sqlAction, context, isActionSave, originalName, fieldName); } + + [HttpPost] + public Task HtmlTemplateAction( + string elementName, + HtmlTemplateAction htmlTemplateAction, + ActionSource context, + string? originalName, bool isActionSave, string? fieldName) + { + return EditActionResult(elementName, htmlTemplateAction, context, isActionSave, originalName, fieldName); + } [HttpPost] diff --git a/src/Web/Areas/DataDictionary/Controllers/ApiController.cs b/src/Web/Areas/DataDictionary/Controllers/ApiController.cs index 91a9fe9a7..30242ba5a 100644 --- a/src/Web/Areas/DataDictionary/Controllers/ApiController.cs +++ b/src/Web/Areas/DataDictionary/Controllers/ApiController.cs @@ -47,7 +47,7 @@ private static ApiViewModel PopulateViewModel(FormElement metadata) ApiOptions = metadata.ApiOptions, SynchronismMode = metadata.SynchronismMode, EnableSynchronism = metadata.EnableSynchronism, - ElementFields = new List(metadata.Fields.ToList().FindAll( + ElementFields = new List(metadata.Fields.FindAll( x => (x.IsPk | x.Filter.Type != FilterMode.None) & x.DataType != FieldType.DateTime & x.DataType != FieldType.DateTime2 & diff --git a/src/Web/Areas/DataDictionary/Controllers/ElementController.cs b/src/Web/Areas/DataDictionary/Controllers/ElementController.cs index 8c151c9f2..e12f76662 100644 --- a/src/Web/Areas/DataDictionary/Controllers/ElementController.cs +++ b/src/Web/Areas/DataDictionary/Controllers/ElementController.cs @@ -92,9 +92,10 @@ private void ConfigureUploadArea(JJUploadArea upload) upload.OnFileUploadedAsync += FileUploaded; } - private async Task FileUploaded(object? sender, FormUploadFileEventArgs e) + private async ValueTask FileUploaded(object? sender, FormUploadFileEventArgs e) { - await elementService.Import(new MemoryStream(e.File.Bytes)); + await using var ms = new MemoryStream(e.File.Bytes); + await elementService.Import(ms); if (ModelState.IsValid) { e.SuccessMessage = stringLocalizer["Dictionary imported successfully!"]; @@ -112,7 +113,7 @@ public IActionResult Duplicate(string? elementName = null) return View(new DuplicateElementViewModel{ OriginalElementName = elementName }); } - public async Task ClassSourceCode(string elementName) + public async ValueTask ClassSourceCode(string elementName) { ViewBag.ClassSourceCode = await classGenerationService.GetClassSourceCode(elementName); ViewBag.ElementName = elementName; @@ -187,11 +188,10 @@ public async Task Delete() { var formView = elementService.GetFormView(); var selectedGridValues = formView.GridView.GetSelectedGridValues(); - + var elementNamesToDelete = selectedGridValues .Where(value => value.TryGetValue("name", out var nameValue) && nameValue is string) - .Select(value => value["name"].ToString()) - .ToList(); + .Select(value => value["name"].ToString()); foreach (var elementName in elementNamesToDelete) { diff --git a/src/Web/Areas/DataDictionary/Controllers/FieldController.cs b/src/Web/Areas/DataDictionary/Controllers/FieldController.cs index 957e9afd4..302b1ef5b 100644 --- a/src/Web/Areas/DataDictionary/Controllers/FieldController.cs +++ b/src/Web/Areas/DataDictionary/Controllers/FieldController.cs @@ -72,7 +72,7 @@ public async Task Index(string elementName, string? fieldName, Fo [ExportModelState] public async Task Save(string elementName, FormElementField field, string? originalName) { - RecoverCustomAttibutes(ref field); + RecoverCustomAttributes(ref field); await fieldService.SaveFieldAsync(elementName, field, originalName); if (ModelState.IsValid) @@ -208,22 +208,28 @@ private async Task PopulateViewBag(FormElement formElement, FormElementField? fi ViewBag.FormElement = formElement; ViewBag.ElementName = formElement.Name; ViewBag.CodeMirrorHintList = JsonConvert.SerializeObject(BaseService.GetAutocompleteHintsList(formElement)); - ViewBag.MaxRequestLength = GetMaxRequestLength(); + + // ASP.NET Core enforces 30MB (~28.6 MiB) max request body size limit, be it Kestrel and HttpSys. + // Under normal circumstances, there is no need to increase the size of the HTTP request. + ViewBag.MaxRequestLength = 30720000; + ViewBag.FieldName = field.Name; ViewBag.Fields = formElement.Fields; if (field.Component is not FormComponent.Lookup && field.Component is not FormComponent.Search && field.Component is not FormComponent.ComboBox && - field.Component is not FormComponent.RadioButtonGroup) + field.Component is not FormComponent.RadioButtonGroup) + { return; - + } + field.DataItem.ElementMap ??= new DataElementMap(); ViewBag.ElementNameList = (await fieldService.GetElementsDictionaryAsync()).OrderBy(e=>e.Key); ViewBag.ElementFieldList = (await fieldService.GetElementFieldListAsync(field.DataItem.ElementMap)).OrderBy(e=>e.Key); } - private void RecoverCustomAttibutes(ref FormElementField field) + private void RecoverCustomAttributes(ref FormElementField field) { field.Attributes = new Dictionary(); switch (field.Component) @@ -279,15 +285,4 @@ or FormComponent.DateTime break; } } - - public int GetMaxRequestLength() - { - // ASP.NET Core enforces 30MB (~28.6 MiB) max request body size limit, be it Kestrel and HttpSys. - // Under normal circumstances, there is no need to increase the size of the HTTP request. - - const int maxRequestLength = 30720000; - - return maxRequestLength; - } - } \ No newline at end of file diff --git a/src/Web/Areas/DataDictionary/Controllers/LogController.cs b/src/Web/Areas/DataDictionary/Controllers/LogController.cs index 64624e088..13493c9df 100644 --- a/src/Web/Areas/DataDictionary/Controllers/LogController.cs +++ b/src/Web/Areas/DataDictionary/Controllers/LogController.cs @@ -43,12 +43,12 @@ public async Task Index(bool isModal) formView.GridView.OnRenderCellAsync += (_, args) => { if (!args.Field.Name.Equals(Options.MessageColumnName)) - return Task.CompletedTask; + return ValueTask.CompletedTask; var message = args.DataRow[Options.MessageColumnName].ToString()?.Replace("\n", "
"); args.HtmlResult = new HtmlBuilder(message ?? string.Empty); - return Task.CompletedTask; + return ValueTask.CompletedTask; }; var result = await formView.GetResultAsync(); diff --git a/src/Web/Areas/DataDictionary/Controllers/PanelController.cs b/src/Web/Areas/DataDictionary/Controllers/PanelController.cs index 517a438f3..470aa5cb0 100644 --- a/src/Web/Areas/DataDictionary/Controllers/PanelController.cs +++ b/src/Web/Areas/DataDictionary/Controllers/PanelController.cs @@ -107,25 +107,25 @@ private void PopulateViewBag(FormElement formElement, FormElementPanel panel) else if (TempData.TryGetValue("selected-tab", out var tempSelectedTab)) ViewBag.Tab = tempSelectedTab?.ToString()!; - if (TempData.ContainsKey("error")) - ViewBag.Error = TempData["error"]!; + if (TempData.TryGetValue("error", out object? value)) + ViewBag.Error = value!; ViewBag.MenuId = "Panels"; ViewBag.ElementName = formElement.Name; ViewBag.PanelId = panel.PanelId; ViewBag.Panels = formElement.Panels; - ViewBag.AvailableFields = GetAvailableFields(formElement, panel); + ViewBag.AvailableFields = GetAvailableFields(formElement); ViewBag.CodeMirrorHintList = JsonConvert.SerializeObject(BaseService.GetAutocompleteHintsList(formElement)); ViewBag.SelectedFields = (panel.PanelId > 0) ? - formElement.Fields.ToList().FindAll(x => x.PanelId == panel.PanelId) : []; + formElement.Fields.FindAll(x => x.PanelId == panel.PanelId) : []; } - protected List GetAvailableFields(FormElement formElement, FormElementPanel panel) + protected List GetAvailableFields(FormElement formElement) { var list = new List(); if ((string?)Request.Query["enabled_fields"] == null) { - list = formElement.Fields.ToList().FindAll(x => x.PanelId == 0); + list = formElement.Fields.FindAll(x => x.PanelId == 0); } else { diff --git a/src/Web/Areas/DataDictionary/Models/AboutViewModel.cs b/src/Web/Areas/DataDictionary/Models/AboutViewModel.cs index 3a53134c5..80ec9b505 100644 --- a/src/Web/Areas/DataDictionary/Models/AboutViewModel.cs +++ b/src/Web/Areas/DataDictionary/Models/AboutViewModel.cs @@ -1,6 +1,4 @@ -using System.Reflection; - -namespace JJMasterData.Web.Areas.DataDictionary.Models; +namespace JJMasterData.Web.Areas.DataDictionary.Models; public sealed class AboutViewModel { diff --git a/src/Web/Areas/DataDictionary/Services/LocalizationService.cs b/src/Web/Areas/DataDictionary/Services/LocalizationService.cs index 242638285..35b333e8d 100644 --- a/src/Web/Areas/DataDictionary/Services/LocalizationService.cs +++ b/src/Web/Areas/DataDictionary/Services/LocalizationService.cs @@ -34,10 +34,10 @@ public JJFormView GetFormView() return formView; } - private Task ClearCache(object sender, FormAfterActionEventArgs args) + private ValueTask ClearCache(object sender, FormAfterActionEventArgs args) { ClearCache(); - return Task.CompletedTask; + return ValueTask.CompletedTask; } public async Task GetAllStringsFile() diff --git a/src/Web/Areas/DataDictionary/Services/SettingsService.cs b/src/Web/Areas/DataDictionary/Services/SettingsService.cs index c810af6ed..b21c3aa2a 100644 --- a/src/Web/Areas/DataDictionary/Services/SettingsService.cs +++ b/src/Web/Areas/DataDictionary/Services/SettingsService.cs @@ -1,7 +1,6 @@ using JJMasterData.Commons.Configuration.Options; using JJMasterData.Commons.Configuration.Options.Abstractions; using JJMasterData.Commons.Data; -using JJMasterData.Commons.Data.Entity.Models; using JJMasterData.Commons.Localization; using JJMasterData.Core.Configuration.Options; using JJMasterData.Core.DataDictionary.Repository.Abstractions; diff --git a/src/Web/Areas/DataDictionary/Views/Actions/HtmlTemplateAction.cshtml b/src/Web/Areas/DataDictionary/Views/Actions/HtmlTemplateAction.cshtml new file mode 100644 index 000000000..960999047 --- /dev/null +++ b/src/Web/Areas/DataDictionary/Views/Actions/HtmlTemplateAction.cshtml @@ -0,0 +1,99 @@ +@inject IStringLocalizer StringLocalizer +@inject IOptionsSnapshot Options + +@model JJMasterData.Core.DataDictionary.Models.Actions.HtmlTemplateAction + +@{ + Layout = Options.Value.ModalLayoutPath; +} + + +@section Scripts{ + + } + + + +
+
+ +
+ +
+
+
+ +  @StringLocalizer["(Type Ctrl+Space to autocomplete)"] +
+ @Html.TextAreaFor(model => model.SqlCommand, new { @class = "form-control", rows = "15" }) +
+
+
+
+ +
+
+
+ +  @StringLocalizer["Here we use a Liquid template to render the HTML. Use the DataSource property to access your tables and columns."] +
+ +
+
+
+
+ +
+ +
+
+ +
+ + diff --git a/src/Web/Areas/DataDictionary/Views/Actions/InternalAction.cshtml b/src/Web/Areas/DataDictionary/Views/Actions/InternalAction.cshtml index bdc6cd545..92ab48831 100644 --- a/src/Web/Areas/DataDictionary/Views/Actions/InternalAction.cshtml +++ b/src/Web/Areas/DataDictionary/Views/Actions/InternalAction.cshtml @@ -92,72 +92,69 @@ -
-
-

@StringLocalizer["Relation"]

-
-
-
-
-
- - @Html.DropDownList("redirectField", new SelectList(ViewBag.RedirectFieldList, "Key", "Value"), new { @class = "form-control" }) -
-
- - @Html.DropDownList("internalField", new SelectList(ViewBag.InternalFieldList, "Key", "Value"), new { @class = "form-control" }) + +
+
+ + @Html.DropDownList("redirectField", new SelectList(ViewBag.RedirectFieldList, "Key", "Value"), new { @class = "form-control" }) +
+
+ + @Html.DropDownList("internalField", new SelectList(ViewBag.InternalFieldList, "Key", "Value"), new { @class = "form-control" }) +
-
-
-
- +
+
+ +
-
-
-
+
+
- - +
+ - - + + @if (Model.ElementRedirect.RelationFields.Count == 0) { - - - + + + } @for (int i = 0; i < Model.ElementRedirect.RelationFields.Count; i++) { - - - - - + + + + + } - -
@StringLocalizer["Id"] @StringLocalizer["Relationship"] @StringLocalizer["Remove"]
@StringLocalizer["No relationships found"]
@StringLocalizer["No relationships found"]
@i - @Model.ElementRedirect.RelationFields[i].RedirectField - - @Model.ElementRedirect.RelationFields[i].InternalField - @Html.HiddenFor(model => model.ElementRedirect.RelationFields[i].InternalField) - @Html.HiddenFor(model => model.ElementRedirect.RelationFields[i].RedirectField) - - - - -
@i + @Model.ElementRedirect.RelationFields[i].RedirectField + + @Model.ElementRedirect.RelationFields[i].InternalField + @Html.HiddenFor(model => model.ElementRedirect.RelationFields[i].InternalField) + @Html.HiddenFor(model => model.ElementRedirect.RelationFields[i].RedirectField) + + + + +
+ + +
-
+ +
diff --git a/src/Web/Areas/DataDictionary/Views/Actions/SqlCommandAction.cshtml b/src/Web/Areas/DataDictionary/Views/Actions/SqlCommandAction.cshtml index 8360dc2e3..86eb551a8 100644 --- a/src/Web/Areas/DataDictionary/Views/Actions/SqlCommandAction.cshtml +++ b/src/Web/Areas/DataDictionary/Views/Actions/SqlCommandAction.cshtml @@ -11,7 +11,7 @@ @section Scripts{ } diff --git a/src/Web/Areas/DataDictionary/Views/Entity/Edit.cshtml b/src/Web/Areas/DataDictionary/Views/Entity/Edit.cshtml index 81af0a183..d8bc72704 100644 --- a/src/Web/Areas/DataDictionary/Views/Entity/Edit.cshtml +++ b/src/Web/Areas/DataDictionary/Views/Entity/Edit.cshtml @@ -1,6 +1,5 @@ @inject IStringLocalizer StringLocalizer @using JJMasterData.Core.UI.Components -@using JJMasterData.Web.Extensions @model JJMasterData.Web.Areas.DataDictionary.Models.EntityViewModel; @{ diff --git a/src/Web/Areas/DataDictionary/Views/Field/Index.cshtml b/src/Web/Areas/DataDictionary/Views/Field/Index.cshtml index bdfa0de58..cd2b938fd 100644 --- a/src/Web/Areas/DataDictionary/Views/Field/Index.cshtml +++ b/src/Web/Areas/DataDictionary/Views/Field/Index.cshtml @@ -194,10 +194,10 @@ } CodeMirrorWrapper.setupCodeMirror("@Html.NameFor(m=>m.GridRenderingTemplate)",{ - mode: "text/html", + mode: "text/x-liquid", hintList: @Html.Raw(ViewBag.CodeMirrorHintList), hintKey: '{', - size: 100 + size: 320 }); } diff --git a/src/Web/Areas/DataDictionary/Views/Field/_DataItemSql.cshtml b/src/Web/Areas/DataDictionary/Views/Field/_DataItemSql.cshtml index d4b22f0ef..6f38891c9 100644 --- a/src/Web/Areas/DataDictionary/Views/Field/_DataItemSql.cshtml +++ b/src/Web/Areas/DataDictionary/Views/Field/_DataItemSql.cshtml @@ -7,10 +7,10 @@ bool showIcon = Model.DataItem!.ShowIcon; } -
+
-  @StringLocalizer["(Type Ctrl+Space to AutoComplete)"] +  @StringLocalizer["(Type Ctrl+Space to autocomplete)"]
@Html.TextAreaFor(model => model.DataItem!.Command!.Sql, new { @class = "form-control", rows = "10" })
diff --git a/src/Web/Areas/DataDictionary/Views/Field/_FieldLayout.cshtml b/src/Web/Areas/DataDictionary/Views/Field/_FieldLayout.cshtml index 2261227cc..eb7468572 100644 --- a/src/Web/Areas/DataDictionary/Views/Field/_FieldLayout.cshtml +++ b/src/Web/Areas/DataDictionary/Views/Field/_FieldLayout.cshtml @@ -44,7 +44,7 @@
- +
diff --git a/src/Web/Areas/DataDictionary/Views/Indexes/Index.cshtml b/src/Web/Areas/DataDictionary/Views/Indexes/Index.cshtml index 381f2fa25..9cc7cb075 100644 --- a/src/Web/Areas/DataDictionary/Views/Indexes/Index.cshtml +++ b/src/Web/Areas/DataDictionary/Views/Indexes/Index.cshtml @@ -68,12 +68,10 @@ if (i > 0) { - ,@(item.Columns[i]) - } - else - { - @(item.Columns[i]) + @:, } + + @item.Columns[i] } diff --git a/src/Web/Areas/MasterData/Controllers/FormController.cs b/src/Web/Areas/MasterData/Controllers/FormController.cs index 0c52c9eec..3b9fd2d08 100644 --- a/src/Web/Areas/MasterData/Controllers/FormController.cs +++ b/src/Web/Areas/MasterData/Controllers/FormController.cs @@ -2,7 +2,6 @@ using JJMasterData.Core.DataDictionary; using JJMasterData.Core.Extensions; using JJMasterData.Core.UI.Components; -using JJMasterData.Web.Areas.MasterData.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; @@ -24,13 +23,10 @@ public async Task Render(string elementName) if (result is IActionResult actionResult) return actionResult; - var model = new FormViewModel - { - FormTitle = formView.FormElement.Name, - FormViewHtml = result.Content - }; + ViewData["Title"] = formView.FormElement.Name; + ViewData["FormViewHtml"] = result.Content; - return View(model); + return View(); } private void ConfigureFormView(JJFormView formView) diff --git a/src/Web/Areas/MasterData/Controllers/IconsController.cs b/src/Web/Areas/MasterData/Controllers/IconsController.cs index a4788b70c..58621d125 100644 --- a/src/Web/Areas/MasterData/Controllers/IconsController.cs +++ b/src/Web/Areas/MasterData/Controllers/IconsController.cs @@ -5,7 +5,7 @@ namespace JJMasterData.Web.Areas.MasterData.Controllers; public class IconsController : MasterDataController { - public IActionResult Index(string inputId) + public PartialViewResult Index(string inputId) { return PartialView("_Icons", new IconViewModel{InputId = inputId}); } diff --git a/src/Web/Areas/MasterData/Controllers/InternalRedirectController.cs b/src/Web/Areas/MasterData/Controllers/InternalRedirectController.cs index 5b60a4a79..cdf7a5cc1 100644 --- a/src/Web/Areas/MasterData/Controllers/InternalRedirectController.cs +++ b/src/Web/Areas/MasterData/Controllers/InternalRedirectController.cs @@ -1,25 +1,23 @@ using System.Web; using JJMasterData.Commons.Data.Entity.Models; -using JJMasterData.Commons.Exceptions; -using JJMasterData.Commons.Localization; using JJMasterData.Commons.Security.Cryptography.Abstractions; using JJMasterData.Core.DataDictionary.Models; using JJMasterData.Core.DataManager.Expressions; using JJMasterData.Core.DataManager.Models; +using JJMasterData.Core.DataManager.Services; using JJMasterData.Core.Extensions; +using JJMasterData.Core.Http.Abstractions; using JJMasterData.Core.UI.Components; using JJMasterData.Web.Areas.MasterData.Models; -using JJMasterData.Web.Extensions; using Microsoft.AspNetCore.Mvc; -using Microsoft.Data.SqlClient; -using Microsoft.Extensions.Localization; namespace JJMasterData.Web.Areas.MasterData.Controllers; public class InternalRedirectController( ExpressionsService expressionsService, IComponentFactory componentFactory, - IStringLocalizer localizer, + FormService formService, + IHttpRequest request, IEncryptionService encryptionService) : MasterDataController { private string? _elementName; @@ -118,21 +116,12 @@ public async Task Save(string parameters) panel.SetUserValues("USERID", userId); var values = await panel.GetFormValuesAsync(); - var errors = panel.ValidateFields(values, PageState.Update); - var formElement = panel.FormElement; - try - { - if (errors.Count == 0) - await panel.EntityRepository.SetValuesAsync(formElement, values); - } - catch (SqlException ex) - { - errors.Add("DB", localizer[ExceptionManager.GetMessage(ex)]); - } - if (errors.Count > 0) + var letter =await formService.InsertOrReplaceAsync(panel.FormElement, values, new DataContext(request,DataContextSource.Form,userId)); + + if (letter.Errors.Count > 0) { - ViewBag.Error = componentFactory.Html.ValidationSummary.Create(errors).GetHtml(); + ViewBag.Error = componentFactory.Html.ValidationSummary.Create(letter.Errors).GetHtml(); ViewBag.Success = false; } else diff --git a/src/Web/Areas/MasterData/Models/FormViewModel.cs b/src/Web/Areas/MasterData/Models/FormViewModel.cs deleted file mode 100644 index 36153ab5f..000000000 --- a/src/Web/Areas/MasterData/Models/FormViewModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JJMasterData.Web.Areas.MasterData.Models; - -public sealed class FormViewModel -{ - public required string FormTitle { get; init; } - public required string FormViewHtml { get; init; } -} \ No newline at end of file diff --git a/src/Web/Areas/MasterData/Views/Form/Render.cshtml b/src/Web/Areas/MasterData/Views/Form/Render.cshtml index 906267585..be3b8bc2c 100644 --- a/src/Web/Areas/MasterData/Views/Form/Render.cshtml +++ b/src/Web/Areas/MasterData/Views/Form/Render.cshtml @@ -1,9 +1,10 @@ -@model JJMasterData.Web.Areas.MasterData.Models.FormViewModel - -@{ - ViewData["Title"] = Model.FormTitle; +@{ + if (TempData.TryGetValue("Title", out var title)) + { + ViewData["Title"] = title; + } } - @Html.Raw(Model.FormViewHtml) + @Html.Raw(ViewData["FormViewHtml"]) diff --git a/src/Web/Areas/MasterData/Views/Icons/_Icons.cshtml b/src/Web/Areas/MasterData/Views/Icons/_Icons.cshtml index 854b5246c..f742bd848 100644 --- a/src/Web/Areas/MasterData/Views/Icons/_Icons.cshtml +++ b/src/Web/Areas/MasterData/Views/Icons/_Icons.cshtml @@ -1,5 +1,4 @@ -@using JJMasterData.Core.DataDictionary.Models -@model IconViewModel +@model IconViewModel @inject IStringLocalizer StringLocalizer