From 3d5a277bc63c51fa10a29e6ef8579c877f4cf46a Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Tue, 16 Jun 2026 10:01:13 +0200 Subject: [PATCH 01/10] refactor admin bundle: replace custom controller traits with dedicated handler classes (CQRS-Lite) --- config/opendxp/routing.yaml | 2 +- config/services.yaml | 85 +- .../object/helpers/customLayoutEditor.js | 2 +- src/Builder/AdminSettingsAssembler.php | 239 ++ .../Admin/Asset/AssetController.php | 2483 +---------------- .../Admin/Asset/AssetCopyController.php | 121 + .../Admin/Asset/AssetDownloadController.php | 161 ++ .../Admin/Asset/AssetEditorController.php | 57 + .../Admin/Asset/AssetHelperController.php | 1047 +------ .../Admin/Asset/AssetMediaController.php | 158 ++ .../Admin/Asset/AssetThumbnailController.php | 210 ++ .../Admin/Asset/AssetUploadController.php | 144 + .../Admin/Asset/AssetVersionController.php | 95 + .../Admin/DataObject/ClassController.php | 2119 ++------------ .../ClassificationstoreController.php | 1666 +++-------- .../DataObject/CustomLayoutController.php | 153 + .../Admin/DataObject/DataObjectController.php | 2079 ++------------ .../DataObject/DataObjectCopyController.php | 165 ++ .../DataObject/DataObjectHelperController.php | 1731 ++---------- .../DataObjectVersionController.php | 100 + .../DataObject/FieldCollectionController.php | 151 + .../DataObject/ObjectBrickController.php | 145 + .../DataObject/QuantityValueController.php | 284 +- .../Admin/DataObject/VariantsController.php | 65 +- .../Admin/Document/DocumentController.php | 1416 ++-------- .../Admin/Document/DocumentControllerBase.php | 396 +-- .../Admin/Document/DocumentCopyController.php | 189 ++ .../Document/DocumentVersionController.php | 86 + .../Admin/Document/EmailController.php | 104 +- .../Admin/Document/FolderController.php | 49 +- .../Admin/Document/HardlinkController.php | 89 +- .../Admin/Document/LinkController.php | 131 +- .../Admin/Document/PageController.php | 353 +-- .../Admin/Document/RenderletController.php | 87 +- .../Admin/Document/SnippetController.php | 116 +- src/Controller/Admin/ElementController.php | 910 ++---- .../Admin/ElementControllerBase.php | 188 +- src/Controller/Admin/EmailController.php | 511 +--- src/Controller/Admin/IndexController.php | 383 +-- src/Controller/Admin/InstallController.php | 10 +- src/Controller/Admin/LoginController.php | 301 +- src/Controller/Admin/MiscController.php | 185 +- .../Admin/NotificationController.php | 224 +- src/Controller/Admin/PortalController.php | 326 +-- src/Controller/Admin/RecyclebinController.php | 186 +- .../Admin/Settings/ThumbnailController.php | 94 + .../Settings/VideoThumbnailController.php | 110 + src/Controller/Admin/SettingsController.php | 1201 ++------ src/Controller/Admin/TagsController.php | 410 +-- .../Admin/TranslationController.php | 790 +----- src/Controller/Admin/User/RoleController.php | 85 + .../Admin/User/UserProfileController.php | 108 + src/Controller/Admin/UserController.php | 1134 ++------ src/Controller/Admin/WorkflowController.php | 429 +-- src/Controller/AdminAbstractController.php | 5 + src/Controller/GDPR/AdminController.php | 16 +- src/Controller/GDPR/AssetController.php | 24 +- src/Controller/GDPR/DataObjectController.php | 24 +- .../GDPR/OpenDxpUsersController.php | 24 +- src/Controller/GDPR/SentMailController.php | 24 +- .../Traits/ApplySchedulerDataTrait.php | 51 - .../Traits/DocumentTreeConfigTrait.php | 6 - src/Controller/Traits/UserNameTrait.php | 64 - src/Dto/Admin/AdminSettingsDto.php | 115 + src/Dto/Admin/StatisticsDto.php | 30 + src/Dto/Response/ApiResponse.php | 54 + src/Event/AdminEvents.php | 31 +- .../Asset/AssetNotFoundException.php | 28 + .../Asset/AssetVersionNotFoundException.php | 28 + .../DataObjectNotFoundException.php | 28 + .../Document/DocumentNotFoundException.php | 28 + src/Exception/ElementLockedException.php | 46 + src/Exception/NotFoundException.php | 22 + src/Factory/DashboardFactory.php | 34 + src/Factory/ElementServiceFactory.php | 44 + src/Factory/LoginPageFactory.php | 45 + src/Handler/Admin/IndexActionHandler.php | 139 + src/Handler/Admin/SettingsResult.php | 25 + src/Handler/Admin/StatisticsActionHandler.php | 57 + src/Handler/Asset/AssetResult.php | 27 + .../Asset/ClearAssetThumbnailHandler.php | 41 + src/Handler/Asset/Copy/ChildIdsResult.php | 26 + src/Handler/Asset/Copy/CopyAssetHandler.php | 77 + src/Handler/Asset/Copy/CopyAssetResult.php | 27 + .../Asset/Copy/GetAssetChildIdsHandler.php | 40 + .../Asset/CreateAssetFolderHandler.php | 52 + src/Handler/Asset/DeleteAssetHandler.php | 69 + src/Handler/Asset/DeleteAssetResult.php | 26 + .../Asset/Download/AddFilesToZipHandler.php | 100 + .../Asset/Download/DownloadAssetHandler.php | 37 + .../DownloadImageThumbnailHandler.php | 96 + .../Download/DownloadImageThumbnailResult.php | 30 + .../Asset/Download/DownloadZipHandler.php | 33 + .../Asset/Download/DownloadZipResult.php | 26 + .../Download/GetDownloadZipJobsHandler.php | 98 + .../Download/GetDownloadZipJobsResult.php | 26 + .../Editor/LoadAssetForEditorHandler.php | 37 + .../Asset/Editor/SaveImageEditorHandler.php | 49 + src/Handler/Asset/GetAssetChildrenHandler.php | 115 + src/Handler/Asset/GetAssetChildrenResult.php | 30 + src/Handler/Asset/GetAssetDataHandler.php | 230 ++ src/Handler/Asset/GetAssetDataResult.php | 25 + .../Helper/DeleteGridColumnConfigHandler.php | 46 + .../Asset/Helper/DoAssetExportHandler.php | 138 + .../Asset/Helper/ExecuteAssetBatchHandler.php | 38 + .../Asset/Helper/GetAssetBatchJobsHandler.php | 38 + .../Asset/Helper/GetAssetBatchJobsResult.php | 25 + ...GetAssetMetadataForColumnConfigHandler.php | 77 + .../GetAssetMetadataForColumnConfigResult.php | 25 + .../Asset/Helper/GetExportJobsHandler.php | 51 + .../Asset/Helper/GetExportJobsResult.php | 26 + .../Helper/MarkGridConfigFavouriteHandler.php | 67 + .../Helper/MarkGridConfigFavouriteResult.php | 25 + .../Helper/SaveGridColumnConfigHandler.php | 109 + .../Helper/SaveGridColumnConfigResult.php | 27 + src/Handler/Asset/Media/AssetTextResult.php | 25 + .../Asset/Media/GetAssetTextHandler.php | 41 + .../Asset/Media/GetDocumentPreviewHandler.php | 103 + .../Asset/Media/GetVideoPreviewHandler.php | 44 + .../Asset/Media/PreviewDocumentResult.php | 34 + .../Asset/Media/PreviewVideoResult.php | 30 + .../Asset/Media/ServeVideoPreviewHandler.php | 54 + .../Asset/Media/ServeVideoPreviewResult.php | 26 + src/Handler/Asset/SaveAssetHandler.php | 46 + src/Handler/Asset/SaveAssetResult.php | 27 + .../Asset/Thumbnail/FolderThumbnailResult.php | 26 + .../Thumbnail/GetDocumentThumbnailHandler.php | 64 + .../Thumbnail/GetDocumentThumbnailResult.php | 26 + .../GetFolderContentPreviewHandler.php | 101 + .../GetFolderContentPreviewResult.php | 26 + .../Thumbnail/GetFolderThumbnailHandler.php | 48 + .../Thumbnail/GetImageThumbnailHandler.php | 95 + .../Thumbnail/GetImageThumbnailResult.php | 30 + .../Thumbnail/GetVideoThumbnailHandler.php | 97 + .../Thumbnail/GetVideoThumbnailResult.php | 26 + src/Handler/Asset/UpdateAssetHandler.php | 94 + src/Handler/Asset/UpdateAssetResult.php | 25 + .../Asset/Upload/CheckAssetExistsHandler.php | 42 + .../Asset/Upload/ImportZipFilesHandler.php | 112 + src/Handler/Asset/Upload/ImportZipHandler.php | 77 + src/Handler/Asset/Upload/ImportZipResult.php | 26 + .../Asset/Upload/ReplaceAssetHandler.php | 77 + .../Asset/Version/PublishVersionHandler.php | 54 + .../Asset/Version/ShowVersionHandler.php | 54 + .../Asset/Version/ShowVersionResult.php | 31 + .../DataObject/AddObjectFolderHandler.php | 59 + src/Handler/DataObject/AddObjectHandler.php | 84 + src/Handler/DataObject/AddObjectResult.php | 26 + .../ChangeChildrenSortByHandler.php | 155 + .../DataObject/ClassDef/AddClassHandler.php | 51 + .../DataObject/ClassDef/AddClassResult.php | 25 + .../DataObject/ClassDef/BulkCommitHandler.php | 132 + .../ClassDef/BulkExportPrepareHandler.php | 38 + .../DataObject/ClassDef/BulkImportHandler.php | 83 + .../DataObject/ClassDef/BulkImportResult.php | 25 + .../ClassDef/DeleteClassHandler.php | 31 + .../ClassDef/DeleteSelectOptionsHandler.php | 34 + .../ClassDef/DoBulkExportHandler.php | 73 + .../ClassDef/DoBulkExportResult.php | 25 + .../ClassDef/ExportClassHandler.php | 43 + .../DataObject/ClassDef/ExportClassResult.php | 26 + .../ClassDef/GetAssetTypesHandler.php | 33 + .../ClassDef/GetAssetTypesResult.php | 25 + .../GetClassBulkExportListHandler.php | 101 + .../ClassDef/GetClassBulkExportListResult.php | 25 + ...tClassDefinitionForColumnConfigHandler.php | 93 + ...etClassDefinitionForColumnConfigResult.php | 25 + .../DataObject/ClassDef/GetClassHandler.php | 39 + .../ClassDef/GetClassIconsHandler.php | 113 + .../ClassDef/GetClassIconsResult.php | 25 + .../DataObject/ClassDef/GetClassResult.php | 25 + .../ClassDef/GetClassTreeHandler.php | 149 + .../ClassDef/GetClassTreeResult.php | 25 + .../ClassDef/GetDocumentTypesHandler.php | 33 + .../ClassDef/GetDocumentTypesResult.php | 25 + .../ClassDef/GetSelectOptionsHandler.php | 38 + .../ClassDef/GetSelectOptionsResult.php | 25 + .../ClassDef/GetSelectOptionsTreeHandler.php | 75 + .../ClassDef/GetSelectOptionsTreeResult.php | 25 + .../GetSelectOptionsUsagesHandler.php | 44 + .../ClassDef/GetSelectOptionsUsagesResult.php | 25 + .../ClassDef/GetTextLayoutPreviewHandler.php | 66 + .../ClassDef/GetTextLayoutPreviewResult.php | 25 + .../ClassDef/GetVideoAllowedTypesHandler.php | 43 + .../ClassDef/GetVideoAllowedTypesResult.php | 25 + .../ClassDef/ImportClassHandler.php | 38 + .../ClassDef/SaveClassDefinitionHandler.php | 104 + .../ClassDef/SaveClassDefinitionResult.php | 27 + .../ClassDef/SaveSelectOptionsHandler.php | 59 + .../ClassDef/SaveSelectOptionsResult.php | 25 + .../SuggestClassIdentifierHandler.php | 35 + .../ClassDef/SuggestClassIdentifierResult.php | 26 + .../AddCollectionsHandler.php | 128 + .../AddCollectionsResult.php | 25 + .../Classificationstore/AddGroupsHandler.php | 90 + .../Classificationstore/AddGroupsResult.php | 25 + .../AddPropertyHandler.php | 44 + .../Classificationstore/AddPropertyResult.php | 25 + .../CreateCollectionHandler.php | 37 + .../CreateCollectionResult.php | 25 + .../CreateGroupHandler.php | 39 + .../Classificationstore/CreateGroupResult.php | 26 + .../CreateStoreHandler.php | 42 + .../Classificationstore/CreateStoreResult.php | 25 + .../DeleteCollectionHandler.php | 36 + .../DeleteCollectionRelationHandler.php | 31 + .../DeleteGroupHandler.php | 29 + .../DeletePropertyHandler.php | 30 + .../DeleteRelationHandler.php | 31 + .../Classificationstore/EditStoreHandler.php | 49 + .../GetCollectionRelationsHandler.php | 110 + .../GetCollectionRelationsResult.php | 26 + .../GetCollectionsHandler.php | 161 ++ .../GetCollectionsResult.php | 26 + .../Classificationstore/GetGroupsHandler.php | 147 + .../Classificationstore/GetGroupsResult.php | 26 + .../Classificationstore/GetPageHandler.php | 82 + .../Classificationstore/GetPageResult.php | 25 + .../GetPropertiesHandler.php | 191 ++ .../GetPropertiesResult.php | 26 + .../GetRelationsHandler.php | 135 + .../GetRelationsResult.php | 26 + .../GetStoreTreeHandler.php | 47 + .../GetStoreTreeResult.php | 25 + .../Classificationstore/ListStoresHandler.php | 36 + .../Classificationstore/ListStoresResult.php | 25 + .../SaveCollectionRelationsHandler.php | 47 + .../SaveCollectionRelationsResult.php | 25 + .../SaveRelationHandler.php | 42 + .../SaveRelationResult.php | 25 + .../SearchRelationsHandler.php | 134 + .../SearchRelationsResult.php | 26 + .../UpdateCollectionHandler.php | 56 + .../UpdateCollectionResult.php | 25 + .../UpdateGroupHandler.php | 56 + .../Classificationstore/UpdateGroupResult.php | 25 + .../UpdatePropertyHandler.php | 43 + .../UpdatePropertyResult.php | 25 + .../DataObject/Copy/ChildIdsResult.php | 26 + .../DataObject/Copy/CopyDataObjectHandler.php | 89 + .../DataObject/Copy/CopyDataObjectResult.php | 28 + .../Copy/GetDataObjectChildIdsHandler.php | 41 + .../Copy/RewriteDataObjectIdsHandler.php | 39 + .../CustomLayout/AddCustomLayoutHandler.php | 54 + .../CustomLayout/AllLayoutsResult.php | 25 + .../CustomLayoutDefinitionsResult.php | 25 + .../DeleteCustomLayoutHandler.php | 37 + .../ExportCustomLayoutHandler.php | 43 + .../CustomLayout/ExportCustomLayoutResult.php | 26 + .../CustomLayout/GetAllLayoutsHandler.php | 60 + .../GetCustomLayoutDefinitionsHandler.php | 44 + .../CustomLayout/GetCustomLayoutHandler.php | 61 + .../CustomLayout/GetCustomLayoutResult.php | 26 + .../ImportCustomLayoutHandler.php | 45 + .../CustomLayout/SaveCustomLayoutHandler.php | 55 + .../SuggestCustomLayoutIdentifierHandler.php | 40 + .../SuggestCustomLayoutIdentifierResult.php | 27 + .../DataObject/DataObjectGridProxyHandler.php | 49 + .../DataObject/DataObjectGridProxyResult.php | 26 + src/Handler/DataObject/DataObjectResult.php | 27 + .../DataObject/DeleteDataObjectHandler.php | 65 + .../DataObject/DeleteDataObjectResult.php | 25 + .../DeleteFieldCollectionHandler.php | 29 + .../ExportFieldCollectionHandler.php | 42 + .../ExportFieldCollectionResult.php | 26 + .../FieldCollectionListResult.php | 25 + .../FieldCollectionTreeResult.php | 26 + .../GetFieldCollectionHandler.php | 30 + .../GetFieldCollectionListHandler.php | 75 + .../GetFieldCollectionResult.php | 26 + .../GetFieldCollectionTreeHandler.php | 105 + .../GetFieldCollectionUsagesHandler.php | 42 + .../ImportFieldCollectionHandler.php | 33 + .../UpdateFieldCollectionHandler.php | 64 + .../GetDataObjectChildrenHandler.php | 78 + .../GetDataObjectChildrenResult.php | 31 + .../DataObject/GetDataObjectFolderHandler.php | 94 + .../DataObject/GetDataObjectFolderResult.php | 25 + .../DataObject/GetDataObjectHandler.php | 377 +++ .../GetDataObjectPreviewUrlHandler.php | 60 + .../DataObject/GetDataObjectResult.php | 25 + .../DataObject/GetIdPathPagingInfoHandler.php | 57 + .../DataObject/GetIdPathPagingInfoResult.php | 25 + .../DataObject/GetSelectOptionsHandler.php | 63 + .../Helper/ApplyGridConfigToAllHandler.php | 50 + ...eleteDataObjectGridColumnConfigHandler.php | 46 + .../Helper/DeleteGridColumnConfigHandler.php | 50 + .../Helper/DoDataObjectExportHandler.php | 142 + .../DataObject/Helper/ExecuteBatchHandler.php | 36 + .../GetAvailableVisibleFieldsHandler.php | 114 + .../GetAvailableVisibleFieldsResult.php | 25 + .../DataObject/Helper/GetBatchJobsHandler.php | 38 + .../DataObject/Helper/GetBatchJobsResult.php | 25 + .../Helper/GetExportConfigsHandler.php | 49 + .../Helper/GetExportJobsHandler.php | 61 + .../DataObject/Helper/GetExportJobsResult.php | 26 + .../Helper/GetGridColumnConfigHandler.php | 49 + .../DataObject/Helper/ImportUploadHandler.php | 38 + .../Helper/LoadObjectDataHandler.php | 34 + ...rkDataObjectGridConfigFavouriteHandler.php | 93 + ...arkDataObjectGridConfigFavouriteResult.php | 25 + .../PrepareHelperColumnConfigsHandler.php | 45 + .../SaveDataObjectGridColumnConfigHandler.php | 110 + .../SaveDataObjectGridColumnConfigResult.php | 27 + .../ObjectBrick/BrickUsagesResult.php | 25 + .../ObjectBrick/DeleteObjectBrickHandler.php | 29 + .../ObjectBrick/ExportObjectBrickHandler.php | 42 + .../ObjectBrick/ExportObjectBrickResult.php | 26 + .../ObjectBrick/GetBrickUsagesHandler.php | 42 + .../ObjectBrick/GetObjectBrickHandler.php | 30 + .../ObjectBrick/GetObjectBrickListHandler.php | 80 + .../ObjectBrick/GetObjectBrickResult.php | 26 + .../ObjectBrick/GetObjectBrickTreeHandler.php | 151 + .../ObjectBrick/ImportObjectBrickHandler.php | 33 + .../ObjectBrick/ObjectBrickListResult.php | 25 + .../ObjectBrick/ObjectBrickTreeResult.php | 26 + .../ObjectBrick/UpdateObjectBrickHandler.php | 74 + .../ConvertAllQuantityValuesHandler.php | 55 + .../ConvertAllQuantityValuesResult.php | 27 + .../ConvertQuantityValueHandler.php | 44 + .../ConvertQuantityValueResult.php | 25 + .../ExportQuantityValueUnitsHandler.php | 30 + .../GetQuantityValueUnitListHandler.php | 60 + .../GetQuantityValueUnitListResult.php | 26 + .../GetQuantityValueUnitsHandler.php | 81 + .../GetQuantityValueUnitsResult.php | 26 + .../ImportQuantityValueUnitsHandler.php | 30 + .../ManageQuantityValueUnitHandler.php | 72 + .../ManageQuantityValueUnitResult.php | 25 + .../SaveDataObjectFolderHandler.php | 90 + .../DataObject/SaveDataObjectHandler.php | 90 + .../DataObject/SaveDataObjectResult.php | 29 + .../DataObject/TreeGetChildrenByIdHandler.php | 178 ++ .../DataObject/UpdateDataObjectHandler.php | 254 ++ .../DataObject/UpdateDataObjectResult.php | 25 + .../Variants/GetVariantsHandler.php | 49 + .../DataObject/Variants/GetVariantsResult.php | 25 + .../Variants/UpdateObjectKeyHandler.php | 42 + .../Variants/UpdateObjectKeyResult.php | 25 + .../Version/DiffVersionsHandler.php | 67 + .../DataObject/Version/DiffVersionsResult.php | 31 + .../Version/PreviewVersionHandler.php | 53 + .../Version/PreviewVersionResult.php | 29 + .../Version/PublishVersionHandler.php | 61 + .../Version/PublishVersionResult.php | 25 + src/Handler/Document/AddDocumentHandler.php | 157 ++ src/Handler/Document/AddDocumentResult.php | 26 + .../Document/ChangeMainDocumentHandler.php | 35 + .../Document/ConvertDocumentHandler.php | 61 + src/Handler/Document/Copy/ChildIdsResult.php | 26 + .../Document/Copy/CopyDocumentHandler.php | 94 + .../Document/Copy/CopyDocumentResult.php | 28 + .../Copy/GetDocumentChildIdsHandler.php | 40 + .../Copy/RewriteDocumentIdsHandler.php | 44 + .../Document/DeleteDocumentHandler.php | 73 + src/Handler/Document/DeleteDocumentResult.php | 24 + src/Handler/Document/DocumentResult.php | 27 + .../Document/Email/GetEmailDataHandler.php | 68 + .../Document/Email/GetEmailDataResult.php | 30 + .../Document/Email/SaveEmailHandler.php | 69 + .../Document/Email/SaveEmailResult.php | 31 + .../Document/Folder/GetFolderDataHandler.php | 45 + .../Document/Folder/GetFolderDataResult.php | 28 + .../Document/Folder/SaveFolderHandler.php | 48 + .../Document/Folder/SaveFolderResult.php | 28 + .../Document/GetDocTypesByTypeHandler.php | 43 + .../Document/GetDocTypesByTypeResult.php | 25 + .../Document/GetDocTypesListHandler.php | 46 + .../Document/GetDocTypesListResult.php | 26 + .../Document/GetDocumentChildrenHandler.php | 43 + .../Document/GetDocumentChildrenResult.php | 24 + .../Document/GetDocumentDataHandler.php | 62 + .../Document/GetDocumentDataResult.php | 24 + .../Document/GetDocumentIdForPathHandler.php | 33 + .../Document/GetDocumentIdForPathResult.php | 26 + .../Hardlink/GetHardlinkDataHandler.php | 63 + .../Hardlink/GetHardlinkDataResult.php | 31 + .../Document/Hardlink/SaveHardlinkHandler.php | 49 + .../Document/Hardlink/SaveHardlinkResult.php | 29 + .../Document/Link/GetLinkDataHandler.php | 64 + .../Document/Link/GetLinkDataResult.php | 31 + src/Handler/Document/Link/SaveLinkHandler.php | 49 + src/Handler/Document/Link/SaveLinkResult.php | 29 + .../Document/ManageDocTypesHandler.php | 67 + src/Handler/Document/ManageDocTypesResult.php | 25 + .../Document/Page/CheckPrettyUrlHandler.php | 85 + .../Document/Page/CheckPrettyUrlResult.php | 27 + .../Page/GeneratePagePreviewsHandler.php | 44 + .../Document/Page/GenerateQrCodeHandler.php | 48 + .../Document/Page/GetPageDataHandler.php | 84 + .../Document/Page/GetPageDataResult.php | 30 + .../Page/GetPagePreviewImagePathHandler.php | 34 + .../RenderAreabrickIndexEditmodeHandler.php | 63 + .../RenderAreabrickIndexEditmodeResult.php | 29 + .../Page/ResetEditablesSessionHandler.php | 46 + src/Handler/Document/Page/SavePageHandler.php | 82 + src/Handler/Document/Page/SavePageResult.php | 32 + .../Renderlet/RenderRenderletHandler.php | 90 + .../Renderlet/RenderRenderletResult.php | 25 + .../Site/GetSiteCustomSettingsHandler.php | 40 + .../Site/GetSiteCustomSettingsResult.php | 25 + .../Document/Site/RemoveSiteHandler.php | 28 + .../Snippet/GetSnippetDataHandler.php | 74 + .../Document/Snippet/GetSnippetDataResult.php | 30 + .../Document/Snippet/SaveSnippetHandler.php | 69 + .../Document/Snippet/SaveSnippetResult.php | 31 + .../AddDocumentTranslationHandler.php | 53 + .../CheckTranslationLanguageHandler.php | 41 + .../CheckTranslationLanguageResult.php | 26 + .../DetermineTranslationParentHandler.php | 51 + .../DetermineTranslationParentResult.php | 26 + .../Translation/GetLanguageTreeHandler.php | 80 + .../Translation/GetLanguageTreeResult.php | 24 + .../GetLanguageTreeRootHandler.php | 129 + .../Translation/GetLanguageTreeRootResult.php | 26 + .../RemoveDocumentTranslationHandler.php | 39 + .../TreeGetDocumentChildrenHandler.php | 122 + .../TreeGetDocumentChildrenResult.php | 29 + .../Document/UpdateDocumentHandler.php | 182 ++ src/Handler/Document/UpdateDocumentResult.php | 24 + src/Handler/Document/UpdateSiteHandler.php | 74 + src/Handler/Document/UpdateSiteResult.php | 25 + .../Document/Version/DiffVersionsHandler.php | 104 + .../Document/Version/DiffVersionsResult.php | 28 + .../Version/PublishVersionHandler.php | 64 + .../Document/Version/PublishVersionResult.php | 25 + .../Version/SaveVersionToSessionHandler.php | 40 + src/Handler/Element/AddNoteHandler.php | 41 + .../Element/AnalyzePermissionsHandler.php | 43 + .../Element/AnalyzePermissionsResult.php | 25 + .../Element/DeleteAllVersionsHandler.php | 36 + src/Handler/Element/DeleteDraftHandler.php | 32 + src/Handler/Element/DeleteNoteHandler.php | 36 + src/Handler/Element/DeleteVersionHandler.php | 28 + src/Handler/Element/FindUsagesHandler.php | 86 + src/Handler/Element/FindUsagesResult.php | 27 + src/Handler/Element/GetDeleteInfoHandler.php | 185 ++ src/Handler/Element/GetNicePathHandler.php | 147 + src/Handler/Element/GetNicePathResult.php | 25 + src/Handler/Element/GetNoteListHandler.php | 123 + src/Handler/Element/GetNoteListResult.php | 26 + .../GetPredefinedPropertiesHandler.php | 52 + .../Element/GetPredefinedPropertiesResult.php | 25 + .../GetReplaceAssignmentsBatchJobsHandler.php | 42 + .../GetRequiredByDependenciesHandler.php | 74 + .../GetRequiresDependenciesHandler.php | 74 + .../Element/GetRequiresDependenciesResult.php | 25 + src/Handler/Element/GetSubtypeHandler.php | 63 + src/Handler/Element/GetSubtypeResult.php | 27 + src/Handler/Element/GetVersionsHandler.php | 83 + src/Handler/Element/GetVersionsResult.php | 25 + src/Handler/Element/LockElementHandler.php | 28 + .../Element/ReplaceAssignmentsHandler.php | 78 + src/Handler/Element/TypePathHandler.php | 50 + src/Handler/Element/TypePathResult.php | 29 + src/Handler/Element/UnlockElementHandler.php | 28 + src/Handler/Element/UnlockElementsHandler.php | 31 + .../Element/UnlockPropagateHandler.php | 35 + src/Handler/Element/VersionUpdateHandler.php | 34 + .../Email/CreateBlocklistEntryHandler.php | 34 + .../Email/DeleteBlocklistEntryHandler.php | 29 + src/Handler/Email/DeleteEmailLogHandler.php | 34 + src/Handler/Email/GetBlocklistHandler.php | 53 + src/Handler/Email/GetBlocklistResult.php | 26 + src/Handler/Email/GetEmailLogHandler.php | 39 + .../Email/GetEmailLogParamsHandler.php | 119 + src/Handler/Email/GetEmailLogResult.php | 27 + src/Handler/Email/GetEmailLogsHandler.php | 85 + src/Handler/Email/GetEmailLogsResult.php | 26 + src/Handler/Email/ResendEmailHandler.php | 128 + src/Handler/Email/SendTestEmailHandler.php | 77 + .../Email/UpdateBlocklistEntryHandler.php | 32 + .../Login/GenerateTwoFactorSetupHandler.php | 55 + .../Login/GenerateTwoFactorSetupResult.php | 25 + src/Handler/Login/LostPasswordHandler.php | 102 + src/Handler/Login/LostPasswordResult.php | 27 + .../Login/SaveTwoFactorSetupHandler.php | 49 + src/Handler/Misc/GetIconListHandler.php | 83 + src/Handler/Misc/GetIconListResult.php | 29 + .../Misc/GetJsonTranslationsHandler.php | 54 + .../Misc/GetJsonTranslationsResult.php | 25 + .../DeleteAllNotificationsHandler.php | 30 + .../DeleteNotificationHandler.php | 31 + .../FindAllNotificationsHandler.php | 56 + .../FindAllNotificationsResult.php | 26 + .../FindLastUnreadNotificationsHandler.php | 48 + .../FindLastUnreadNotificationsResult.php | 27 + .../Notification/FindNotificationHandler.php | 44 + .../Notification/FindNotificationResult.php | 25 + .../Notification/GetRecipientsHandler.php | 48 + .../Notification/GetRecipientsResult.php | 25 + .../MarkAsReadNotificationHandler.php | 31 + .../Notification/SendNotificationHandler.php | 44 + src/Handler/Portal/AddWidgetHandler.php | 51 + src/Handler/Portal/AddWidgetResult.php | 24 + src/Handler/Portal/CreateDashboardHandler.php | 42 + src/Handler/Portal/DeleteDashboardHandler.php | 32 + .../GetDashboardConfigurationHandler.php | 33 + .../GetDashboardConfigurationResult.php | 24 + .../Portal/GetDashboardListHandler.php | 40 + src/Handler/Portal/GetDashboardListResult.php | 24 + .../GetModificationStatisticsHandler.php | 64 + .../GetModificationStatisticsResult.php | 25 + .../Portal/GetModifiedAssetsHandler.php | 54 + .../Portal/GetModifiedAssetsResult.php | 25 + .../Portal/GetModifiedDocumentsHandler.php | 53 + .../Portal/GetModifiedDocumentsResult.php | 25 + .../Portal/GetModifiedObjectsHandler.php | 53 + .../Portal/GetModifiedObjectsResult.php | 25 + src/Handler/Portal/RemoveWidgetHandler.php | 47 + src/Handler/Portal/ReorderWidgetHandler.php | 52 + .../Portal/UpdatePortletConfigHandler.php | 43 + .../Recyclebin/AddToRecyclebinHandler.php | 48 + .../DeleteRecyclebinItemHandler.php | 31 + .../Recyclebin/ListRecyclebinHandler.php | 109 + .../Recyclebin/ListRecyclebinResult.php | 26 + .../RestoreRecyclebinItemHandler.php | 34 + src/Handler/Settings/AddThumbnailHandler.php | 46 + src/Handler/Settings/AddThumbnailResult.php | 26 + .../Settings/AddVideoThumbnailHandler.php | 46 + .../Settings/AddVideoThumbnailResult.php | 26 + .../Settings/ClearOpenDxpCacheHandler.php | 46 + .../Settings/ClearOutputCacheHandler.php | 35 + .../Settings/ClearSymfonyCacheHandler.php | 47 + .../Settings/ClearTemporaryFilesHandler.php | 44 + .../CreatePredefinedMetadataHandler.php | 52 + .../CreatePredefinedMetadataResult.php | 25 + .../CreatePredefinedPropertyHandler.php | 40 + .../CreatePredefinedPropertyResult.php | 25 + .../Settings/CreateWebsiteSettingHandler.php | 32 + .../Settings/CreateWebsiteSettingResult.php | 25 + .../Settings/DeleteCustomLogoHandler.php | 33 + .../DeletePredefinedMetadataHandler.php | 35 + .../DeletePredefinedPropertyHandler.php | 35 + .../Settings/DeleteThumbnailHandler.php | 35 + .../Settings/DeleteVideoThumbnailHandler.php | 35 + .../Settings/DeleteWebsiteSettingHandler.php | 34 + .../Settings/DisplayCustomLogoHandler.php | 45 + .../Settings/DisplayCustomLogoResult.php | 26 + .../Settings/GetAppearanceSettingsHandler.php | 32 + .../Settings/GetAppearanceSettingsResult.php | 23 + .../GetAvailableAdminLanguagesHandler.php | 40 + .../GetAvailableAdminLanguagesResult.php | 23 + .../GetAvailableAlgorithmsHandler.php | 41 + .../Settings/GetAvailableAlgorithmsResult.php | 23 + .../Settings/GetAvailableCountriesHandler.php | 42 + .../Settings/GetAvailableCountriesResult.php | 23 + .../Settings/GetAvailableSitesHandler.php | 63 + .../Settings/GetAvailableSitesResult.php | 23 + .../GetDownloadableThumbnailsHandler.php | 39 + .../GetDownloadableThumbnailsResult.php | 25 + .../GetFilteredPredefinedMetadataHandler.php | 41 + .../GetFilteredPredefinedMetadataResult.php | 23 + .../GetPredefinedMetadataListHandler.php | 50 + .../GetPredefinedMetadataListResult.php | 26 + .../GetPredefinedPropertiesListHandler.php | 55 + .../GetPredefinedPropertiesListResult.php | 26 + .../Settings/GetSystemSettingsHandler.php | 67 + .../Settings/GetSystemSettingsResult.php | 26 + src/Handler/Settings/GetThumbnailHandler.php | 32 + src/Handler/Settings/GetThumbnailResult.php | 25 + .../Settings/GetThumbnailTreeHandler.php | 70 + .../Settings/GetThumbnailTreeResult.php | 25 + .../Settings/GetVideoThumbnailHandler.php | 32 + .../Settings/GetVideoThumbnailListHandler.php | 40 + .../Settings/GetVideoThumbnailListResult.php | 25 + .../Settings/GetVideoThumbnailResult.php | 25 + .../Settings/GetVideoThumbnailTreeHandler.php | 70 + .../Settings/GetVideoThumbnailTreeResult.php | 25 + .../GetWebsiteSettingsListHandler.php | 93 + .../Settings/GetWebsiteSettingsListResult.php | 26 + .../SaveAppearanceSettingsHandler.php | 50 + .../Settings/SaveSystemSettingsHandler.php | 50 + .../Settings/ThumbnailAdapterCheckHandler.php | 42 + .../Settings/ThumbnailAdapterCheckResult.php | 23 + .../UpdatePredefinedMetadataHandler.php | 55 + .../UpdatePredefinedMetadataResult.php | 25 + .../UpdatePredefinedPropertyHandler.php | 45 + .../UpdatePredefinedPropertyResult.php | 25 + .../Settings/UpdateThumbnailHandler.php | 70 + .../Settings/UpdateVideoThumbnailHandler.php | 65 + .../Settings/UpdateWebsiteSettingHandler.php | 86 + .../Settings/UpdateWebsiteSettingResult.php | 25 + .../Settings/UploadCustomLogoHandler.php | 37 + src/Handler/Tags/AddTagHandler.php | 33 + src/Handler/Tags/AddTagResult.php | 25 + src/Handler/Tags/AddTagToElementHandler.php | 36 + src/Handler/Tags/AddTagToElementResult.php | 25 + src/Handler/Tags/DeleteTagHandler.php | 34 + src/Handler/Tags/DoBatchAssignmentHandler.php | 28 + .../Tags/GetBatchAssignmentJobsHandler.php | 170 ++ .../Tags/GetBatchAssignmentJobsResult.php | 26 + .../Tags/GetTagTreeChildrenHandler.php | 106 + src/Handler/Tags/GetTagTreeChildrenResult.php | 25 + src/Handler/Tags/GetTagsForElementHandler.php | 52 + src/Handler/Tags/GetTagsForElementResult.php | 25 + .../Tags/RemoveTagFromElementHandler.php | 36 + .../Tags/RemoveTagFromElementResult.php | 25 + src/Handler/Tags/UpdateTagHandler.php | 42 + .../AddAdminTranslationKeysHandler.php | 59 + .../BuildContentExportJobsHandler.php | 116 + .../BuildContentExportJobsResult.php | 26 + .../CleanupTranslationsHandler.php | 33 + .../Translation/CreateTranslationHandler.php | 60 + .../Translation/CreateTranslationResult.php | 29 + .../Translation/DeleteTranslationHandler.php | 31 + .../Translation/ExportTranslationsHandler.php | 149 + .../Translation/ExportTranslationsResult.php | 26 + .../GetTranslationDomainsHandler.php | 35 + .../GetTranslationDomainsResult.php | 25 + .../Translation/GetTranslationsHandler.php | 118 + .../Translation/GetTranslationsResult.php | 26 + .../GetWebsiteTranslationLanguagesHandler.php | 34 + .../GetWebsiteTranslationLanguagesResult.php | 25 + .../Translation/ImportTranslationsHandler.php | 73 + .../Translation/ImportTranslationsResult.php | 25 + .../MergeTranslationItemsHandler.php | 34 + .../Translation/TranslationQueryTrait.php | 175 ++ .../Translation/UpdateTranslationHandler.php | 58 + .../Translation/UpdateTranslationResult.php | 29 + .../UploadTranslationImportFileHandler.php | 49 + .../UploadTranslationImportFileResult.php | 26 + src/Handler/User/AddUserHandler.php | 100 + src/Handler/User/AddUserResult.php | 25 + src/Handler/User/DeleteUserHandler.php | 84 + src/Handler/User/DeleteUserImageHandler.php | 50 + src/Handler/User/Disable2FaHandler.php | 42 + src/Handler/User/GetCurrentUserHandler.php | 54 + src/Handler/User/GetCurrentUserResult.php | 25 + src/Handler/User/GetMinimalUserHandler.php | 43 + src/Handler/User/GetMinimalUserResult.php | 28 + src/Handler/User/GetRoleHandler.php | 72 + src/Handler/User/GetRoleResult.php | 31 + .../User/GetRoleTreeChildrenHandler.php | 68 + src/Handler/User/GetRolesHandler.php | 42 + src/Handler/User/GetTokenLoginLinkHandler.php | 57 + src/Handler/User/GetTokenLoginLinkResult.php | 25 + src/Handler/User/GetUserHandler.php | 128 + src/Handler/User/GetUserImageHandler.php | 39 + src/Handler/User/GetUserResult.php | 32 + .../User/GetUserTreeChildrenHandler.php | 77 + src/Handler/User/GetUsersHandler.php | 53 + src/Handler/User/Reset2FaSecretHandler.php | 36 + src/Handler/User/ResetMy2FaSecretHandler.php | 33 + src/Handler/User/SearchUsersHandler.php | 48 + .../User/SendInvitationLinkHandler.php | 91 + src/Handler/User/SendInvitationLinkResult.php | 26 + src/Handler/User/UpdateCurrentUserHandler.php | 99 + src/Handler/User/UpdateUserHandler.php | 138 + src/Handler/User/UploadUserImageHandler.php | 61 + .../Workflow/GetModalCustomHtmlHandler.php | 86 + .../Workflow/GetModalCustomHtmlResult.php | 25 + .../Workflow/GetWorkflowDetailsHandler.php | 115 + .../Workflow/GetWorkflowDetailsResult.php | 25 + .../Workflow/GetWorkflowFormHandler.php | 74 + .../Workflow/GetWorkflowFormResult.php | 28 + .../Workflow/GetWorkflowSvgHandler.php | 69 + .../Workflow/SubmitGlobalActionHandler.php | 53 + .../SubmitWorkflowTransitionHandler.php | 62 + .../SubmitWorkflowTransitionResult.php | 27 + src/Helper/DataObjectVersionHelper.php | 39 + src/Helper/DocumentVersionHelper.php | 49 + src/Http/Result/FileResult.php | 27 + src/Http/Result/StreamResult.php | 26 + .../DataObject/DataObjectLoadContext.php | 27 + .../DataObject/AdminStyleNormalizer.php | 61 + .../DataObject/CustomLayoutNormalizer.php | 63 + src/Normalizer/DataObject/DraftNormalizer.php | 50 + .../DataObject/TreeStyleNormalizer.php | 58 + .../DataObject/UserNamesNormalizer.php | 46 + .../Document/AdminStyleNormalizer.php | 55 + .../Document/DocumentMetaNormalizer.php | 59 + src/Normalizer/Document/DraftNormalizer.php | 52 + .../Document/PropertiesNormalizer.php | 51 + .../Document/TranslationNormalizer.php | 56 + .../Element/AbstractUserNamesNormalizer.php | 46 + .../Element/UserNamesNormalizer.php | 60 + src/Normalizer/ElementResponseNormalizer.php | 35 + .../ElementResponseNormalizerInterface.php | 27 + src/Payload/Asset/AssetPayload.php | 55 + src/Payload/DataObject/DataObjectPayload.php | 54 + src/Payload/Document/EmailPayload.php | 20 + src/Payload/Document/FolderPayload.php | 36 + src/Payload/Document/HardlinkPayload.php | 46 + src/Payload/Document/LinkPayload.php | 46 + src/Payload/Document/PagePayload.php | 56 + .../RenderAreabrickIndexEditmodePayload.php | 44 + .../Document/RenderRenderletPayload.php | 44 + src/Payload/Document/SnippetPayload.php | 20 + src/Security/Permission/AdminPermission.php | 25 + src/Security/Permission/CorePermission.php | 58 + src/Security/Voter/PermissionVoter.php | 46 + src/Service/AdminUserContext.php | 38 + src/Service/AdminUserContextInterface.php | 27 + src/Service/Asset/AssetGridService.php | 193 ++ src/Service/Asset/AssetPayloadMapper.php | 125 + .../Asset/AssetPersistenceCoordinator.php | 48 + src/Service/Asset/AssetUploadService.php | 234 ++ src/Service/CustomLoginUrlGenerator.php | 39 + .../DataObject/DataObjectGridService.php} | 74 +- .../DataObject/DataObjectPayloadMapper.php | 214 ++ .../DataObjectPersistenceCoordinator.php | 113 + .../Document/DocumentPayloadMapper.php | 245 ++ .../DocumentPersistenceCoordinator.php | 75 + src/Service/Document/DocumentSaveResult.php | 31 + src/Service/Element/EditLockService.php | 71 + src/Service/Element/SessionService.php | 100 + .../Grid/AssetGridColumnConfigResolver.php | 176 ++ .../DataObjectGridColumnConfigResolver.php | 459 +++ .../Grid/Dto/GridColumnConfigResult.php | 66 + src/Service/Grid/Dto/GridConfigData.php | 38 + src/Service/Grid/GridBatchService.php | 384 +++ src/Service/Grid/GridColumnConfigService.php | 302 ++ src/Service/Grid/GridExportService.php | 78 + src/Service/Login/LoginPageService.php | 110 + .../Translation/AdminSearchTermResolver.php | 46 + .../Workflow/WorkflowElementResolver.php | 84 + .../Permissions/AbstractPermissionTest.php | 24 +- .../Permissions/ModelAssetPermissionsTest.php | 16 +- .../ModelDataObjectPermissionsTest.php | 18 +- .../ModelDocumentPermissionsTest.php | 13 +- 721 files changed, 40263 insertions(+), 18527 deletions(-) create mode 100644 src/Builder/AdminSettingsAssembler.php create mode 100644 src/Controller/Admin/Asset/AssetCopyController.php create mode 100644 src/Controller/Admin/Asset/AssetDownloadController.php create mode 100644 src/Controller/Admin/Asset/AssetEditorController.php create mode 100644 src/Controller/Admin/Asset/AssetMediaController.php create mode 100644 src/Controller/Admin/Asset/AssetThumbnailController.php create mode 100644 src/Controller/Admin/Asset/AssetUploadController.php create mode 100644 src/Controller/Admin/Asset/AssetVersionController.php create mode 100644 src/Controller/Admin/DataObject/CustomLayoutController.php create mode 100644 src/Controller/Admin/DataObject/DataObjectCopyController.php create mode 100644 src/Controller/Admin/DataObject/DataObjectVersionController.php create mode 100644 src/Controller/Admin/DataObject/FieldCollectionController.php create mode 100644 src/Controller/Admin/DataObject/ObjectBrickController.php create mode 100644 src/Controller/Admin/Document/DocumentCopyController.php create mode 100644 src/Controller/Admin/Document/DocumentVersionController.php create mode 100644 src/Controller/Admin/Settings/ThumbnailController.php create mode 100644 src/Controller/Admin/Settings/VideoThumbnailController.php create mode 100644 src/Controller/Admin/User/RoleController.php create mode 100644 src/Controller/Admin/User/UserProfileController.php delete mode 100644 src/Controller/Traits/ApplySchedulerDataTrait.php delete mode 100644 src/Controller/Traits/UserNameTrait.php create mode 100644 src/Dto/Admin/AdminSettingsDto.php create mode 100644 src/Dto/Admin/StatisticsDto.php create mode 100644 src/Dto/Response/ApiResponse.php create mode 100644 src/Exception/Asset/AssetNotFoundException.php create mode 100644 src/Exception/Asset/AssetVersionNotFoundException.php create mode 100644 src/Exception/DataObject/DataObjectNotFoundException.php create mode 100644 src/Exception/Document/DocumentNotFoundException.php create mode 100644 src/Exception/ElementLockedException.php create mode 100644 src/Exception/NotFoundException.php create mode 100644 src/Factory/DashboardFactory.php create mode 100644 src/Factory/ElementServiceFactory.php create mode 100644 src/Factory/LoginPageFactory.php create mode 100644 src/Handler/Admin/IndexActionHandler.php create mode 100644 src/Handler/Admin/SettingsResult.php create mode 100644 src/Handler/Admin/StatisticsActionHandler.php create mode 100644 src/Handler/Asset/AssetResult.php create mode 100644 src/Handler/Asset/ClearAssetThumbnailHandler.php create mode 100644 src/Handler/Asset/Copy/ChildIdsResult.php create mode 100644 src/Handler/Asset/Copy/CopyAssetHandler.php create mode 100644 src/Handler/Asset/Copy/CopyAssetResult.php create mode 100644 src/Handler/Asset/Copy/GetAssetChildIdsHandler.php create mode 100644 src/Handler/Asset/CreateAssetFolderHandler.php create mode 100644 src/Handler/Asset/DeleteAssetHandler.php create mode 100644 src/Handler/Asset/DeleteAssetResult.php create mode 100644 src/Handler/Asset/Download/AddFilesToZipHandler.php create mode 100644 src/Handler/Asset/Download/DownloadAssetHandler.php create mode 100644 src/Handler/Asset/Download/DownloadImageThumbnailHandler.php create mode 100644 src/Handler/Asset/Download/DownloadImageThumbnailResult.php create mode 100644 src/Handler/Asset/Download/DownloadZipHandler.php create mode 100644 src/Handler/Asset/Download/DownloadZipResult.php create mode 100644 src/Handler/Asset/Download/GetDownloadZipJobsHandler.php create mode 100644 src/Handler/Asset/Download/GetDownloadZipJobsResult.php create mode 100644 src/Handler/Asset/Editor/LoadAssetForEditorHandler.php create mode 100644 src/Handler/Asset/Editor/SaveImageEditorHandler.php create mode 100644 src/Handler/Asset/GetAssetChildrenHandler.php create mode 100644 src/Handler/Asset/GetAssetChildrenResult.php create mode 100644 src/Handler/Asset/GetAssetDataHandler.php create mode 100644 src/Handler/Asset/GetAssetDataResult.php create mode 100644 src/Handler/Asset/Helper/DeleteGridColumnConfigHandler.php create mode 100644 src/Handler/Asset/Helper/DoAssetExportHandler.php create mode 100644 src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php create mode 100644 src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php create mode 100644 src/Handler/Asset/Helper/GetAssetBatchJobsResult.php create mode 100644 src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigHandler.php create mode 100644 src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigResult.php create mode 100644 src/Handler/Asset/Helper/GetExportJobsHandler.php create mode 100644 src/Handler/Asset/Helper/GetExportJobsResult.php create mode 100644 src/Handler/Asset/Helper/MarkGridConfigFavouriteHandler.php create mode 100644 src/Handler/Asset/Helper/MarkGridConfigFavouriteResult.php create mode 100644 src/Handler/Asset/Helper/SaveGridColumnConfigHandler.php create mode 100644 src/Handler/Asset/Helper/SaveGridColumnConfigResult.php create mode 100644 src/Handler/Asset/Media/AssetTextResult.php create mode 100644 src/Handler/Asset/Media/GetAssetTextHandler.php create mode 100644 src/Handler/Asset/Media/GetDocumentPreviewHandler.php create mode 100644 src/Handler/Asset/Media/GetVideoPreviewHandler.php create mode 100644 src/Handler/Asset/Media/PreviewDocumentResult.php create mode 100644 src/Handler/Asset/Media/PreviewVideoResult.php create mode 100644 src/Handler/Asset/Media/ServeVideoPreviewHandler.php create mode 100644 src/Handler/Asset/Media/ServeVideoPreviewResult.php create mode 100644 src/Handler/Asset/SaveAssetHandler.php create mode 100644 src/Handler/Asset/SaveAssetResult.php create mode 100644 src/Handler/Asset/Thumbnail/FolderThumbnailResult.php create mode 100644 src/Handler/Asset/Thumbnail/GetDocumentThumbnailHandler.php create mode 100644 src/Handler/Asset/Thumbnail/GetDocumentThumbnailResult.php create mode 100644 src/Handler/Asset/Thumbnail/GetFolderContentPreviewHandler.php create mode 100644 src/Handler/Asset/Thumbnail/GetFolderContentPreviewResult.php create mode 100644 src/Handler/Asset/Thumbnail/GetFolderThumbnailHandler.php create mode 100644 src/Handler/Asset/Thumbnail/GetImageThumbnailHandler.php create mode 100644 src/Handler/Asset/Thumbnail/GetImageThumbnailResult.php create mode 100644 src/Handler/Asset/Thumbnail/GetVideoThumbnailHandler.php create mode 100644 src/Handler/Asset/Thumbnail/GetVideoThumbnailResult.php create mode 100644 src/Handler/Asset/UpdateAssetHandler.php create mode 100644 src/Handler/Asset/UpdateAssetResult.php create mode 100644 src/Handler/Asset/Upload/CheckAssetExistsHandler.php create mode 100644 src/Handler/Asset/Upload/ImportZipFilesHandler.php create mode 100644 src/Handler/Asset/Upload/ImportZipHandler.php create mode 100644 src/Handler/Asset/Upload/ImportZipResult.php create mode 100644 src/Handler/Asset/Upload/ReplaceAssetHandler.php create mode 100644 src/Handler/Asset/Version/PublishVersionHandler.php create mode 100644 src/Handler/Asset/Version/ShowVersionHandler.php create mode 100644 src/Handler/Asset/Version/ShowVersionResult.php create mode 100644 src/Handler/DataObject/AddObjectFolderHandler.php create mode 100644 src/Handler/DataObject/AddObjectHandler.php create mode 100644 src/Handler/DataObject/AddObjectResult.php create mode 100644 src/Handler/DataObject/ChangeChildrenSortByHandler.php create mode 100644 src/Handler/DataObject/ClassDef/AddClassHandler.php create mode 100644 src/Handler/DataObject/ClassDef/AddClassResult.php create mode 100644 src/Handler/DataObject/ClassDef/BulkCommitHandler.php create mode 100644 src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php create mode 100644 src/Handler/DataObject/ClassDef/BulkImportHandler.php create mode 100644 src/Handler/DataObject/ClassDef/BulkImportResult.php create mode 100644 src/Handler/DataObject/ClassDef/DeleteClassHandler.php create mode 100644 src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php create mode 100644 src/Handler/DataObject/ClassDef/DoBulkExportHandler.php create mode 100644 src/Handler/DataObject/ClassDef/DoBulkExportResult.php create mode 100644 src/Handler/DataObject/ClassDef/ExportClassHandler.php create mode 100644 src/Handler/DataObject/ClassDef/ExportClassResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetAssetTypesHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetAssetTypesResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassBulkExportListHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassBulkExportListResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassIconsHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassIconsResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassTreeHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassTreeResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetDocumentTypesHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetDocumentTypesResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsTreeHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsTreeResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetTextLayoutPreviewHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetTextLayoutPreviewResult.php create mode 100644 src/Handler/DataObject/ClassDef/GetVideoAllowedTypesHandler.php create mode 100644 src/Handler/DataObject/ClassDef/GetVideoAllowedTypesResult.php create mode 100644 src/Handler/DataObject/ClassDef/ImportClassHandler.php create mode 100644 src/Handler/DataObject/ClassDef/SaveClassDefinitionHandler.php create mode 100644 src/Handler/DataObject/ClassDef/SaveClassDefinitionResult.php create mode 100644 src/Handler/DataObject/ClassDef/SaveSelectOptionsHandler.php create mode 100644 src/Handler/DataObject/ClassDef/SaveSelectOptionsResult.php create mode 100644 src/Handler/DataObject/ClassDef/SuggestClassIdentifierHandler.php create mode 100644 src/Handler/DataObject/ClassDef/SuggestClassIdentifierResult.php create mode 100644 src/Handler/DataObject/Classificationstore/AddCollectionsHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/AddCollectionsResult.php create mode 100644 src/Handler/DataObject/Classificationstore/AddGroupsHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/AddGroupsResult.php create mode 100644 src/Handler/DataObject/Classificationstore/AddPropertyHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/AddPropertyResult.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateCollectionHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateCollectionResult.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateGroupHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateGroupResult.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateStoreHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateStoreResult.php create mode 100644 src/Handler/DataObject/Classificationstore/DeleteCollectionHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/EditStoreHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/GetCollectionRelationsResult.php create mode 100644 src/Handler/DataObject/Classificationstore/GetCollectionsHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/GetCollectionsResult.php create mode 100644 src/Handler/DataObject/Classificationstore/GetGroupsHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/GetGroupsResult.php create mode 100644 src/Handler/DataObject/Classificationstore/GetPageHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/GetPageResult.php create mode 100644 src/Handler/DataObject/Classificationstore/GetPropertiesHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/GetPropertiesResult.php create mode 100644 src/Handler/DataObject/Classificationstore/GetRelationsHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/GetRelationsResult.php create mode 100644 src/Handler/DataObject/Classificationstore/GetStoreTreeHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/GetStoreTreeResult.php create mode 100644 src/Handler/DataObject/Classificationstore/ListStoresHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/ListStoresResult.php create mode 100644 src/Handler/DataObject/Classificationstore/SaveCollectionRelationsHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/SaveCollectionRelationsResult.php create mode 100644 src/Handler/DataObject/Classificationstore/SaveRelationHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/SaveRelationResult.php create mode 100644 src/Handler/DataObject/Classificationstore/SearchRelationsHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/SearchRelationsResult.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdateCollectionHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdateCollectionResult.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdateGroupHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdateGroupResult.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdatePropertyHandler.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdatePropertyResult.php create mode 100644 src/Handler/DataObject/Copy/ChildIdsResult.php create mode 100644 src/Handler/DataObject/Copy/CopyDataObjectHandler.php create mode 100644 src/Handler/DataObject/Copy/CopyDataObjectResult.php create mode 100644 src/Handler/DataObject/Copy/GetDataObjectChildIdsHandler.php create mode 100644 src/Handler/DataObject/Copy/RewriteDataObjectIdsHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/AllLayoutsResult.php create mode 100644 src/Handler/DataObject/CustomLayout/CustomLayoutDefinitionsResult.php create mode 100644 src/Handler/DataObject/CustomLayout/DeleteCustomLayoutHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/ExportCustomLayoutResult.php create mode 100644 src/Handler/DataObject/CustomLayout/GetAllLayoutsHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/GetCustomLayoutResult.php create mode 100644 src/Handler/DataObject/CustomLayout/ImportCustomLayoutHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php create mode 100644 src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierResult.php create mode 100644 src/Handler/DataObject/DataObjectGridProxyHandler.php create mode 100644 src/Handler/DataObject/DataObjectGridProxyResult.php create mode 100644 src/Handler/DataObject/DataObjectResult.php create mode 100644 src/Handler/DataObject/DeleteDataObjectHandler.php create mode 100644 src/Handler/DataObject/DeleteDataObjectResult.php create mode 100644 src/Handler/DataObject/FieldCollection/DeleteFieldCollectionHandler.php create mode 100644 src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php create mode 100644 src/Handler/DataObject/FieldCollection/ExportFieldCollectionResult.php create mode 100644 src/Handler/DataObject/FieldCollection/FieldCollectionListResult.php create mode 100644 src/Handler/DataObject/FieldCollection/FieldCollectionTreeResult.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollectionHandler.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollectionResult.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollectionTreeHandler.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php create mode 100644 src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php create mode 100644 src/Handler/DataObject/FieldCollection/UpdateFieldCollectionHandler.php create mode 100644 src/Handler/DataObject/GetDataObjectChildrenHandler.php create mode 100644 src/Handler/DataObject/GetDataObjectChildrenResult.php create mode 100644 src/Handler/DataObject/GetDataObjectFolderHandler.php create mode 100644 src/Handler/DataObject/GetDataObjectFolderResult.php create mode 100644 src/Handler/DataObject/GetDataObjectHandler.php create mode 100644 src/Handler/DataObject/GetDataObjectPreviewUrlHandler.php create mode 100644 src/Handler/DataObject/GetDataObjectResult.php create mode 100644 src/Handler/DataObject/GetIdPathPagingInfoHandler.php create mode 100644 src/Handler/DataObject/GetIdPathPagingInfoResult.php create mode 100644 src/Handler/DataObject/GetSelectOptionsHandler.php create mode 100644 src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php create mode 100644 src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php create mode 100644 src/Handler/DataObject/Helper/DeleteGridColumnConfigHandler.php create mode 100644 src/Handler/DataObject/Helper/DoDataObjectExportHandler.php create mode 100644 src/Handler/DataObject/Helper/ExecuteBatchHandler.php create mode 100644 src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php create mode 100644 src/Handler/DataObject/Helper/GetAvailableVisibleFieldsResult.php create mode 100644 src/Handler/DataObject/Helper/GetBatchJobsHandler.php create mode 100644 src/Handler/DataObject/Helper/GetBatchJobsResult.php create mode 100644 src/Handler/DataObject/Helper/GetExportConfigsHandler.php create mode 100644 src/Handler/DataObject/Helper/GetExportJobsHandler.php create mode 100644 src/Handler/DataObject/Helper/GetExportJobsResult.php create mode 100644 src/Handler/DataObject/Helper/GetGridColumnConfigHandler.php create mode 100644 src/Handler/DataObject/Helper/ImportUploadHandler.php create mode 100644 src/Handler/DataObject/Helper/LoadObjectDataHandler.php create mode 100644 src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteHandler.php create mode 100644 src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteResult.php create mode 100644 src/Handler/DataObject/Helper/PrepareHelperColumnConfigsHandler.php create mode 100644 src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php create mode 100644 src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigResult.php create mode 100644 src/Handler/DataObject/ObjectBrick/BrickUsagesResult.php create mode 100644 src/Handler/DataObject/ObjectBrick/DeleteObjectBrickHandler.php create mode 100644 src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php create mode 100644 src/Handler/DataObject/ObjectBrick/ExportObjectBrickResult.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetBrickUsagesHandler.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetObjectBrickResult.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetObjectBrickTreeHandler.php create mode 100644 src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php create mode 100644 src/Handler/DataObject/ObjectBrick/ObjectBrickListResult.php create mode 100644 src/Handler/DataObject/ObjectBrick/ObjectBrickTreeResult.php create mode 100644 src/Handler/DataObject/ObjectBrick/UpdateObjectBrickHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesResult.php create mode 100644 src/Handler/DataObject/QuantityValue/ConvertQuantityValueHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/ConvertQuantityValueResult.php create mode 100644 src/Handler/DataObject/QuantityValue/ExportQuantityValueUnitsHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListResult.php create mode 100644 src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsResult.php create mode 100644 src/Handler/DataObject/QuantityValue/ImportQuantityValueUnitsHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitResult.php create mode 100644 src/Handler/DataObject/SaveDataObjectFolderHandler.php create mode 100644 src/Handler/DataObject/SaveDataObjectHandler.php create mode 100644 src/Handler/DataObject/SaveDataObjectResult.php create mode 100644 src/Handler/DataObject/TreeGetChildrenByIdHandler.php create mode 100644 src/Handler/DataObject/UpdateDataObjectHandler.php create mode 100644 src/Handler/DataObject/UpdateDataObjectResult.php create mode 100644 src/Handler/DataObject/Variants/GetVariantsHandler.php create mode 100644 src/Handler/DataObject/Variants/GetVariantsResult.php create mode 100644 src/Handler/DataObject/Variants/UpdateObjectKeyHandler.php create mode 100644 src/Handler/DataObject/Variants/UpdateObjectKeyResult.php create mode 100644 src/Handler/DataObject/Version/DiffVersionsHandler.php create mode 100644 src/Handler/DataObject/Version/DiffVersionsResult.php create mode 100644 src/Handler/DataObject/Version/PreviewVersionHandler.php create mode 100644 src/Handler/DataObject/Version/PreviewVersionResult.php create mode 100644 src/Handler/DataObject/Version/PublishVersionHandler.php create mode 100644 src/Handler/DataObject/Version/PublishVersionResult.php create mode 100644 src/Handler/Document/AddDocumentHandler.php create mode 100644 src/Handler/Document/AddDocumentResult.php create mode 100644 src/Handler/Document/ChangeMainDocumentHandler.php create mode 100644 src/Handler/Document/ConvertDocumentHandler.php create mode 100644 src/Handler/Document/Copy/ChildIdsResult.php create mode 100644 src/Handler/Document/Copy/CopyDocumentHandler.php create mode 100644 src/Handler/Document/Copy/CopyDocumentResult.php create mode 100644 src/Handler/Document/Copy/GetDocumentChildIdsHandler.php create mode 100644 src/Handler/Document/Copy/RewriteDocumentIdsHandler.php create mode 100644 src/Handler/Document/DeleteDocumentHandler.php create mode 100644 src/Handler/Document/DeleteDocumentResult.php create mode 100644 src/Handler/Document/DocumentResult.php create mode 100644 src/Handler/Document/Email/GetEmailDataHandler.php create mode 100644 src/Handler/Document/Email/GetEmailDataResult.php create mode 100644 src/Handler/Document/Email/SaveEmailHandler.php create mode 100644 src/Handler/Document/Email/SaveEmailResult.php create mode 100644 src/Handler/Document/Folder/GetFolderDataHandler.php create mode 100644 src/Handler/Document/Folder/GetFolderDataResult.php create mode 100644 src/Handler/Document/Folder/SaveFolderHandler.php create mode 100644 src/Handler/Document/Folder/SaveFolderResult.php create mode 100644 src/Handler/Document/GetDocTypesByTypeHandler.php create mode 100644 src/Handler/Document/GetDocTypesByTypeResult.php create mode 100644 src/Handler/Document/GetDocTypesListHandler.php create mode 100644 src/Handler/Document/GetDocTypesListResult.php create mode 100644 src/Handler/Document/GetDocumentChildrenHandler.php create mode 100644 src/Handler/Document/GetDocumentChildrenResult.php create mode 100644 src/Handler/Document/GetDocumentDataHandler.php create mode 100644 src/Handler/Document/GetDocumentDataResult.php create mode 100644 src/Handler/Document/GetDocumentIdForPathHandler.php create mode 100644 src/Handler/Document/GetDocumentIdForPathResult.php create mode 100644 src/Handler/Document/Hardlink/GetHardlinkDataHandler.php create mode 100644 src/Handler/Document/Hardlink/GetHardlinkDataResult.php create mode 100644 src/Handler/Document/Hardlink/SaveHardlinkHandler.php create mode 100644 src/Handler/Document/Hardlink/SaveHardlinkResult.php create mode 100644 src/Handler/Document/Link/GetLinkDataHandler.php create mode 100644 src/Handler/Document/Link/GetLinkDataResult.php create mode 100644 src/Handler/Document/Link/SaveLinkHandler.php create mode 100644 src/Handler/Document/Link/SaveLinkResult.php create mode 100644 src/Handler/Document/ManageDocTypesHandler.php create mode 100644 src/Handler/Document/ManageDocTypesResult.php create mode 100644 src/Handler/Document/Page/CheckPrettyUrlHandler.php create mode 100644 src/Handler/Document/Page/CheckPrettyUrlResult.php create mode 100644 src/Handler/Document/Page/GeneratePagePreviewsHandler.php create mode 100644 src/Handler/Document/Page/GenerateQrCodeHandler.php create mode 100644 src/Handler/Document/Page/GetPageDataHandler.php create mode 100644 src/Handler/Document/Page/GetPageDataResult.php create mode 100644 src/Handler/Document/Page/GetPagePreviewImagePathHandler.php create mode 100644 src/Handler/Document/Page/RenderAreabrickIndexEditmodeHandler.php create mode 100644 src/Handler/Document/Page/RenderAreabrickIndexEditmodeResult.php create mode 100644 src/Handler/Document/Page/ResetEditablesSessionHandler.php create mode 100644 src/Handler/Document/Page/SavePageHandler.php create mode 100644 src/Handler/Document/Page/SavePageResult.php create mode 100644 src/Handler/Document/Renderlet/RenderRenderletHandler.php create mode 100644 src/Handler/Document/Renderlet/RenderRenderletResult.php create mode 100644 src/Handler/Document/Site/GetSiteCustomSettingsHandler.php create mode 100644 src/Handler/Document/Site/GetSiteCustomSettingsResult.php create mode 100644 src/Handler/Document/Site/RemoveSiteHandler.php create mode 100644 src/Handler/Document/Snippet/GetSnippetDataHandler.php create mode 100644 src/Handler/Document/Snippet/GetSnippetDataResult.php create mode 100644 src/Handler/Document/Snippet/SaveSnippetHandler.php create mode 100644 src/Handler/Document/Snippet/SaveSnippetResult.php create mode 100644 src/Handler/Document/Translation/AddDocumentTranslationHandler.php create mode 100644 src/Handler/Document/Translation/CheckTranslationLanguageHandler.php create mode 100644 src/Handler/Document/Translation/CheckTranslationLanguageResult.php create mode 100644 src/Handler/Document/Translation/DetermineTranslationParentHandler.php create mode 100644 src/Handler/Document/Translation/DetermineTranslationParentResult.php create mode 100644 src/Handler/Document/Translation/GetLanguageTreeHandler.php create mode 100644 src/Handler/Document/Translation/GetLanguageTreeResult.php create mode 100644 src/Handler/Document/Translation/GetLanguageTreeRootHandler.php create mode 100644 src/Handler/Document/Translation/GetLanguageTreeRootResult.php create mode 100644 src/Handler/Document/Translation/RemoveDocumentTranslationHandler.php create mode 100644 src/Handler/Document/TreeGetDocumentChildrenHandler.php create mode 100644 src/Handler/Document/TreeGetDocumentChildrenResult.php create mode 100644 src/Handler/Document/UpdateDocumentHandler.php create mode 100644 src/Handler/Document/UpdateDocumentResult.php create mode 100644 src/Handler/Document/UpdateSiteHandler.php create mode 100644 src/Handler/Document/UpdateSiteResult.php create mode 100644 src/Handler/Document/Version/DiffVersionsHandler.php create mode 100644 src/Handler/Document/Version/DiffVersionsResult.php create mode 100644 src/Handler/Document/Version/PublishVersionHandler.php create mode 100644 src/Handler/Document/Version/PublishVersionResult.php create mode 100644 src/Handler/Document/Version/SaveVersionToSessionHandler.php create mode 100644 src/Handler/Element/AddNoteHandler.php create mode 100644 src/Handler/Element/AnalyzePermissionsHandler.php create mode 100644 src/Handler/Element/AnalyzePermissionsResult.php create mode 100644 src/Handler/Element/DeleteAllVersionsHandler.php create mode 100644 src/Handler/Element/DeleteDraftHandler.php create mode 100644 src/Handler/Element/DeleteNoteHandler.php create mode 100644 src/Handler/Element/DeleteVersionHandler.php create mode 100644 src/Handler/Element/FindUsagesHandler.php create mode 100644 src/Handler/Element/FindUsagesResult.php create mode 100644 src/Handler/Element/GetDeleteInfoHandler.php create mode 100644 src/Handler/Element/GetNicePathHandler.php create mode 100644 src/Handler/Element/GetNicePathResult.php create mode 100644 src/Handler/Element/GetNoteListHandler.php create mode 100644 src/Handler/Element/GetNoteListResult.php create mode 100644 src/Handler/Element/GetPredefinedPropertiesHandler.php create mode 100644 src/Handler/Element/GetPredefinedPropertiesResult.php create mode 100644 src/Handler/Element/GetReplaceAssignmentsBatchJobsHandler.php create mode 100644 src/Handler/Element/GetRequiredByDependenciesHandler.php create mode 100644 src/Handler/Element/GetRequiresDependenciesHandler.php create mode 100644 src/Handler/Element/GetRequiresDependenciesResult.php create mode 100644 src/Handler/Element/GetSubtypeHandler.php create mode 100644 src/Handler/Element/GetSubtypeResult.php create mode 100644 src/Handler/Element/GetVersionsHandler.php create mode 100644 src/Handler/Element/GetVersionsResult.php create mode 100644 src/Handler/Element/LockElementHandler.php create mode 100644 src/Handler/Element/ReplaceAssignmentsHandler.php create mode 100644 src/Handler/Element/TypePathHandler.php create mode 100644 src/Handler/Element/TypePathResult.php create mode 100644 src/Handler/Element/UnlockElementHandler.php create mode 100644 src/Handler/Element/UnlockElementsHandler.php create mode 100644 src/Handler/Element/UnlockPropagateHandler.php create mode 100644 src/Handler/Element/VersionUpdateHandler.php create mode 100644 src/Handler/Email/CreateBlocklistEntryHandler.php create mode 100644 src/Handler/Email/DeleteBlocklistEntryHandler.php create mode 100644 src/Handler/Email/DeleteEmailLogHandler.php create mode 100644 src/Handler/Email/GetBlocklistHandler.php create mode 100644 src/Handler/Email/GetBlocklistResult.php create mode 100644 src/Handler/Email/GetEmailLogHandler.php create mode 100644 src/Handler/Email/GetEmailLogParamsHandler.php create mode 100644 src/Handler/Email/GetEmailLogResult.php create mode 100644 src/Handler/Email/GetEmailLogsHandler.php create mode 100644 src/Handler/Email/GetEmailLogsResult.php create mode 100644 src/Handler/Email/ResendEmailHandler.php create mode 100644 src/Handler/Email/SendTestEmailHandler.php create mode 100644 src/Handler/Email/UpdateBlocklistEntryHandler.php create mode 100644 src/Handler/Login/GenerateTwoFactorSetupHandler.php create mode 100644 src/Handler/Login/GenerateTwoFactorSetupResult.php create mode 100644 src/Handler/Login/LostPasswordHandler.php create mode 100644 src/Handler/Login/LostPasswordResult.php create mode 100644 src/Handler/Login/SaveTwoFactorSetupHandler.php create mode 100644 src/Handler/Misc/GetIconListHandler.php create mode 100644 src/Handler/Misc/GetIconListResult.php create mode 100644 src/Handler/Misc/GetJsonTranslationsHandler.php create mode 100644 src/Handler/Misc/GetJsonTranslationsResult.php create mode 100644 src/Handler/Notification/DeleteAllNotificationsHandler.php create mode 100644 src/Handler/Notification/DeleteNotificationHandler.php create mode 100644 src/Handler/Notification/FindAllNotificationsHandler.php create mode 100644 src/Handler/Notification/FindAllNotificationsResult.php create mode 100644 src/Handler/Notification/FindLastUnreadNotificationsHandler.php create mode 100644 src/Handler/Notification/FindLastUnreadNotificationsResult.php create mode 100644 src/Handler/Notification/FindNotificationHandler.php create mode 100644 src/Handler/Notification/FindNotificationResult.php create mode 100644 src/Handler/Notification/GetRecipientsHandler.php create mode 100644 src/Handler/Notification/GetRecipientsResult.php create mode 100644 src/Handler/Notification/MarkAsReadNotificationHandler.php create mode 100644 src/Handler/Notification/SendNotificationHandler.php create mode 100644 src/Handler/Portal/AddWidgetHandler.php create mode 100644 src/Handler/Portal/AddWidgetResult.php create mode 100644 src/Handler/Portal/CreateDashboardHandler.php create mode 100644 src/Handler/Portal/DeleteDashboardHandler.php create mode 100644 src/Handler/Portal/GetDashboardConfigurationHandler.php create mode 100644 src/Handler/Portal/GetDashboardConfigurationResult.php create mode 100644 src/Handler/Portal/GetDashboardListHandler.php create mode 100644 src/Handler/Portal/GetDashboardListResult.php create mode 100644 src/Handler/Portal/GetModificationStatisticsHandler.php create mode 100644 src/Handler/Portal/GetModificationStatisticsResult.php create mode 100644 src/Handler/Portal/GetModifiedAssetsHandler.php create mode 100644 src/Handler/Portal/GetModifiedAssetsResult.php create mode 100644 src/Handler/Portal/GetModifiedDocumentsHandler.php create mode 100644 src/Handler/Portal/GetModifiedDocumentsResult.php create mode 100644 src/Handler/Portal/GetModifiedObjectsHandler.php create mode 100644 src/Handler/Portal/GetModifiedObjectsResult.php create mode 100644 src/Handler/Portal/RemoveWidgetHandler.php create mode 100644 src/Handler/Portal/ReorderWidgetHandler.php create mode 100644 src/Handler/Portal/UpdatePortletConfigHandler.php create mode 100644 src/Handler/Recyclebin/AddToRecyclebinHandler.php create mode 100644 src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php create mode 100644 src/Handler/Recyclebin/ListRecyclebinHandler.php create mode 100644 src/Handler/Recyclebin/ListRecyclebinResult.php create mode 100644 src/Handler/Recyclebin/RestoreRecyclebinItemHandler.php create mode 100644 src/Handler/Settings/AddThumbnailHandler.php create mode 100644 src/Handler/Settings/AddThumbnailResult.php create mode 100644 src/Handler/Settings/AddVideoThumbnailHandler.php create mode 100644 src/Handler/Settings/AddVideoThumbnailResult.php create mode 100644 src/Handler/Settings/ClearOpenDxpCacheHandler.php create mode 100644 src/Handler/Settings/ClearOutputCacheHandler.php create mode 100644 src/Handler/Settings/ClearSymfonyCacheHandler.php create mode 100644 src/Handler/Settings/ClearTemporaryFilesHandler.php create mode 100644 src/Handler/Settings/CreatePredefinedMetadataHandler.php create mode 100644 src/Handler/Settings/CreatePredefinedMetadataResult.php create mode 100644 src/Handler/Settings/CreatePredefinedPropertyHandler.php create mode 100644 src/Handler/Settings/CreatePredefinedPropertyResult.php create mode 100644 src/Handler/Settings/CreateWebsiteSettingHandler.php create mode 100644 src/Handler/Settings/CreateWebsiteSettingResult.php create mode 100644 src/Handler/Settings/DeleteCustomLogoHandler.php create mode 100644 src/Handler/Settings/DeletePredefinedMetadataHandler.php create mode 100644 src/Handler/Settings/DeletePredefinedPropertyHandler.php create mode 100644 src/Handler/Settings/DeleteThumbnailHandler.php create mode 100644 src/Handler/Settings/DeleteVideoThumbnailHandler.php create mode 100644 src/Handler/Settings/DeleteWebsiteSettingHandler.php create mode 100644 src/Handler/Settings/DisplayCustomLogoHandler.php create mode 100644 src/Handler/Settings/DisplayCustomLogoResult.php create mode 100644 src/Handler/Settings/GetAppearanceSettingsHandler.php create mode 100644 src/Handler/Settings/GetAppearanceSettingsResult.php create mode 100644 src/Handler/Settings/GetAvailableAdminLanguagesHandler.php create mode 100644 src/Handler/Settings/GetAvailableAdminLanguagesResult.php create mode 100644 src/Handler/Settings/GetAvailableAlgorithmsHandler.php create mode 100644 src/Handler/Settings/GetAvailableAlgorithmsResult.php create mode 100644 src/Handler/Settings/GetAvailableCountriesHandler.php create mode 100644 src/Handler/Settings/GetAvailableCountriesResult.php create mode 100644 src/Handler/Settings/GetAvailableSitesHandler.php create mode 100644 src/Handler/Settings/GetAvailableSitesResult.php create mode 100644 src/Handler/Settings/GetDownloadableThumbnailsHandler.php create mode 100644 src/Handler/Settings/GetDownloadableThumbnailsResult.php create mode 100644 src/Handler/Settings/GetFilteredPredefinedMetadataHandler.php create mode 100644 src/Handler/Settings/GetFilteredPredefinedMetadataResult.php create mode 100644 src/Handler/Settings/GetPredefinedMetadataListHandler.php create mode 100644 src/Handler/Settings/GetPredefinedMetadataListResult.php create mode 100644 src/Handler/Settings/GetPredefinedPropertiesListHandler.php create mode 100644 src/Handler/Settings/GetPredefinedPropertiesListResult.php create mode 100644 src/Handler/Settings/GetSystemSettingsHandler.php create mode 100644 src/Handler/Settings/GetSystemSettingsResult.php create mode 100644 src/Handler/Settings/GetThumbnailHandler.php create mode 100644 src/Handler/Settings/GetThumbnailResult.php create mode 100644 src/Handler/Settings/GetThumbnailTreeHandler.php create mode 100644 src/Handler/Settings/GetThumbnailTreeResult.php create mode 100644 src/Handler/Settings/GetVideoThumbnailHandler.php create mode 100644 src/Handler/Settings/GetVideoThumbnailListHandler.php create mode 100644 src/Handler/Settings/GetVideoThumbnailListResult.php create mode 100644 src/Handler/Settings/GetVideoThumbnailResult.php create mode 100644 src/Handler/Settings/GetVideoThumbnailTreeHandler.php create mode 100644 src/Handler/Settings/GetVideoThumbnailTreeResult.php create mode 100644 src/Handler/Settings/GetWebsiteSettingsListHandler.php create mode 100644 src/Handler/Settings/GetWebsiteSettingsListResult.php create mode 100644 src/Handler/Settings/SaveAppearanceSettingsHandler.php create mode 100644 src/Handler/Settings/SaveSystemSettingsHandler.php create mode 100644 src/Handler/Settings/ThumbnailAdapterCheckHandler.php create mode 100644 src/Handler/Settings/ThumbnailAdapterCheckResult.php create mode 100644 src/Handler/Settings/UpdatePredefinedMetadataHandler.php create mode 100644 src/Handler/Settings/UpdatePredefinedMetadataResult.php create mode 100644 src/Handler/Settings/UpdatePredefinedPropertyHandler.php create mode 100644 src/Handler/Settings/UpdatePredefinedPropertyResult.php create mode 100644 src/Handler/Settings/UpdateThumbnailHandler.php create mode 100644 src/Handler/Settings/UpdateVideoThumbnailHandler.php create mode 100644 src/Handler/Settings/UpdateWebsiteSettingHandler.php create mode 100644 src/Handler/Settings/UpdateWebsiteSettingResult.php create mode 100644 src/Handler/Settings/UploadCustomLogoHandler.php create mode 100644 src/Handler/Tags/AddTagHandler.php create mode 100644 src/Handler/Tags/AddTagResult.php create mode 100644 src/Handler/Tags/AddTagToElementHandler.php create mode 100644 src/Handler/Tags/AddTagToElementResult.php create mode 100644 src/Handler/Tags/DeleteTagHandler.php create mode 100644 src/Handler/Tags/DoBatchAssignmentHandler.php create mode 100644 src/Handler/Tags/GetBatchAssignmentJobsHandler.php create mode 100644 src/Handler/Tags/GetBatchAssignmentJobsResult.php create mode 100644 src/Handler/Tags/GetTagTreeChildrenHandler.php create mode 100644 src/Handler/Tags/GetTagTreeChildrenResult.php create mode 100644 src/Handler/Tags/GetTagsForElementHandler.php create mode 100644 src/Handler/Tags/GetTagsForElementResult.php create mode 100644 src/Handler/Tags/RemoveTagFromElementHandler.php create mode 100644 src/Handler/Tags/RemoveTagFromElementResult.php create mode 100644 src/Handler/Tags/UpdateTagHandler.php create mode 100644 src/Handler/Translation/AddAdminTranslationKeysHandler.php create mode 100644 src/Handler/Translation/BuildContentExportJobsHandler.php create mode 100644 src/Handler/Translation/BuildContentExportJobsResult.php create mode 100644 src/Handler/Translation/CleanupTranslationsHandler.php create mode 100644 src/Handler/Translation/CreateTranslationHandler.php create mode 100644 src/Handler/Translation/CreateTranslationResult.php create mode 100644 src/Handler/Translation/DeleteTranslationHandler.php create mode 100644 src/Handler/Translation/ExportTranslationsHandler.php create mode 100644 src/Handler/Translation/ExportTranslationsResult.php create mode 100644 src/Handler/Translation/GetTranslationDomainsHandler.php create mode 100644 src/Handler/Translation/GetTranslationDomainsResult.php create mode 100644 src/Handler/Translation/GetTranslationsHandler.php create mode 100644 src/Handler/Translation/GetTranslationsResult.php create mode 100644 src/Handler/Translation/GetWebsiteTranslationLanguagesHandler.php create mode 100644 src/Handler/Translation/GetWebsiteTranslationLanguagesResult.php create mode 100644 src/Handler/Translation/ImportTranslationsHandler.php create mode 100644 src/Handler/Translation/ImportTranslationsResult.php create mode 100644 src/Handler/Translation/MergeTranslationItemsHandler.php create mode 100644 src/Handler/Translation/TranslationQueryTrait.php create mode 100644 src/Handler/Translation/UpdateTranslationHandler.php create mode 100644 src/Handler/Translation/UpdateTranslationResult.php create mode 100644 src/Handler/Translation/UploadTranslationImportFileHandler.php create mode 100644 src/Handler/Translation/UploadTranslationImportFileResult.php create mode 100644 src/Handler/User/AddUserHandler.php create mode 100644 src/Handler/User/AddUserResult.php create mode 100644 src/Handler/User/DeleteUserHandler.php create mode 100644 src/Handler/User/DeleteUserImageHandler.php create mode 100644 src/Handler/User/Disable2FaHandler.php create mode 100644 src/Handler/User/GetCurrentUserHandler.php create mode 100644 src/Handler/User/GetCurrentUserResult.php create mode 100644 src/Handler/User/GetMinimalUserHandler.php create mode 100644 src/Handler/User/GetMinimalUserResult.php create mode 100644 src/Handler/User/GetRoleHandler.php create mode 100644 src/Handler/User/GetRoleResult.php create mode 100644 src/Handler/User/GetRoleTreeChildrenHandler.php create mode 100644 src/Handler/User/GetRolesHandler.php create mode 100644 src/Handler/User/GetTokenLoginLinkHandler.php create mode 100644 src/Handler/User/GetTokenLoginLinkResult.php create mode 100644 src/Handler/User/GetUserHandler.php create mode 100644 src/Handler/User/GetUserImageHandler.php create mode 100644 src/Handler/User/GetUserResult.php create mode 100644 src/Handler/User/GetUserTreeChildrenHandler.php create mode 100644 src/Handler/User/GetUsersHandler.php create mode 100644 src/Handler/User/Reset2FaSecretHandler.php create mode 100644 src/Handler/User/ResetMy2FaSecretHandler.php create mode 100644 src/Handler/User/SearchUsersHandler.php create mode 100644 src/Handler/User/SendInvitationLinkHandler.php create mode 100644 src/Handler/User/SendInvitationLinkResult.php create mode 100644 src/Handler/User/UpdateCurrentUserHandler.php create mode 100644 src/Handler/User/UpdateUserHandler.php create mode 100644 src/Handler/User/UploadUserImageHandler.php create mode 100644 src/Handler/Workflow/GetModalCustomHtmlHandler.php create mode 100644 src/Handler/Workflow/GetModalCustomHtmlResult.php create mode 100644 src/Handler/Workflow/GetWorkflowDetailsHandler.php create mode 100644 src/Handler/Workflow/GetWorkflowDetailsResult.php create mode 100644 src/Handler/Workflow/GetWorkflowFormHandler.php create mode 100644 src/Handler/Workflow/GetWorkflowFormResult.php create mode 100644 src/Handler/Workflow/GetWorkflowSvgHandler.php create mode 100644 src/Handler/Workflow/SubmitGlobalActionHandler.php create mode 100644 src/Handler/Workflow/SubmitWorkflowTransitionHandler.php create mode 100644 src/Handler/Workflow/SubmitWorkflowTransitionResult.php create mode 100644 src/Helper/DataObjectVersionHelper.php create mode 100644 src/Helper/DocumentVersionHelper.php create mode 100644 src/Http/Result/FileResult.php create mode 100644 src/Http/Result/StreamResult.php create mode 100644 src/Model/DataObject/DataObjectLoadContext.php create mode 100644 src/Normalizer/DataObject/AdminStyleNormalizer.php create mode 100644 src/Normalizer/DataObject/CustomLayoutNormalizer.php create mode 100644 src/Normalizer/DataObject/DraftNormalizer.php create mode 100644 src/Normalizer/DataObject/TreeStyleNormalizer.php create mode 100644 src/Normalizer/DataObject/UserNamesNormalizer.php create mode 100644 src/Normalizer/Document/AdminStyleNormalizer.php create mode 100644 src/Normalizer/Document/DocumentMetaNormalizer.php create mode 100644 src/Normalizer/Document/DraftNormalizer.php create mode 100644 src/Normalizer/Document/PropertiesNormalizer.php create mode 100644 src/Normalizer/Document/TranslationNormalizer.php create mode 100644 src/Normalizer/Element/AbstractUserNamesNormalizer.php create mode 100644 src/Normalizer/Element/UserNamesNormalizer.php create mode 100644 src/Normalizer/ElementResponseNormalizer.php create mode 100644 src/Normalizer/ElementResponseNormalizerInterface.php create mode 100644 src/Payload/Asset/AssetPayload.php create mode 100644 src/Payload/DataObject/DataObjectPayload.php create mode 100644 src/Payload/Document/EmailPayload.php create mode 100644 src/Payload/Document/FolderPayload.php create mode 100644 src/Payload/Document/HardlinkPayload.php create mode 100644 src/Payload/Document/LinkPayload.php create mode 100644 src/Payload/Document/PagePayload.php create mode 100644 src/Payload/Document/RenderAreabrickIndexEditmodePayload.php create mode 100644 src/Payload/Document/RenderRenderletPayload.php create mode 100644 src/Payload/Document/SnippetPayload.php create mode 100644 src/Security/Permission/AdminPermission.php create mode 100644 src/Security/Permission/CorePermission.php create mode 100644 src/Security/Voter/PermissionVoter.php create mode 100644 src/Service/AdminUserContext.php create mode 100644 src/Service/AdminUserContextInterface.php create mode 100644 src/Service/Asset/AssetGridService.php create mode 100644 src/Service/Asset/AssetPayloadMapper.php create mode 100644 src/Service/Asset/AssetPersistenceCoordinator.php create mode 100644 src/Service/Asset/AssetUploadService.php create mode 100644 src/Service/CustomLoginUrlGenerator.php rename src/{Controller/Admin/DataObject/DataObjectActionsTrait.php => Service/DataObject/DataObjectGridService.php} (82%) create mode 100644 src/Service/DataObject/DataObjectPayloadMapper.php create mode 100644 src/Service/DataObject/DataObjectPersistenceCoordinator.php create mode 100644 src/Service/Document/DocumentPayloadMapper.php create mode 100644 src/Service/Document/DocumentPersistenceCoordinator.php create mode 100644 src/Service/Document/DocumentSaveResult.php create mode 100644 src/Service/Element/EditLockService.php create mode 100644 src/Service/Element/SessionService.php create mode 100644 src/Service/Grid/AssetGridColumnConfigResolver.php create mode 100644 src/Service/Grid/DataObjectGridColumnConfigResolver.php create mode 100644 src/Service/Grid/Dto/GridColumnConfigResult.php create mode 100644 src/Service/Grid/Dto/GridConfigData.php create mode 100644 src/Service/Grid/GridBatchService.php create mode 100644 src/Service/Grid/GridColumnConfigService.php create mode 100644 src/Service/Grid/GridExportService.php create mode 100644 src/Service/Login/LoginPageService.php create mode 100644 src/Service/Translation/AdminSearchTermResolver.php create mode 100644 src/Service/Workflow/WorkflowElementResolver.php diff --git a/config/opendxp/routing.yaml b/config/opendxp/routing.yaml index 37684d99..cb2ae9ab 100644 --- a/config/opendxp/routing.yaml +++ b/config/opendxp/routing.yaml @@ -19,4 +19,4 @@ opendxp_admin_page_display_preview_image: # we need to have this outside of /admin scope, to be reachable publicly opendxp_admin_document_document_diff_versions_html: path: /__admin/document/diff-versions-html - defaults: { _controller: OpenDxp\Bundle\AdminBundle\Controller\Admin\Document\DocumentController::diffVersionsHtmlAction } + defaults: { _controller: OpenDxp\Bundle\AdminBundle\Controller\Admin\Document\DocumentVersionController::diffVersionsHtmlAction } diff --git a/config/services.yaml b/config/services.yaml index d852378a..ed5071df 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -3,6 +3,10 @@ services: autowire: true autoconfigure: true + _instanceof: + OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface: + tags: ['opendxp.admin.normalizer.element_response'] + OpenDxp\Bundle\AdminBundle\Installer: public: true arguments: @@ -18,11 +22,12 @@ services: public: true tags: ['controller.service_arguments'] - OpenDxp\Bundle\AdminBundle\Controller\Admin\IndexController: - public: true + OpenDxp\Maintenance\ExecutorInterface: + alias: OpenDxp\Maintenance\Executor + + OpenDxp\Bundle\AdminBundle\Handler\Admin\StatisticsActionHandler: arguments: $httpClient: '@opendxp.http_client' - tags: [ 'controller.service_arguments' ] # # COMMANDS @@ -102,3 +107,77 @@ services: # OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface: class: OpenDxp\Bundle\AdminBundle\Service\ElementService + + OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface: + class: OpenDxp\Bundle\AdminBundle\Service\AdminUserContext + + OpenDxp\Bundle\AdminBundle\Service\Element\SessionService: ~ + + OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService: ~ + + OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper: ~ + + OpenDxp\Bundle\AdminBundle\Normalizer\: + resource: '../src/Normalizer' + + OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer: + arguments: + $normalizers: !tagged_iterator opendxp.admin.normalizer.element_response + + OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator: ~ + + OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPayloadMapper: ~ + + OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPersistenceCoordinator: ~ + + OpenDxp\Bundle\AdminBundle\Service\Asset\AssetPayloadMapper: ~ + + OpenDxp\Bundle\AdminBundle\Service\Asset\AssetPersistenceCoordinator: ~ + + OpenDxp\Bundle\AdminBundle\Service\Asset\AssetUploadService: ~ + + OpenDxp\Bundle\AdminBundle\Service\Asset\AssetGridService: ~ + + OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService: ~ + + OpenDxp\Bundle\AdminBundle\Service\Grid\GridColumnConfigService: ~ + + OpenDxp\Bundle\AdminBundle\Service\Grid\AssetGridColumnConfigResolver: ~ + + OpenDxp\Bundle\AdminBundle\Service\Grid\DataObjectGridColumnConfigResolver: ~ + + OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService: ~ + + OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService: ~ + + # + # Factories + # + OpenDxp\Bundle\AdminBundle\Factory\: + resource: '../src/Factory' + + # + # Builder + # + OpenDxp\Bundle\AdminBundle\Builder\: + resource: '../src/Builder' + bind: + $customAdminRouteName: '%opendxp_admin.custom_admin_route_name%' + $secret: '%secret%' + + # + # Handlers + # + OpenDxp\Bundle\AdminBundle\Handler\: + resource: '../src/Handler' + + OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocumentHandler: + arguments: + $documentClassResolver: '@opendxp.class.resolver.document' + $defaultDocumentController: '%opendxp.documents.default_controller%' + + # + # Security Voters + # + OpenDxp\Bundle\AdminBundle\Security\Voter\: + resource: '../src/Security/Voter' diff --git a/public/js/opendxp/object/helpers/customLayoutEditor.js b/public/js/opendxp/object/helpers/customLayoutEditor.js index a483815c..2087bd38 100644 --- a/public/js/opendxp/object/helpers/customLayoutEditor.js +++ b/public/js/opendxp/object/helpers/customLayoutEditor.js @@ -828,7 +828,7 @@ opendxp.object.helpers.customLayoutEditor = Class.create({ this.layoutComboStore.reload(); this.data = res.data; } else { - Ext.Msg.alert(t('error'), t(res.msg)); + Ext.Msg.alert(t('error'), t(res.message)); } } catch (e) { this.saveOnError(); diff --git a/src/Builder/AdminSettingsAssembler.php b/src/Builder/AdminSettingsAssembler.php new file mode 100644 index 00000000..668465e8 --- /dev/null +++ b/src/Builder/AdminSettingsAssembler.php @@ -0,0 +1,239 @@ +config; + $systemSettings = SystemSettingsConfig::get(); + $adminSettings = AdminConfig::get(); + + $runtimePerspective = PerspectiveConfig::getRuntimePerspective($user); + $dashboard = $this->dashboardFactory->create(); + + try { + $adminEntrypointUrl = $this->urlGenerator->generate( + $this->customAdminRouteName, + [], + UrlGeneratorInterface::ABSOLUTE_URL + ); + } catch (\Exception) { + $adminEntrypointUrl = null; + } + + $requiredLanguages = $systemSettings['general']['valid_languages']; + if (array_key_exists('required_languages', $systemSettings['general'])) { + $requiredLanguages = $systemSettings['general']['required_languages']; + } + + $maxUpload = OpenDxp\Helper\FileSystemHelper::filesizeToBytes(ini_get('upload_max_filesize') . 'B'); + $maxPost = OpenDxp\Helper\FileSystemHelper::filesizeToBytes(ini_get('post_max_size') . 'B'); + $uploadBytes = min($maxUpload, $maxPost) ?: $maxUpload; + + $sessionGcMaxlifetime = (int) ini_get('session.gc_maxlifetime') ?: 120; + + $maintenanceActive = false; + if (($lastExecution = $this->maintenanceExecutor->getLastExecution()) && time() - $lastExecution < 3660) { + $maintenanceActive = true; + } + + $mailIncomplete = false; + if (isset($config['email']) && $systemSettings['email']) { + if (OpenDxp::inDebugMode() && empty($systemSettings['email']['debug']['email_addresses'])) { + $mailIncomplete = true; + } + if (empty($config['email']['sender']['email'])) { + $mailIncomplete = true; + } + } + + $notificationsEnabled = (bool) $config['notifications']['enabled']; + + return new AdminSettingsDto( + instanceId: $this->buildInstanceId(), + version: Version::getVersion(), + build: Version::getRevision(), + debug: OpenDxp::inDebugMode(), + devMode: OpenDxp::inDevMode(), + disableMinifyJs: OpenDxp::disableMinifyJs(), + environment: $this->kernel->getEnvironment(), + sessionId: htmlentities($request->getSession()->getId(), ENT_QUOTES, 'UTF-8'), + + language: $request->getLocale(), + websiteLanguages: Admin::reorderWebsiteLanguages($user, $systemSettings['general']['valid_languages'], true), + requiredLanguages: $requiredLanguages, + + chromiumAvailable: HtmlToImage::isSupported(), + videoConverterAvailable: Video::isAvailable(), + + debugAdminTranslations: (bool) $systemSettings['general']['debug_admin_translations'], + generateDocumentPreviews: (bool) $config['documents']['generate_preview'], + disableAssetTreePreview: (bool) $adminSettings['assets']['disable_tree_preview'], + hideEditImage: (bool) $adminSettings['assets']['hide_edit_image'], + dependencyEnabled: $config['dependency']['enabled'], + + mainDomain: $systemSettings['general']['domain'], + customAdminEntrypointUrl: $adminEntrypointUrl, + timezone: $config['general']['timezone'] ?: date_default_timezone_get(), + tileLayerUrlTemplate: $config['maps']['tile_layer_url_template'], + geocodingUrlTemplate: $config['maps']['geocoding_url_template'], + reverseGeocodingUrlTemplate: $config['maps']['reverse_geocoding_url_template'], + hostname: htmlentities(Tool::getHostname(), ENT_QUOTES, 'UTF-8'), + assetDefaultUploadPath: $config['assets']['default_upload_path'], + + assetTreePagingLimit: $config['assets']['tree_paging_limit'], + documentTreePagingLimit: $config['documents']['tree_paging_limit'], + objectTreePagingLimit: $config['objects']['tree_paging_limit'], + + documentAutoSaveInterval: $config['documents']['auto_save_interval'], + objectAutoSaveInterval: $config['objects']['auto_save_interval'], + + perspective: $runtimePerspective, + availablePerspectives: PerspectiveConfig::getAvailablePerspectives($user), + disabledPortlets: $dashboard->getDisabledPortlets(), + + imageThumbnailsWriteable: (new Asset\Image\Thumbnail\Config())->isWriteable(), + videoThumbnailsWriteable: (new Asset\Video\Thumbnail\Config())->isWriteable(), + documentTypesWriteable: (new DocType())->isWriteable(), + predefinedPropertiesWriteable: (new Predefined())->isWriteable(), + predefinedAssetMetadataWriteable: (new \OpenDxp\Model\Metadata\Predefined())->isWriteable(), + perspectivesWriteable: PerspectiveConfig::isWriteable(), + customViewsWriteable: \OpenDxp\Bundle\AdminBundle\CustomView\Config::isWriteable(), + classDefinitionWriteable: !isset($_SERVER['OPENDXP_CLASS_DEFINITION_WRITABLE']) || (bool) $_SERVER['OPENDXP_CLASS_DEFINITION_WRITABLE'], + objectCustomLayoutWriteable: (new CustomLayout())->isWriteable(), + selectOptionsWriteable: (new \OpenDxp\Model\DataObject\SelectOptions\Config())->isWriteable(), + + assetSearchTypes: Asset::getTypes(), + documentTypesConfiguration: Document::getTypesConfiguration(), + documentSearchTypes: Document::getTypes(), + documentValidTypes: array_values(array_filter(Document::getTypes(), fn ($t) => $t !== 'folder')), + documentEmailSearchTypes: $config['documents']['email_search'], + selectOptionsProviderClass: SelectOptionsOptionsProvider::class, + + uploadMaxFilesize: (int) $uploadBytes, + sessionGcMaxlifetime: $sessionGcMaxlifetime, + + maintenanceActive: $maintenanceActive, + maintenanceMode: $this->maintenanceModeHelper->isActive(), + + mailConfigured: !$mailIncomplete, + mailDefaultAddress: $config['email']['sender']['email'] ?? null, + + customViews: $this->buildCustomViews(), + + notificationsEnabled: $notificationsEnabled, + checkNewNotificationEnabled: $notificationsEnabled && (bool) $config['notifications']['check_new_notification']['enabled'], + checkNewNotificationInterval: $config['notifications']['check_new_notification']['interval'] * 1000, + + csrfToken: $this->csrfProtection->getCsrfToken($request->getSession()), + ); + } + + public function createStatistics(): StatisticsDto + { + try { + $dbVersion = $this->db->fetchOne('SELECT VERSION()'); + } catch (\Throwable) { + $dbVersion = null; + } + + return new StatisticsDto( + instanceId: $this->buildInstanceId(), + revision: Version::getRevision(), + version: Version::getVersion(), + majorVersion: Version::getMajorVersion(), + phpVersion: PHP_VERSION, + dbVersion: is_string($dbVersion) ? $dbVersion : null, + bundles: array_keys($this->kernel->getBundles()), + ); + } + + private function buildInstanceId(): string + { + try { + return sha1(substr($this->secret, 3, -3)); + } catch (\Exception) { + return 'not-set'; + } + } + + private function buildCustomViews(): array + { + $cvData = []; + foreach (\OpenDxp\Bundle\AdminBundle\CustomView\Config::get() as $node) { + $tmpData = $node; + $treeType = $tmpData['treetype'] ?: 'object'; + $rootNode = Service::getElementByPath($treeType, $tmpData['rootfolder']); + + if ($rootNode) { + $tmpData['rootId'] = $rootNode->getId(); + $tmpData['allowedClasses'] = $tmpData['classes'] ?? null; + $tmpData['showroot'] = (bool) $tmpData['showroot']; + + if ($rootNode->isAllowed('list')) { + $cvData[] = $tmpData; + } + } + } + + return $cvData; + } +} diff --git a/src/Controller/Admin/Asset/AssetController.php b/src/Controller/Admin/Asset/AssetController.php index 2e93cf14..21975514 100644 --- a/src/Controller/Admin/Asset/AssetController.php +++ b/src/Controller/Admin/Asset/AssetController.php @@ -1,5 +1,7 @@ value)] +class AssetController extends ElementControllerBase { - use AdminStyleTrait; use ElementEditLockHelperTrait; - use ApplySchedulerDataTrait; - use UserNameTrait; - final const string PDF_MIMETYPE = 'application/pdf'; + public function __construct( + ElementServiceInterface $elementService, + private readonly AssetGridService $assetGridService, + ) { + parent::__construct($elementService); + } + + #[Route('/grid-proxy', name: 'opendxp_admin_asset_gridproxy', methods: ['GET', 'POST', 'PUT'])] + public function gridProxyAction( + Request $request, + EventDispatcherInterface $eventDispatcher, + CsrfProtectionHandler $csrfProtection, + #[MapQueryParameter] ?string $language = null, + ): JsonResponse { + $allParams = [...$request->request->all(), ...$request->query->all()]; + $effectiveLanguage = $language !== 'default' ? $language : null; + + $filterPrepareEvent = new GenericEvent(null, ['requestParams' => $allParams]); + $eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE); + $allParams = $filterPrepareEvent->getArgument('requestParams'); + + if (isset($allParams['data']) && $allParams['data']) { + $csrfProtection->checkCsrfToken($request); + } - protected Asset\Service $_assetService; + return $this->adminJson( + $this->assetGridService->gridProxy($allParams, $effectiveLanguage) + ); + } #[Override] #[Route('/tree-get-root', name: 'opendxp_admin_asset_treegetroot', methods: ['GET'])] - public function treeGetRootAction(Request $request): JsonResponse - { - return parent::treeGetRootAction($request); + public function treeGetRootAction( + #[MapQueryParameter] ?string $elementType = null, + #[MapQueryParameter(flags: FILTER_NULL_ON_FAILURE)] ?int $id = null, + ): JsonResponse { + return parent::treeGetRootAction($elementType, $id); } #[Override] #[Route('/delete-info', name: 'opendxp_admin_asset_deleteinfo', methods: ['GET'])] - public function deleteInfoAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - return parent::deleteInfoAction($request, $eventDispatcher); + public function deleteInfoAction( + GetDeleteInfoHandler $handler, + Request $request, + #[MapQueryParameter] ?string $id = null, + #[MapQueryParameter] ?string $type = null, + ): JsonResponse { + return parent::deleteInfoAction($handler, $request, $id, $type); } #[Route('/get-data-by-id', name: 'opendxp_admin_asset_getdatabyid', methods: ['GET'])] - public function getDataByIdAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $assetId = $request->query->getInt('id'); - $type = $request->query->get('type'); - - $asset = Asset::getById($assetId); - if (!$asset instanceof Asset) { - return $this->adminJson(['success' => false, 'message' => "asset doesn't exist"]); - } - - // check for lock on non-folder items only. - if ($type !== 'folder' && ($asset->isAllowed('publish') || $asset->isAllowed('delete'))) { - if (Element\Editlock::isLocked($assetId, 'asset', $request->getSession()->getId())) { - return $this->getEditLockResponse($assetId, 'asset'); - } - - Element\Editlock::lock($assetId, 'asset', $request->getSession()->getId()); - } - - $asset = clone $asset; - $asset->setParent(null); - - $asset->setStream(null); - $data = $asset->getObjectVars(); - $data['locked'] = $asset->isLocked(); - - if ($asset instanceof Asset\Text) { - if ($asset->getFileSize() < 2000000) { - // it doesn't make sense to show a preview for files bigger than 2MB - $data['data'] = \ForceUTF8\Encoding::toUTF8($asset->getData()); - } else { - $data['data'] = false; - } - } elseif ($asset instanceof Asset\Document) { - $data['pdfPreviewAvailable'] = (bool)$this->getDocumentPreviewPdf($asset); - } elseif ($asset instanceof Asset\Video) { - $videoInfo = []; - - if (\OpenDxp\Video::isAvailable()) { - $config = Asset\Video\Thumbnail\Config::getPreviewConfig(); - $thumbnail = $asset->getThumbnail($config, ['mp4']); - if ($thumbnail && $thumbnail['status'] === 'finished') { - $videoInfo['previewUrl'] = $thumbnail['formats']['mp4']; - $videoInfo['width'] = $asset->getWidth(); - $videoInfo['height'] = $asset->getHeight(); - $metaData = $asset->getSphericalMetaData(); - if (isset($metaData['ProjectionType']) && strtolower($metaData['ProjectionType']) === 'equirectangular') { - $videoInfo['isVrVideo'] = true; - } - } - } - - $data['videoInfo'] = $videoInfo; - } elseif ($asset instanceof Asset\Image) { - $imageInfo = []; - - $previewUrl = $this->generateUrl('opendxp_admin_asset_getimagethumbnail', [ - 'id' => $asset->getId(), - 'treepreview' => true, - '_dc' => time(), - ]); - - if ($asset->isAnimated()) { - $previewUrl = $this->generateUrl('opendxp_admin_asset_getasset', [ - 'id' => $asset->getId(), - '_dc' => time(), - ]); - } - - $imageInfo['previewUrl'] = $previewUrl; - - if ($asset->getWidth() && $asset->getHeight()) { - $imageInfo['dimensions'] = []; - $imageInfo['dimensions']['width'] = $asset->getWidth(); - $imageInfo['dimensions']['height'] = $asset->getHeight(); - } - - $imageInfo['exiftoolAvailable'] = (bool)\OpenDxp\Tool\Console::getExecutable('exiftool'); - - if (!$asset->getEmbeddedMetaData(false)) { - $asset->getEmbeddedMetaData(true, false); // read Exif, IPTC and XPM like in the old days ... - } - - $data['imageInfo'] = $imageInfo; - } - - $predefinedMetaData = Metadata\Predefined\Listing::getByTargetType('asset', [$asset->getType()]); - $predefinedMetaDataGroups = []; - /** @var Metadata\Predefined $item */ - foreach ($predefinedMetaData as $item) { - if ($item->getGroup()) { - $predefinedMetaDataGroups[$item->getGroup()] = true; - } - } - $data['predefinedMetaDataGroups'] = array_keys($predefinedMetaDataGroups); - $data['properties'] = Element\Service::minimizePropertiesForEditmode($asset->getProperties()); - $data['metadata'] = Asset\Service::expandMetadataForEditmode($asset->getMetadata()); - $data['versionDate'] = $asset->getModificationDate(); - $data['filesizeFormatted'] = $asset->getFileSize(true); - $data['filesize'] = $asset->getFileSize(); - $data['fileExtension'] = pathinfo($asset->getFilename(), PATHINFO_EXTENSION); - $data['idPath'] = Element\Service::getIdPath($asset); - $data['userPermissions'] = $asset->getUserPermissions($this->getAdminUser()); - $frontendPath = $asset->getFrontendFullPath(); - $data['url'] = preg_match('/^http(s)?:\\/\\/.+/', $frontendPath) ? - $frontendPath : - $request->getSchemeAndHttpHost() . $frontendPath; - - $data['scheduledTasks'] = array_map( - static fn (Task $task) => $task->getObjectVars(), - $asset->getScheduledTasks() - ); - - $userOwnerName = $this->getUserName($asset->getUserOwner()); - $userModificationName = ($asset->getUserOwner() === $asset->getUserModification()) ? $userOwnerName : $this->getUserName($asset->getUserModification()); - $data['userOwnerUsername'] = $userOwnerName['userName']; - $data['userOwnerFullname'] = $userOwnerName['fullName']; - $data['userModificationUsername'] = $userModificationName['userName']; - $data['userModificationFullname'] = $userModificationName['fullName']; - - $this->addAdminStyle($asset, ElementAdminStyleEvent::CONTEXT_EDITOR, $data); - - $data['php'] = [ - 'classes' => [$asset::class, ...array_values(class_parents($asset))], - 'interfaces' => array_values(class_implements($asset)), - ]; - - $event = new GenericEvent($this, [ - 'data' => $data, - 'asset' => $asset, - ]); - $eventDispatcher->dispatch($event, AdminEvents::ASSET_GET_PRE_SEND_DATA); - $data = $event->getArgument('data'); - - if ($asset->isAllowed('view')) { - return $this->adminJson($data); + public function getDataByIdAction( + GetAssetDataHandler $getAssetData, + Request $request, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $type = null, + ): JsonResponse { + try { + $result = $getAssetData($id, $request->getSchemeAndHttpHost()); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - throw $this->createAccessDeniedHttpException(); + return $this->adminJson($result->data); } #[Route('/tree-get-children-by-id', name: 'opendxp_admin_asset_treegetchildrenbyid', methods: ['GET'])] - public function treeGetChildrenByIdAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { + public function treeGetChildrenByIdAction( + GetAssetChildrenHandler $getChildren, + Request $request, + #[MapQueryParameter] ?string $filter = null, + #[MapQueryParameter] int $inSearch = 0, + ): JsonResponse { $allParams = $request->query->all(); + $nodeId = (int) $allParams['node']; - $assets = []; - $cv = []; - $asset = Asset::getById((int) $allParams['node']); - - $filter = $request->query->get('filter'); - $limit = (int)$allParams['limit']; - if (!is_null($filter)) { - if (!str_ends_with($filter, '*')) { - $filter .= '*'; - } - $filter = str_replace('*', '%', $filter); - + $limit = (int) ($allParams['limit'] ?? 0); + if ($filter !== null) { $limit = 100; - $offset = 0; - } elseif (!$allParams['limit']) { + } elseif (!$limit) { $limit = 100000000; } - $offset = isset($allParams['start']) ? (int)$allParams['start'] : 0; - - $filteredTotalCount = 0; - - if ($asset->hasChildren()) { - if ($allParams['view']) { - $cv = $this->elementService->getCustomViewById($allParams['view']); - } - - // get assets - $childrenList = new Asset\Listing(); - $childrenList->addConditionParam('parentId = ?', [$asset->getId()]); - $childrenList->filterAccessibleByUser($this->getAdminUser(), $asset); - - if (!is_null($filter)) { - $childrenList->addConditionParam('CAST(assets.filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ?', [$filter]); - } - - $childrenList->setLimit($limit); - $childrenList->setOffset($offset); - $childrenList->setOrderKey("FIELD(assets.type, 'folder') DESC, CAST(assets.filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci ASC", false); - - \OpenDxp\Model\Element\Service::addTreeFilterJoins($cv, $childrenList); - - $beforeListLoadEvent = new GenericEvent($this, [ - 'list' => $childrenList, - 'context' => $allParams, - ]); - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD); - /** @var Asset\Listing $childrenList */ - $childrenList = $beforeListLoadEvent->getArgument('list'); - - $children = $childrenList->load(); - - $filteredTotalCount = $childrenList->getTotalCount(); - - foreach ($children as $childAsset) { - $assetTreeNode = $this->getTreeNodeConfig($childAsset); - if ($assetTreeNode['permissions']['list'] == 1) { - $assets[] = $assetTreeNode; - } - } - } - - //Hook for modifying return value - e.g. for changing permissions based on asset data - $event = new GenericEvent($this, [ - 'assets' => $assets, - ]); - $eventDispatcher->dispatch($event, AdminEvents::ASSET_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA); - $assets = $event->getArgument('assets'); - - if ($allParams['limit']) { - return $this->adminJson([ - 'offset' => $offset, - 'limit' => $limit, - 'total' => $asset->getChildAmount($this->getAdminUser()), - 'overflow' => !is_null($filter) && ($filteredTotalCount > $limit), - 'nodes' => $assets, - 'filter' => $request->query->get('filter') ?: '', - 'inSearch' => (int)$request->query->get('inSearch'), - ]); - } - - return $this->adminJson($assets); - } - - #[Route('/add-asset', name: 'opendxp_admin_asset_addasset', methods: ['POST'])] - public function addAssetAction(Request $request, Config $config): JsonResponse - { - try { - $res = $this->addAsset($request, $config); - - $response = [ - 'success' => $res['success'], - ]; - - if ($res['success']) { - $response['asset'] = [ - 'id' => $res['asset']->getId(), - 'path' => $res['asset']->getFullPath(), - 'type' => $res['asset']->getType(), - ]; - } - - return $this->adminJson($response); - } catch (Exception $e) { - return $this->adminJson([ - 'success' => false, - 'message' => $e->getMessage(), - ]); - } - } - - #[Route('/add-asset-compatibility', name: 'opendxp_admin_asset_addassetcompatibility', methods: ['POST'])] - public function addAssetCompatibilityAction(Request $request, Config $config): JsonResponse - { - try { - // this is a special action for the compatibility mode upload (without flash) - $res = $this->addAsset($request, $config); - - $response = $this->adminJson([ - 'success' => $res['success'], - 'msg' => $res['success'] ? 'Success' : 'Error', - 'id' => $res['asset'] ? $res['asset']->getId() : null, - 'fullpath' => $res['asset'] ? $res['asset']->getRealFullPath() : null, - 'type' => $res['asset'] ? $res['asset']->getType() : null, - ]); - $response->headers->set('Content-Type', 'text/html'); - - return $response; - } catch (Exception $e) { - return $this->adminJson([ - 'success' => false, - 'message' => $e->getMessage(), - ]); - } - } - - /** - * @throws Exception - */ - #[Route('/exists', name: 'opendxp_admin_asset_exists', methods: ['GET'])] - public function existsAction(Request $request): JsonResponse - { - $parentAsset = \OpenDxp\Model\Asset::getById((int)$request->query->get('parentId')); - - $dir = $request->query->get('dir', ''); - if ($dir) { - // this is for uploading folders with Drag&Drop - // param "dir" contains the relative path of the file - if (str_contains($dir, '..')) { - throw new Exception('not allowed'); - } - $dir = '/' . trim($dir, '/ '); - } - - $assetPath = $parentAsset->getRealFullPath() . $dir . '/' . $request->query->get('filename'); - - return new JsonResponse([ - 'exists' => Asset\Service::pathExists($assetPath), - ]); - } - - /** - * @return array{success: bool, asset: ?Asset} - * - * @throws Exception - */ - protected function addAsset(Request $request, Config $config): array - { - $defaultUploadPath = $config['assets']['default_upload_path'] ?? '/'; - - if ($request->files->has('Filedata')) { - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $filename = $file->getClientOriginalName(); - $sourcePath = $file->getPathname(); - } elseif ($request->request->get('type') === 'base64') { - $filename = $request->request->get('filename'); - $sourcePath = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/upload-base64' . uniqid('', false) . '.tmp'; - $data = preg_replace('@^data:[^,]+;base64,@', '', $request->request->get('data')); - $filesystem = new Filesystem(); - $filesystem->dumpFile($sourcePath, base64_decode($data)); - } else { - throw new Exception('The filename of the asset is empty'); - } - - $parentId = $request->query->getInt('parentId'); - $parentPath = $request->query->get('parentPath'); - - if ($request->query->has('dir') && $request->query->has('parentId')) { - // this is for uploading folders with Drag&Drop - // param "dir" contains the relative path of the file - $parent = Asset::getById((int) $request->query->get('parentId')); - $dir = $request->query->get('dir'); - if (str_contains($dir, '..')) { - throw new Exception('not allowed'); - } - - $newPath = $parent->getRealFullPath() . '/' . trim($dir, '/ '); - - $maxRetries = 5; - $newParent = null; - for ($retries = 0; $retries < $maxRetries; $retries++) { - try { - $newParent = Asset\Service::createFolderByPath($newPath); - - break; - } catch (Exception $e) { - if ($retries < ($maxRetries - 1)) { - $waitTime = random_int(100000, 900000); // microseconds - usleep($waitTime); // wait specified time until we restart the transaction - } else { - // if the transaction still fail after $maxRetries retries, we throw out the exception - throw $e; - } - } - } - if ($newParent) { - $parentId = $newParent->getId(); - } - } elseif (!$request->query->get('parentId') && $parentPath) { - $parent = Asset::getByPath($parentPath); - if ($parent instanceof Asset\Folder) { - $parentId = $parent->getId(); - } else { //create defined parent folder, if doesn't exist. - $parentId = Asset\Service::createFolderByPath($parentPath)->getId(); - } - } - - $filename = Element\Service::getValidKey($filename, 'asset'); - if (empty($filename)) { - throw new Exception('The filename of the asset is empty'); - } - - $context = $request->query->get('context'); - if ($context) { - $context = json_decode($context, true); - $context = $context ?: []; - - $this->validateManyToManyRelationAssetType($context, $filename, $sourcePath); - - $event = new ResolveUploadTargetEvent($parentId, $filename); - $event->setArgument('context', $context); - - OpenDxp::getEventDispatcher()->dispatch($event, AssetEvents::RESOLVE_UPLOAD_TARGET); - $filename = Element\Service::getValidKey($event->getFilename(), 'asset'); - $parentId = $event->getParentId(); - } - - if (!$parentId) { - $parentId = Asset\Service::createFolderByPath($defaultUploadPath)->getId(); - } - - $parentAsset = Asset::getById((int)$parentId); - - if (!$request->query->get('allowOverwrite')) { - // check for duplicate filename - $filename = $this->getSafeFilename($parentAsset->getRealFullPath(), $filename); - } - - if (!$parentAsset->isAllowed('create')) { - throw $this->createAccessDeniedHttpException( - 'Missing the permission to create new assets in the folder: ' . $parentAsset->getRealFullPath() - ); - } - if (is_file($sourcePath) && filesize($sourcePath) < 1) { - throw new Exception('File is empty!'); - } - - if (!is_file($sourcePath)) { - throw new Exception('Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions of your temporary directories.'); - } - - // check if there is a requested type and if matches the asset type of the uploaded file - $uploadAssetType = $request->query->get('uploadAssetType'); - if ($uploadAssetType) { - $mimetype = MimeTypes::getDefault()->guessMimeType($sourcePath); - $assetType = Asset::getTypeFromMimeMapping($mimetype, $filename); - - if ($uploadAssetType !== $assetType) { - throw new Exception("Mime type $mimetype does not match with asset type: $uploadAssetType"); - } - } - - if ($request->query->get('allowOverwrite') && Asset\Service::pathExists($parentAsset->getRealFullPath().'/'.$filename)) { - $asset = Asset::getByPath($parentAsset->getRealFullPath().'/'.$filename); - $asset->setStream(fopen($sourcePath, 'rb', false, File::getContext())); - $asset->save(); - } else { - $asset = Asset::create($parentId, [ - 'filename' => $filename, - 'sourcePath' => $sourcePath, - 'userOwner' => $this->getAdminUser()->getId(), - 'userModification' => $this->getAdminUser()->getId(), - ]); - } - - @unlink($sourcePath); - - return [ - 'success' => true, - 'asset' => $asset, - ]; - } - - protected function getSafeFilename(string $targetPath, string $filename): string - { - $pathinfo = pathinfo($filename); - $originalFilename = $pathinfo['filename']; - $originalFileextension = empty($pathinfo['extension']) ? '' : '.' . $pathinfo['extension']; - $count = 1; - - if ($targetPath === '/') { - $targetPath = ''; - } - - while (true) { - if (Asset\Service::pathExists($targetPath . '/' . $filename)) { - $filename = $originalFilename . '_' . $count . $originalFileextension; - $count++; - } else { - return $filename; - } - } - } - - /** - * @throws Exception - */ - #[Route('/replace-asset', name: 'opendxp_admin_asset_replaceasset', methods: ['POST', 'PUT'])] - public function replaceAssetAction(Request $request, TranslatorInterface $translator): JsonResponse - { - $asset = Asset::getById((int) $request->query->get('id')); - - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); + $offset = isset($allParams['start']) ? (int) $allParams['start'] : 0; + $customViewId = ($allParams['view'] ?? null) ?: null; - $newFilename = Element\Service::getValidKey($file->getClientOriginalName(), 'asset'); - $mimetype = MimeTypes::getDefault()->guessMimeType($file->getPathname()); - $newType = Asset::getTypeFromMimeMapping($mimetype, $newFilename); + $result = $getChildren($nodeId, $customViewId, $filter, $limit, $offset); - if ($newType !== $asset->getType()) { + if ($allParams['limit'] ?? false) { return $this->adminJson([ - 'success' => false, - 'message' => sprintf($translator->trans('asset_type_change_not_allowed', [], 'admin'), $newType, $asset->getType()), + 'offset' => $result->offset, + 'limit' => $result->limit, + 'total' => $result->totalChildCount, + 'overflow' => $filter !== null && ($result->filteredTotalCount > $result->limit), + 'nodes' => $result->assets, + 'filter' => $result->filter ?: '', + 'inSearch' => $inSearch, ]); } - $stream = fopen($file->getPathname(), 'rb+'); - $asset->setStream($stream); - $asset->setCustomSetting('thumbnails', null); - - if (method_exists($asset, 'getEmbeddedMetaData')) { - $asset->getEmbeddedMetaData(true); - } - - $asset->setUserModification($this->getAdminUser()->getId()); - - $newFileExt = pathinfo($newFilename, PATHINFO_EXTENSION); - $currentFileExt = pathinfo($asset->getFilename(), PATHINFO_EXTENSION); - if ($newFileExt != $currentFileExt) { - $newFilename = preg_replace('/\.' . $currentFileExt . '$/i', '.' . $newFileExt, $asset->getFilename()); - $newFilename = Element\Service::getSafeCopyName($newFilename, $asset->getParent()); - $asset->setFilename($newFilename); - } - - if ($asset->isAllowed('publish')) { - $asset->save(); - - $response = $this->adminJson([ - 'id' => $asset->getId(), - 'path' => $asset->getRealFullPath(), - 'success' => true, - ]); - - // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in - // Ext.form.Action.Submit and mark the submission as failed - $response->headers->set('Content-Type', 'text/html'); - - return $response; - } - - throw new Exception('missing permission'); + return $this->adminJson($result->assets); } #[Route('/add-folder', name: 'opendxp_admin_asset_addfolder', methods: ['POST'])] - public function addFolderAction(Request $request): JsonResponse + public function addFolderAction(CreateAssetFolderHandler $createFolder, Request $request): JsonResponse { - $success = false; - $parentAsset = Asset::getById((int)$request->request->get('parentId')); - $equalAsset = Asset::getByPath($parentAsset->getRealFullPath() . '/' . $request->request->get('name')); - - if ($parentAsset->isAllowed('create')) { - if (!$equalAsset) { - $asset = Asset::create($request->request->get('parentId'), [ - 'filename' => $request->request->get('name'), - 'type' => 'folder', - 'userOwner' => $this->getAdminUser()->getId(), - 'userModification' => $this->getAdminUser()->getId(), - ]); - $success = true; - } - } else { - Logger::debug('prevented creating asset because of missing permissions'); - } + $createFolder( + (int) $request->request->get('parentId'), + (string) $request->request->get('name'), + ); - return $this->adminJson(['success' => $success]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/delete', name: 'opendxp_admin_asset_delete', methods: ['DELETE'])] - public function deleteAction(Request $request): JsonResponse + public function deleteAction(DeleteAssetHandler $deleteAsset, Request $request): JsonResponse { - $type = $request->request->get('type'); - - if ($type === 'children') { - $parentAsset = Asset::getById((int) $request->request->get('id')); - - $list = new Asset\Listing(); - $list->setCondition('`path` LIKE ?', [Helper::escapeLike($parentAsset->getRealFullPath()) . '/%']); - $list->setLimit((int)$request->request->get('amount')); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('DESC'); - - $deletedItems = []; - foreach ($list as $asset) { - $deletedItems[$asset->getId()] = $asset->getRealFullPath(); - if ($asset->isAllowed('delete') && !$asset->isLocked()) { - $asset->delete(); - } - } - - return $this->adminJson(['success' => true, 'deleted' => $deletedItems]); - } - if ($request->request->has('id')) { - $asset = Asset::getById((int) $request->request->get('id')); - if ($asset && $asset->isAllowed('delete')) { - if ($asset->isLocked()) { - return $this->adminJson([ - 'success' => false, - 'message' => 'prevented deleting asset, because it is locked: ID: ' . $asset->getId(), - ]); - } - - $asset->delete(); + $result = $deleteAsset( + (string) $request->request->get('type', ''), + (int) $request->request->get('id'), + (int) $request->request->get('amount', 0), + ); - return $this->adminJson(['success' => true]); - } + if ($result->deleted) { + return $this->adminJson(ApiResponse::ok(['deleted' => $result->deleted])); } - throw $this->createAccessDeniedHttpException(); - } - - /** - * @throws Exception - */ - #[Override] - protected function getTreeNodeConfig(ElementInterface $element): array - { - return $this->elementService->getElementTreeNodeConfig($element); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - * @throws RuntimeException - */ #[Route('/update', name: 'opendxp_admin_asset_update', methods: ['PUT'])] - public function updateAction(Request $request): JsonResponse + public function updateAction(UpdateAssetHandler $updateAsset, Request $request): JsonResponse { - $data = ['success' => false]; - $allowUpdate = true; - $updateData = [...$request->request->all(), ...$request->query->all()]; + $result = $updateAsset((int) $request->request->get('id'), $updateData); - $asset = Asset::getById((int) $request->request->get('id')); - if ($asset->isAllowed('settings')) { - $asset->setUserModification($this->getAdminUser()->getId()); - - // if the position is changed the path must be changed || also from the children - if ($parentId = $request->request->get('parentId')) { - $parentAsset = Asset::getById((int) $parentId); - - //check if parent is changed i.e. asset is moved - if ($asset->getParentId() !== $parentAsset->getId()) { - if (!$parentAsset->isAllowed('create')) { - throw new RuntimeException('Prevented moving asset - no create permission on new parent.'); - } - - $intendedPath = $parentAsset->getRealPath(); - $pKey = $parentAsset->getKey(); - if (!empty($pKey)) { - $intendedPath .= $parentAsset->getKey() . '/'; - } - - $assetWithSamePath = Asset::getByPath($intendedPath . $asset->getKey()); - - if ($assetWithSamePath != null) { - $allowUpdate = false; - } - - if ($asset->isLocked()) { - $allowUpdate = false; - } - } - } - - if ($allowUpdate) { - if ($request->request->get('filename') != $asset->getFilename() && !$asset->isAllowed('rename')) { - unset($updateData['filename']); - Logger::debug('prevented renaming asset because of missing permissions.'); - } - - $asset->setValues($updateData); - - try { - $asset->save(); - $data = [ - 'success' => true, - 'treeData' => $this->getTreeNodeConfig($asset), - ]; - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } else { - $msg = 'prevented moving asset, asset with same path+key already exists'; - $msg .= ' at target location or the asset is locked. ID: ' . $asset->getId(); - Logger::debug($msg); - - return $this->adminJson(['success' => false, 'message' => $msg]); - } - } elseif ($asset->isAllowed('rename') && $request->request->has('filename')) { - //just rename - try { - $asset->setFilename($request->request->get('filename')); - $asset->save(); - $data = [ - 'success' => true, - 'treeData' => $this->getTreeNodeConfig($asset), - ]; - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } else { - Logger::debug('prevented update asset because of missing permissions '); - } - - return $this->adminJson($data); + return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } - /** - * @throws Exception - */ #[Route('/save', name: 'opendxp_admin_asset_save', methods: ['PUT', 'POST'])] - public function saveAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $asset = Asset::getById((int) $request->request->get('id')); - - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - - if ($asset->isAllowed('publish')) { - // metadata - if ($request->request->has('metadata')) { - $metadata = $this->decodeJson($request->request->get('metadata')); - - $metadataEvent = new GenericEvent($this, [ - 'id' => $asset->getId(), - 'metadata' => $metadata, - ]); - $eventDispatcher->dispatch($metadataEvent, AdminEvents::ASSET_METADATA_PRE_SET); - - $metadata = $metadataEvent->getArgument('metadata'); - $metadataValues = $metadata['values']; - - $metadataValues = Asset\Service::minimizeMetadata($metadataValues, 'editor'); - $asset->setMetadataRaw($metadataValues); - } - - // properties - if ($request->request->has('properties')) { - $properties = []; - $propertiesData = $this->decodeJson($request->request->get('properties')); - - if (is_array($propertiesData)) { - foreach ($propertiesData as $propertyName => $propertyData) { - $value = $propertyData['data']; - - try { - $property = new Model\Property(); - $property->setType($propertyData['type']); - $property->setName($propertyName); - $property->setCtype('asset'); - $property->setDataFromEditmode($value); - $property->setInheritable($propertyData['inheritable']); - - $properties[$propertyName] = $property; - } catch (Exception) { - Logger::err("Can't add " . $propertyName . ' to asset ' . $asset->getRealFullPath()); - } - } - - $asset->setProperties($properties); - } - } - - $this->applySchedulerDataToElement($request, $asset, $this->getAdminUser()); - - if ($request->request->get('data')) { - $asset->setData($request->request->get('data')); - } - - // image specific data - if ($asset instanceof Asset\Image) { - if ($request->request->has('image')) { - $imageData = $this->decodeJson($request->request->get('image')); - if (isset($imageData['focalPoint'])) { - $asset->setCustomSetting('focalPointX', $imageData['focalPoint']['x']); - $asset->setCustomSetting('focalPointY', $imageData['focalPoint']['y']); - } - } else { - // wipe all data - $asset->removeCustomSetting('focalPointX'); - $asset->removeCustomSetting('focalPointY'); - } - } - - $asset->setUserModification($this->getAdminUser()->getId()); - if ($request->request->get('task') === 'session') { - // save to session only - Asset\Service::saveElementToSession($asset, $request->getSession()->getId()); - } else { - $asset->save(); - } - - $treeData = $this->getTreeNodeConfig($asset); - - return $this->adminJson([ - 'success' => true, - 'data' => [ - 'versionDate' => $asset->getModificationDate(), - 'versionCount' => $asset->getVersionCount(), - ], - 'treeData' => $treeData, - ]); - } - - throw $this->createAccessDeniedHttpException(); - } - - #[Route('/publish-version', name: 'opendxp_admin_asset_publishversion', methods: ['POST'])] - public function publishVersionAction(Request $request): JsonResponse - { - $id = (int)$request->request->get('id'); - $version = Model\Version::getById($id); - $asset = $version?->loadData(); - - if (!$asset) { - throw $this->createNotFoundException('Version with id [' . $id . "] doesn't exist"); - } - - $currentAsset = Asset::getById($asset->getId()); - if ($currentAsset->isAllowed('publish')) { - try { - $asset->setUserModification($this->getAdminUser()->getId()); - $asset->save(); - - $treeData = $this->getTreeNodeConfig($asset); - - return $this->adminJson(['success' => true, 'treeData' => $treeData]); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - throw $this->createAccessDeniedHttpException(); - } - - #[Route('/show-version', name: 'opendxp_admin_asset_showversion', methods: ['GET'])] - public function showVersionAction(Request $request, Environment $twig): Response + public function saveAction(SaveAssetHandler $saveAsset, Request $request): JsonResponse { - $id = (int)$request->query->get('id'); - $version = Model\Version::getById($id); - $asset = $version?->loadData(); - if (!$asset) { - throw $this->createNotFoundException('Version with id [' . $id . "] doesn't exist"); - } - - if (!$asset->isAllowed('versions')) { - throw $this->createAccessDeniedHttpException('Permission denied, version id [' . $id . ']'); - } - - if ($asset instanceof Asset\Document && $asset->getMimeType() === self::PDF_MIMETYPE) { - $previewData = ['thumbnailPath' => '']; - $previewData['assetPath'] = $asset->getRealFullPath(); - - return $this->render( - '@OpenDxpAdmin/admin/asset/get_preview_pdf_open_in_new_tab.html.twig', - $previewData - ); - } - - Tool\UserTimezone::setUserTimezone($request->query->get('userTimezone')); - - if ($timezone = Tool\UserTimezone::getUserTimezone()) { - $twig->getExtension(CoreExtension::class)->setTimezone($timezone); - } - - $loader = OpenDxp::getContainer()->get('opendxp.implementation_loader.asset.metadata.data'); - - return $this->render( - '@OpenDxpAdmin/admin/asset/show_version_' . strtolower($asset->getType()) . '.html.twig', - [ - 'asset' => $asset, - 'version' => $version, - 'loader' => $loader, - ] + $result = $saveAsset( + (int) $request->request->get('id'), + AssetPayload::fromRequest($request), ); + + return $this->adminJson(ApiResponse::ok([ + 'data' => [ + 'versionDate' => $result->versionDate, + 'versionCount' => $result->versionCount, + ], + 'treeData' => $result->treeData, + ])); } - #[Route('/download', name: 'opendxp_admin_asset_download', methods: ['GET'])] - public function downloadAction(Request $request): StreamedResponse + #[Route('/clear-thumbnail', name: 'opendxp_admin_asset_clearthumbnail', methods: ['POST'])] + public function clearThumbnailAction(ClearAssetThumbnailHandler $clearThumbnail, Request $request): JsonResponse { - $asset = Asset::getById((int) $request->query->get('id')); + $clearThumbnail((int) $request->request->get('id')); - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - - if (!$asset->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to view asset'); - } - - $stream = $asset->getStream(); - - if (!is_resource($stream)) { - throw $this->createNotFoundException('Unable to get resource for asset ' . $asset->getId()); - } - - return new StreamedResponse(function () use ($stream): void { - fpassthru($stream); - }, 200, [ - 'Content-Type' => $asset->getMimeType(), - 'Content-Disposition' => sprintf('attachment; filename: "%s"', $asset->getFilename()), - 'Content-Length' => $asset->getFileSize(), - ]); + return $this->adminJson(ApiResponse::ok()); } - #[Route('/download-image-thumbnail', name: 'opendxp_admin_asset_downloadimagethumbnail', methods: ['GET'])] - public function downloadImageThumbnailAction(Request $request): BinaryFileResponse + #[Override] + protected function getTreeNodeConfig(ElementInterface $element): array { - $image = Asset\Image::getById((int) $request->query->get('id')); - - if (!$image) { - throw $this->createNotFoundException('Asset not found'); - } - - if (!$image->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to view thumbnail'); - } - - $config = null; - $thumbnail = null; - $thumbnailName = $request->query->get('thumbnail'); - $thumbnailFile = null; - $deleteThumbnail = true; - - if ($request->query->has('config')) { - $config = $this->decodeJson($request->query->get('config')); - } elseif ($request->query->has('type')) { - $predefined = [ - 'web' => [ - 'resize_mode' => 'scaleByWidth', - 'width' => 3500, - 'dpi' => 72, - 'format' => 'JPEG', - 'quality' => 85, - ], - 'print' => [ - 'resize_mode' => 'scaleByWidth', - 'width' => 6000, - 'dpi' => 300, - 'format' => 'JPEG', - 'quality' => 95, - ], - 'office' => [ - 'resize_mode' => 'scaleByWidth', - 'width' => 1190, - 'dpi' => 144, - 'format' => 'JPEG', - 'quality' => 90, - ], - ]; - - $config = $predefined[$request->query->get('type')]; - } elseif ($thumbnailName) { - $thumbnail = $image->getThumbnail($thumbnailName); - $deleteThumbnail = false; - } - - if ($config) { - $thumbnailConfig = new Asset\Image\Thumbnail\Config(); - $thumbnailConfig->setName('opendxp-download-' . $image->getId() . '-' . md5($request->query->get('config'))); - - if ($config['resize_mode'] === 'scaleByWidth') { - $thumbnailConfig->addItem('scaleByWidth', [ - 'width' => $config['width'], - ]); - } elseif ($config['resize_mode'] === 'scaleByHeight') { - $thumbnailConfig->addItem('scaleByHeight', [ - 'height' => $config['height'], - ]); - } else { - $thumbnailConfig->addItem('resize', [ - 'width' => $config['width'], - 'height' => $config['height'], - ]); - } - - if (!empty($config['quality']) && $config['quality'] <= 100 && $config['quality'] > 0) { - $thumbnailConfig->setQuality($config['quality']); - } - - if (!empty($config['format'])) { - $thumbnailConfig->setFormat($config['format']); - } - - $thumbnailConfig->setRasterizeSVG(true); - - if ($thumbnailConfig->getFormat() === 'JPEG') { - $thumbnailConfig->setPreserveMetaData(true); - - if (empty($config['quality'])) { - $thumbnailConfig->setPreserveColor(true); - } - } - - $thumbnail = $image->getThumbnail($thumbnailConfig); - $thumbnailFile = $thumbnail->getLocalFile(); - - $exiftool = \OpenDxp\Tool\Console::getExecutable('exiftool'); - if ($thumbnailConfig->getFormat() === 'JPEG' && $exiftool && isset($config['dpi']) && $config['dpi']) { - $process = new Process([$exiftool, '-overwrite_original', '-xresolution=' . (int)$config['dpi'], '-yresolution=' . (int)$config['dpi'], '-resolutionunit=inches', $thumbnailFile]); - $process->run(); - } - } - - if ($thumbnail) { - $thumbnailConfig = $thumbnail->getConfig(); - if ($thumbnailConfig->getFormat() === 'SOURCE' && - $autoFormatConfigs = $thumbnailConfig->getAutoFormatThumbnailConfigs()) { - $autoFormatConfig = current($autoFormatConfigs); - $thumbnail = $image->getThumbnail($autoFormatConfig); - } - - $thumbnailFile = $thumbnailFile ?: $thumbnail->getLocalFile(); - - $downloadFilename = preg_replace( - '/\.' . preg_quote(pathinfo($image->getFilename(), PATHINFO_EXTENSION), '/') . '$/i', - '.' . $thumbnail->getFileExtension(), - $image->getFilename() - ); - - clearstatcache(); - - $response = new BinaryFileResponse($thumbnailFile); - $response->headers->set('Content-Type', $thumbnail->getMimeType()); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $downloadFilename); - $this->addThumbnailCacheHeaders($response); - $response->deleteFileAfterSend($deleteThumbnail); - - return $response; - } - - throw $this->createNotFoundException('Thumbnail not found'); - } - - #[Route('/get-asset', name: 'opendxp_admin_asset_getasset', methods: ['GET'])] - public function getAssetAction(Request $request): StreamedResponse - { - $image = Asset::getById((int)$request->query->get('id')); - - if (!$image) { - throw $this->createNotFoundException('Asset not found'); - } - - if (!$image->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to view asset'); - } - - $stream = $image->getStream(); - - if (!is_resource($stream)) { - throw $this->createNotFoundException('Unable to get resource for asset ' . $image->getId()); - } - - $response = new StreamedResponse(function () use ($stream): void { - fpassthru($stream); - }, 200, [ - 'Content-Type' => $image->getMimeType(), - 'Access-Control-Allow-Origin' => '*', - ]); - $this->addThumbnailCacheHeaders($response); - - return $response; - } - - #[Route('/get-image-thumbnail', name: 'opendxp_admin_asset_getimagethumbnail', methods: ['GET'])] - public function getImageThumbnailAction(Request $request): BinaryFileResponse|JsonResponse|StreamedResponse - { - $fileinfo = $request->query->get('fileinfo'); - $image = Asset\Image::getById((int)$request->query->get('id')); - - if (!$image) { - throw $this->createNotFoundException('Asset not found'); - } - - if (!$image->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to view thumbnail'); - } - - $thumbnailConfig = null; - - if ($thumbnailParam = $request->query->all()['thumbnail'] ?? null) { - $thumbnailConfig = $image->getThumbnail($thumbnailParam)->getConfig(); - } - if (!$thumbnailConfig) { - if ($request->query->get('config')) { - $thumbnailConfig = $image->getThumbnail($this->decodeJson($request->query->get('config')))->getConfig(); - } else { - $thumbnailConfig = $image->getThumbnail($request->query->all())->getConfig(); - } - } else { - // no high-res images in admin mode (editmode) - // this is mostly because of the document's image editable, which doesn't know anything about the thumbnail - // configuration, so the dimensions would be incorrect (double the size) - $thumbnailConfig->setHighResolution(1); - } - - $format = strtolower($thumbnailConfig->getFormat()); - if ($format === 'source' || $format === 'print') { - $thumbnailConfig->setFormat('PNG'); - $thumbnailConfig->setRasterizeSVG(true); - } - - if ($request->query->get('treepreview')) { - $thumbnailConfig = Asset\Image\Thumbnail\Config::getPreviewConfig(); - if (!$image->getThumbnail($thumbnailConfig)->exists()) { - - OpenDxp::getContainer()->get('messenger.bus.opendxp-core')->dispatch( - new AssetPreviewImageMessage($image->getId()) - ); - - if ($request->query->get('origin') === 'folderPreview') { - - $response = new BinaryFileResponse(OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/video-loading.gif'); - $response->headers->set('Cache-Control', 'no-store'); - - return $response; - } - - throw $this->createNotFoundException(sprintf('Tree preview thumbnail not available for asset %s', $image->getId())); - } - } - - $cropPercent = $request->query->get('cropPercent'); - if ($cropPercent && filter_var($cropPercent, FILTER_VALIDATE_BOOLEAN)) { - $thumbnailConfig->addItemAt(0, 'cropPercent', [ - 'width' => $request->query->get('cropWidth'), - 'height' => $request->query->get('cropHeight'), - 'y' => $request->query->get('cropTop'), - 'x' => $request->query->get('cropLeft'), - ]); - - $thumbnailConfig->generateAutoName(); - } - - $thumbnail = $image->getThumbnail($thumbnailConfig); - - if ($fileinfo) { - return $this->adminJson([ - 'width' => $thumbnail->getWidth(), - 'height' => $thumbnail->getHeight(), ]); - } - - $stream = $thumbnail->getStream(); - - if (!$stream) { - return new BinaryFileResponse(OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/filetype-not-supported.svg'); - } - - $response = new StreamedResponse(function () use ($stream): void { - fpassthru($stream); - }, 200, [ - 'Content-Type' => $thumbnail->getMimeType(), - 'Access-Control-Allow-Origin' => '*', - ]); - - $this->addThumbnailCacheHeaders($response); - - return $response; - } - - #[Route('/get-folder-thumbnail', name: 'opendxp_admin_asset_getfolderthumbnail', methods: ['GET'])] - public function getFolderThumbnailAction(Request $request): StreamedResponse - { - if ($request->query->has('id')) { - $folder = Asset\Folder::getById((int)$request->query->get('id')); - if ($folder instanceof Asset\Folder) { - if (!$folder->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to view thumbnail'); - } - - $stream = $folder->getPreviewImage(); - if (!$stream) { - throw $this->createNotFoundException(sprintf('Tree preview thumbnail not available for asset %s', $folder->getId())); - } - $response = new StreamedResponse(function () use ($stream): void { - fpassthru($stream); - }, 200, [ - 'Content-Type' => 'image/jpg', - ]); - - $this->addThumbnailCacheHeaders($response); - - return $response; - } - } - - throw $this->createNotFoundException('could not load asset folder'); - } - - #[Route('/get-video-thumbnail', name: 'opendxp_admin_asset_getvideothumbnail', methods: ['GET'])] - public function getVideoThumbnailAction(Request $request): StreamedResponse - { - $video = null; - - if ($request->query->has('id')) { - $video = Asset\Video::getById((int)$request->query->get('id')); - } elseif ($request->query->has('path')) { - $video = Asset\Video::getByPath($request->query->get('path')); - } - - if (!$video) { - throw $this->createNotFoundException('could not load video asset'); - } - - if (!$video->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to view thumbnail'); - } - - $thumbnailConfig = $request->query->all(); - - if ($request->query->has('treepreview')) { - $thumbnailConfig = Asset\Image\Thumbnail\Config::getPreviewConfig(); - } - - $time = null; - if (is_numeric($request->query->get('time'))) { - $time = (int)$request->query->get('time'); - } - - if ($request->query->has('settime')) { - $video->removeCustomSetting('image_thumbnail_asset'); - $video->setCustomSetting('image_thumbnail_time', $time); - $video->save(); - } - - $image = null; - if ($request->query->has('image')) { - $image = Asset\Image::getById((int)$request->query->get('image')); - } - - if ($request->query->has('setimage') && $image) { - $video->removeCustomSetting('image_thumbnail_time'); - $video->setCustomSetting('image_thumbnail_asset', $image->getId()); - $video->save(); - } - - $thumb = $video->getImageThumbnail($thumbnailConfig, $time, $image); - - if ($request->query->get('origin') === 'treeNode' && !$thumb->exists()) { - OpenDxp::getContainer()->get('messenger.bus.opendxp-core')->dispatch( - new AssetPreviewImageMessage($video->getId()) - ); - - throw $this->createNotFoundException(sprintf('Tree preview thumbnail not available for asset %s', $video->getId())); - } - - $stream = $thumb->getStream(); - if (!$stream) { - throw $this->createNotFoundException('Unable to get video thumbnail for video ' . $video->getId()); - } - - $response = new StreamedResponse(function () use ($stream): void { - fpassthru($stream); - }, 200, [ - 'Content-Type' => 'image/' . $thumb->getFileExtension(), - ]); - - $this->addThumbnailCacheHeaders($response); - - return $response; - } - - #[Route('/get-document-thumbnail', name: 'opendxp_admin_asset_getdocumentthumbnail', methods: ['GET'])] - public function getDocumentThumbnailAction(Request $request): BinaryFileResponse|StreamedResponse - { - $document = Asset\Document::getById((int)$request->query->get('id')); - - if (!$document) { - throw $this->createNotFoundException('could not load document asset'); - } - - if (!$document->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to view thumbnail'); - } - - $thumbnail = Asset\Image\Thumbnail\Config::getByAutoDetect($request->query->all()); - - $format = strtolower($thumbnail->getFormat()); - if ($format === 'source') { - $thumbnail->setFormat('jpeg'); // default format for documents is JPEG not PNG (=too big) - } - - if ($request->query->get('treepreview')) { - $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig(); - } - - $page = 1; - if (is_numeric($request->query->get('page'))) { - $page = (int)$request->query->get('page'); - } - - $thumb = $document->getImageThumbnail($thumbnail, $page); - - if ($request->query->get('origin') === 'treeNode' && !$thumb->exists()) { - OpenDxp::getContainer()->get('messenger.bus.opendxp-core')->dispatch( - new AssetPreviewImageMessage($document->getId()) - ); - - throw $this->createNotFoundException(sprintf('Tree preview thumbnail not available for asset %s', $document->getId())); - } - - $stream = $thumb->getStream(); - if ($stream) { - $response = new StreamedResponse(function () use ($stream): void { - fpassthru($stream); - }, 200, [ - 'Content-Type' => 'image/' . $thumb->getFileExtension(), - ]); - } else { - $response = new BinaryFileResponse(OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/filetype-not-supported.svg'); - } - - $this->addThumbnailCacheHeaders($response); - - return $response; - } - - protected function addThumbnailCacheHeaders(Response $response): void - { - $lifetime = 300; - $date = new DateTime('now'); - $date->add(new DateInterval('PT' . $lifetime . 'S')); - - $response->setMaxAge($lifetime); - $response->setPublic(); - $response->setExpires($date); - $response->headers->set('Pragma', ''); - } - - #[Route('/get-preview-document', name: 'opendxp_admin_asset_getpreviewdocument', methods: ['GET'])] - public function getPreviewDocumentAction(Request $request): StreamedResponse|Response - { - $asset = Asset\Document::getById((int) $request->query->get('id')); - - if (!$asset instanceof Asset\Document) { - throw $this->createNotFoundException('could not load document asset'); - } - - if ($asset->isAllowed('view')) { - - if ($asset->getMimeType() === self::PDF_MIMETYPE) { - $scanResponse = $this->getResponseByScanStatus($asset); - $openPdfConfig = Config::getSystemConfiguration('assets')['document']['open_pdf_in_new_tab']; - - if ($openPdfConfig === 'all-pdfs' || - ($openPdfConfig === 'only-unsafe' && $scanResponse === PdfScanStatus::UNSAFE)) { - $thumbnail = $asset->getImageThumbnail(Asset\Image\Thumbnail\Config::getPreviewConfig()); - $previewData = ['thumbnailPath' => $thumbnail->getPath()]; - $previewData['assetPath'] = $asset->getRealFullPath(); - - return $this->render( - '@OpenDxpAdmin/admin/asset/get_preview_pdf_open_in_new_tab.html.twig', - $previewData - ); - } - - if ($scanResponse === PdfScanStatus::IN_PROGRESS) { - return $this->render('@OpenDxpAdmin/admin/asset/get_preview_pdf_in_progress.html.twig'); - } - - if ($scanResponse === PdfScanStatus::UNSAFE) { - return $this->render('@OpenDxpAdmin/admin/asset/get_preview_pdf_unsafe.html.twig'); - } - } - - $stream = $this->getDocumentPreviewPdf($asset); - if ($stream) { - return new StreamedResponse(function () use ($stream): void { - fpassthru($stream); - }, 200, [ - 'Content-Type' => self::PDF_MIMETYPE, - ]); - } - - throw $this->createNotFoundException('Unable to get preview for asset ' . $asset->getId()); - - } - - throw $this->createAccessDeniedException('Access to asset ' . $asset->getId() . ' denied'); - } - - private function getResponseByScanStatus(Asset\Document $asset, bool $processBackground = true): ?PdfScanStatus - { - if (!Config::getSystemConfiguration('assets')['document']['scan_pdf']) { - return null; - } - - $scanStatus = $asset->getScanStatus(); - if (!$scanStatus instanceof \OpenDxp\Model\Asset\Enum\PdfScanStatus) { - $scanStatus = Asset\Enum\PdfScanStatus::IN_PROGRESS; - if ($processBackground) { - $asset->addToUpdateTaskQueue(); - } - } - - return $scanStatus; - } - - /** - * @return resource|null - */ - protected function getDocumentPreviewPdf(Asset\Document $asset) - { - $stream = null; - - if ($asset->getMimeType() == self::PDF_MIMETYPE) { - $stream = $asset->getStream(); - } - - if ( - !$stream && - $asset->getPageCount() && - \OpenDxp\Document::isAvailable() && - \OpenDxp\Document::isFileTypeSupported($asset->getFilename()) - ) { - try { - $document = \OpenDxp\Document::getInstance(); - $stream = $document->getPdf($asset); - } catch (Exception) { - // nothing to do - } - } - - return $stream; - } - - #[Route('/get-preview-video', name: 'opendxp_admin_asset_getpreviewvideo', methods: ['GET'])] - public function getPreviewVideoAction(Request $request): Response - { - $asset = Asset\Video::getById((int) $request->query->get('id')); - $configName = $request->query->get('config'); - - if (!$asset) { - throw $this->createNotFoundException('could not load video asset'); - } - - if (!$asset->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to preview'); - } - - $previewData = ['asset' => $asset]; - - $config = Asset\Video\Thumbnail\Config::getByName($configName); - - if (!$config instanceof Asset\Video\Thumbnail\Config) { - $config = Asset\Video\Thumbnail\Config::getPreviewConfig(); - } - - $thumbnail = $asset->getThumbnail($config, ['mp4']); - - if ($thumbnail) { - $previewData['asset'] = $asset; - $previewData['thumbnail'] = $thumbnail; - $previewData['config'] = $config->getName(); - - if ($thumbnail['status'] === 'finished') { - return $this->render( - '@OpenDxpAdmin/admin/asset/get_preview_video_display.html.twig', - $previewData - ); - } - - return $this->render( - '@OpenDxpAdmin/admin/asset/get_preview_video_error.html.twig', - $previewData - ); - } - - return $this->render( - '@OpenDxpAdmin/admin/asset/get_preview_video_error.html.twig', - $previewData - ); - } - - #[Route('/serve-video-preview', name: 'opendxp_admin_asset_servevideopreview', methods: ['GET'])] - public function serveVideoPreviewAction(Request $request): StreamedResponse - { - $asset = Asset\Video::getById((int) $request->query->get('id')); - $configName = $request->query->get('config'); - - if (!$asset) { - throw $this->createNotFoundException('could not load video asset'); - } - - if (!$asset->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to preview'); - } - - $config = Asset\Video\Thumbnail\Config::getByName($configName); - - if (!$config instanceof Asset\Video\Thumbnail\Config) { - $config = Asset\Video\Thumbnail\Config::getPreviewConfig(); - } - - $thumbnail = $asset->getThumbnail($config, ['mp4']); - $storagePath = $asset->getRealPath() . '/' . preg_replace('@^' . preg_quote($asset->getPath(), '@') . '@', '', urldecode($thumbnail['formats']['mp4'])); - - $storage = Tool\Storage::get('thumbnail'); - if ($storage->fileExists($storagePath)) { - $fs = $storage->fileSize($storagePath); - $stream = $storage->readStream($storagePath); - - return new StreamedResponse(function () use ($stream): void { - fpassthru($stream); - }, 200, [ - 'Content-Type' => 'video/mp4', - 'Content-Length' => $fs, - 'Accept-Ranges' => 'bytes', - ]); - } - - throw $this->createNotFoundException('Video thumbnail not found'); - } - - #[Route('/image-editor', name: 'opendxp_admin_asset_imageeditor', methods: ['GET'])] - public function imageEditorAction(Request $request): Response - { - $asset = Asset::getById((int) $request->query->get('id')); - - if (!$asset->isAllowed('view')) { - throw $this->createAccessDeniedException('Not allowed to preview'); - } - - return $this->render( - '@OpenDxpAdmin/admin/asset/image_editor.html.twig', - ['asset' => $asset] - ); - } - - #[Route('/image-editor-save', name: 'opendxp_admin_asset_imageeditorsave', methods: ['PUT'])] - public function imageEditorSaveAction(Request $request): JsonResponse - { - $asset = Asset::getById((int) $request->query->get('id')); - - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - - if (!$asset->isAllowed('publish')) { - throw $this->createAccessDeniedException('not allowed to publish'); - } - - $data = $request->request->get('dataUri'); - $data = substr($data, strpos($data, ',')); - $data = base64_decode($data); - $asset->setData($data); - $asset->setUserModification($this->getAdminUser()->getId()); - $asset->save(); - - return $this->adminJson(['success' => true]); - } - - #[Route('/get-folder-content-preview', name: 'opendxp_admin_asset_getfoldercontentpreview', methods: ['GET'])] - public function getFolderContentPreviewAction(Request $request, - EventDispatcherInterface $eventDispatcher, - GridHelperService $gridHelperService): JsonResponse - { - $allParams = $request->query->all(); - - $filterPrepareEvent = new GenericEvent($this, [ - 'requestParams' => $allParams, - ]); - $eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE); - - $allParams = $filterPrepareEvent->getArgument('requestParams'); - - $folder = Asset::getById((int) $allParams['id']); - - $start = 0; - $limit = 10; - - if ($allParams['limit']) { - $limit = $allParams['limit']; - } - if ($allParams['start']) { - $start = $allParams['start']; - } - - $conditionFilters = []; - $list = new Asset\Listing(); - $conditionFilters[] = '`path` LIKE ' . ($folder->getRealFullPath() === '/' ? "'/%'" : $list->quote(Helper::escapeLike($folder->getRealFullPath()) . '/%')) . " AND `type` != 'folder'"; - - $adminUser = $this->getAdminUser(); - - if (!$adminUser->isAdmin()) { - $conditionFilters[] = $gridHelperService->getPermittedPathsByUser('asset', $adminUser); - } - - $condition = implode(' AND ', $conditionFilters); - - $list->setCondition($condition); - $list->setLimit($limit); - $list->setOffset($start); - $list->setOrderKey('CAST(filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci ASC', false); - - $beforeListLoadEvent = new GenericEvent($this, [ - 'list' => $list, - 'context' => $allParams, - ]); - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD); - /** @var Asset\Listing $list */ - $list = $beforeListLoadEvent->getArgument('list'); - - $list->load(); - - $assets = []; - - foreach ($list as $asset) { - $filenameDisplay = $asset->getFilename(); - if (strlen($filenameDisplay) > 32) { - $filenameDisplay = substr($filenameDisplay, 0, 25) . '...' . pathinfo($filenameDisplay, PATHINFO_EXTENSION); - } - - // Like for treeGetChildrenByIdAction, so we respect isAllowed method which can be extended (object DI) for custom permissions, - // so relying only users_workspaces_asset is insufficient and could lead security breach - if ($asset->isAllowed('list')) { - - $assets[] = [ - 'id' => $asset->getId(), - 'type' => $asset->getType(), - 'filename' => $asset->getFilename(), - 'filenameDisplay' => htmlspecialchars($filenameDisplay ?? ''), - 'url' => $this->elementService->getThumbnailUrl($asset, ['origin' => 'folderPreview']), - 'idPath' => Element\Service::getIdPath($asset), - ]; - } - } - - // We need to temporary use data key to be compatible with the ASSET_LIST_AFTER_LIST_LOAD global event - $result = ['data' => $assets, 'success' => true, 'total' => $list->getTotalCount()]; - - $afterListLoadEvent = new GenericEvent($this, [ - 'list' => $result, - 'context' => $allParams, - ]); - $eventDispatcher->dispatch($afterListLoadEvent, AdminEvents::ASSET_LIST_AFTER_LIST_LOAD); - $result = $afterListLoadEvent->getArgument('list'); - - // Here we revert to assets key - return $this->adminJson(['assets' => $result['data'], 'success' => $result['success'], 'total' => $result['total']]); - } - - #[Route('/copy-info', name: 'opendxp_admin_asset_copyinfo', methods: ['GET'])] - public function copyInfoAction(Request $request): JsonResponse - { - $transactionId = time(); - $pasteJobs = []; - - Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { - $session->set((string) $transactionId, []); - }, 'opendxp_copy'); - - if ($request->query->get('type') === 'recursive') { - $asset = Asset::getById((int) $request->query->get('sourceId')); - - if (!$asset) { - throw $this->createNotFoundException('Source not found'); - } - - // first of all the new parent - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_asset_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $request->query->get('sourceId'), - 'targetId' => $request->query->get('targetId'), - 'type' => 'child', - 'transactionId' => $transactionId, - 'saveParentId' => true, - ], - ]]; - - if ($asset->hasChildren()) { - // get amount of children - $list = new Asset\Listing(); - $list->setCondition('`path` LIKE ?', [$list->escapeLike($asset->getRealFullPath()) . '/%']); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('ASC'); - $childIds = $list->loadIdList(); - - if (count($childIds) > 0) { - foreach ($childIds as $id) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_asset_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $id, - 'targetParentId' => $request->query->get('targetId'), - 'sourceParentId' => $request->query->get('sourceId'), - 'type' => 'child', - 'transactionId' => $transactionId, - ], - ]]; - } - } - } - } elseif ($request->query->get('type') === 'child' || $request->query->get('type') === 'replace') { - // the object itself is the last one - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_asset_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $request->query->get('sourceId'), - 'targetId' => $request->query->get('targetId'), - 'type' => $request->query->get('type'), - 'transactionId' => $transactionId, - ], - ]]; - } - - return $this->adminJson([ - 'pastejobs' => $pasteJobs, - ]); - } - - #[Route('/copy', name: 'opendxp_admin_asset_copy', methods: ['POST'])] - public function copyAction(Request $request): JsonResponse - { - $success = false; - $sourceId = (int)$request->request->get('sourceId'); - $source = Asset::getById($sourceId); - - $session = Tool\Session::getSessionBag($request->getSession(), 'opendxp_copy'); - $sessionBag = $session->get($request->request->get('transactionId')); - - $targetId = (int)$request->request->get('targetId'); - if ($request->request->has('targetParentId')) { - $sourceParent = Asset::getById((int) $request->request->get('sourceParentId')); - - // this is because the key can get the prefix "_copy" if the target does already exists - if ($sessionBag['parentId']) { - $targetParent = Asset::getById((int) $sessionBag['parentId']); - } else { - $targetParent = Asset::getById((int) $request->request->get('targetParentId')); - } - - $targetPath = preg_replace('@^' . $sourceParent->getRealFullPath() . '@', $targetParent . '/', $source->getRealPath()); - $target = Asset::getByPath($targetPath); - } else { - $target = Asset::getById($targetId); - } - - if (!$target) { - throw $this->createNotFoundException('Target not found'); - } - - if ($target->isAllowed('create')) { - $source = Asset::getById($sourceId); - if ($source !== null) { - if ($request->request->get('type') === 'child') { - $newAsset = $this->_assetService->copyAsChild($target, $source); - - // this is because the key can get the prefix "_copy" if the target does already exists - if ($request->request->get('saveParentId')) { - $sessionBag['parentId'] = $newAsset->getId(); - } - } elseif ($request->request->get('type') === 'replace') { - $this->_assetService->copyContents($target, $source); - } - - $session->set($request->request->get('transactionId'), $sessionBag); - - $success = true; - } else { - Logger::debug('prevended copy/paste because asset with same path+key already exists in this location'); - } - } else { - Logger::error('could not execute copy/paste because of missing permissions on target [ ' . $targetId . ' ]'); - - throw $this->createAccessDeniedHttpException(); - } - - return $this->adminJson(['success' => $success]); - } - - #[Route('/download-as-zip-jobs', name: 'opendxp_admin_asset_downloadaszipjobs', methods: ['GET'])] - public function downloadAsZipJobsAction(Request $request): JsonResponse - { - $jobId = uniqid('', false); - $filesPerJob = 5; - $jobs = []; - $asset = Asset::getById((int) $request->query->get('id')); - - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - - if ($asset->isAllowed('view')) { - $parentPath = $asset->getRealFullPath(); - if ($asset->getId() == 1) { - $parentPath = ''; - } - - $db = \OpenDxp\Db::get(); - $conditionFilters = []; - $selectedIds = explode(',', $request->query->get('selectedIds', '')); - $quotedSelectedIds = []; - foreach ($selectedIds as $selectedId) { - if ($selectedId) { - $quotedSelectedIds[] = $db->quote($selectedId); - } - } - if ($quotedSelectedIds !== []) { - //add a condition if id numbers are specified - $conditionFilters[] = 'id IN (' . implode(',', $quotedSelectedIds) . ')'; - } - $conditionFilters[] = '`path` LIKE ' . $db->quote(Helper::escapeLike($parentPath) . '/%') . ' AND `type` != ' . $db->quote('folder'); - if (!$this->getAdminUser()->isAdmin()) { - $userIds = $this->getAdminUser()->getRoles(); - $userIds[] = $this->getAdminUser()->getId(); - $conditionFilters[] = ' ( - (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`, filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - OR - (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`, filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - )'; - } - - $condition = implode(' AND ', $conditionFilters); - - $assetList = new Asset\Listing(); - $assetList->setCondition($condition); - $assetList->setOrderKey('LENGTH(`path`)', false); - $assetList->setOrder('ASC'); - - for ($i = 0; $i < ceil($assetList->getTotalCount() / $filesPerJob); $i++) { - $jobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_asset_downloadaszipaddfiles'), - 'method' => 'GET', - 'params' => [ - 'id' => $asset->getId(), - 'selectedIds' => implode(',', $selectedIds), - 'offset' => $i * $filesPerJob, - 'limit' => $filesPerJob, - 'jobId' => $jobId, - ], - ]]; - } - } - - return $this->adminJson([ - 'success' => true, - 'jobs' => $jobs, - 'jobId' => $jobId, - ]); - } - - #[Route('/download-as-zip-add-files', name: 'opendxp_admin_asset_downloadaszipaddfiles', methods: ['GET'])] - public function downloadAsZipAddFilesAction(Request $request): JsonResponse - { - $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/download-zip-' . $request->query->get('jobId') . '.zip'; - $asset = Asset::getById((int) $request->query->get('id')); - $success = false; - - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - - if ($asset->isAllowed('view')) { - $zip = new ZipArchive(); - $zipState = is_file($zipFile) ? $zip->open($zipFile) : $zip->open($zipFile, ZipArchive::CREATE); - - if ($zipState === true) { - $parentPath = $asset->getRealFullPath(); - if ($asset->getId() === 1) { - $parentPath = ''; - } - - $db = \OpenDxp\Db::get(); - $conditionFilters = []; - - $selectedIds = $request->query->get('selectedIds', null); - - if (!empty($selectedIds)) { - $selectedIds = explode(',', $selectedIds); - $quotedSelectedIds = []; - foreach ($selectedIds as $selectedId) { - if ($selectedId) { - $quotedSelectedIds[] = $db->quote($selectedId); - } - } - //add a condition if id numbers are specified - $conditionFilters[] = 'id IN (' . implode(',', $quotedSelectedIds) . ')'; - } - $conditionFilters[] = "`type` != 'folder' AND `path` like " . $db->quote(Helper::escapeLike($parentPath) . '/%'); - if (!$this->getAdminUser()->isAdmin()) { - $userIds = $this->getAdminUser()->getRoles(); - $userIds[] = $this->getAdminUser()->getId(); - $conditionFilters[] = ' ( - (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`, filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - OR - (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`, filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - )'; - } - - $condition = implode(' AND ', $conditionFilters); - - $assetList = new Asset\Listing(); - $assetList->setCondition($condition); - $assetList->setOrderKey('LENGTH(`path`) ASC, id ASC', false); - $assetList->setOffset((int)$request->query->get('offset')); - $assetList->setLimit((int)$request->query->get('limit')); - - foreach ($assetList as $a) { - if (!$a->isAllowed('view')) { - continue; - } - if ($a instanceof Asset\Folder) { - continue; - } - // add the file with the relative path to the parent directory - $zip->addFile($a->getLocalFile(), preg_replace('@^' . preg_quote($asset->getRealPath(), '@') . '@i', '', $a->getRealFullPath())); - } - - $zip->close(); - $success = true; - } - } - - return $this->adminJson([ - 'success' => $success, - ]); - } - - /** - * Download all assets contained in the folder with parameter id as ZIP file. - * The suggested filename is either [folder name].zip or assets.zip for the root folder. - */ - #[Route('/download-as-zip', name: 'opendxp_admin_asset_downloadaszip', methods: ['GET'])] - public function downloadAsZipAction(Request $request): BinaryFileResponse - { - $asset = Asset::getById((int) $request->query->get('id')); - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/download-zip-' . $request->query->get('jobId') . '.zip'; - $suggestedFilename = $asset->getFilename(); - if (empty($suggestedFilename)) { - $suggestedFilename = 'assets'; - } - - $response = new BinaryFileResponse($zipFile); - $response->headers->set('Content-Type', 'application/zip'); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $suggestedFilename . '.zip'); - $response->deleteFileAfterSend(true); - - return $response; - } - - #[Route('/import-zip', name: 'opendxp_admin_asset_importzip', methods: ['POST'])] - public function importZipAction(Request $request, TranslatorInterface $translator): Response - { - $jobId = uniqid('', false); - $filesPerJob = 5; - $jobs = []; - $asset = Asset::getById((int) $request->query->get('parentId')); - - $filePath = null; - if ($request->files->has('Filedata')) { - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $filePath = $file->getPathname(); - } - - if ($filePath === null || !is_file($filePath)) { - return $this->adminJson([ - 'success' => false, - 'message' => 'Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions on the file system', - ]); - } - - if (!$asset) { - throw $this->createNotFoundException('Parent asset not found'); - } - - if (!$asset->isAllowed('create')) { - throw $this->createAccessDeniedException('not allowed to create'); - } - - $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . $jobId . '.zip'; - - copy($filePath, $zipFile); - - $zip = new ZipArchive; - $retCode = $zip->open($zipFile); - if ($retCode === true) { - $jobAmount = ceil($zip->numFiles / $filesPerJob); - for ($i = 0; $i < $jobAmount; $i++) { - $jobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_asset_importzipfiles'), - 'method' => 'POST', - 'params' => [ - 'parentId' => $asset->getId(), - 'offset' => $i * $filesPerJob, - 'limit' => $filesPerJob, - 'jobId' => $jobId, - 'last' => (($i + 1) >= $jobAmount) ? 'true' : '', - 'allowOverwrite' => $request->query->get('allowOverwrite') ?: 'false', - ], - ]]; - } - $zip->close(); - - // here we have to use this method and not the JSON action helper ($this->_helper->json()) because this will add - // Content-Type: application/json which fires a download window in most browsers, because this is a normal POST - // request and not XHR where the content-type doesn't matter - $responseJson = $this->encodeJson([ - 'success' => true, - 'jobs' => $jobs, - 'jobId' => $jobId, - ]); - - return new Response($responseJson); - } - - return $this->adminJson([ - 'success' => false, - 'message' => $translator->trans('could_not_open_zip_file', [], 'admin'), - ]); - } - - #[Route('/import-zip-files', name: 'opendxp_admin_asset_importzipfiles', methods: ['POST'])] - public function importZipFilesAction(Request $request, Filesystem $filesystem): JsonResponse - { - $jobId = $request->request->get('jobId'); - $limit = (int)$request->request->get('limit'); - $offset = (int)$request->request->get('offset'); - $importAsset = Asset::getById((int) $request->request->get('parentId')); - $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . $jobId . '.zip'; - $tmpDir = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/zip-import'; - - if (!is_dir($tmpDir)) { - $filesystem->mkdir($tmpDir); - } - - $zip = new ZipArchive; - if ($zip->open($zipFile) === true) { - for ($i = $offset; $i < ($offset + $limit); $i++) { - $path = $zip->getNameIndex($i); - if (str_starts_with($path, '__MACOSX/')) { - continue; - } - if (str_ends_with($path, '/Thumbs.db')) { - continue; - } - if (str_ends_with($path, '/.DS_Store')) { - continue; - } - - if ($path !== false && $zip->extractTo($tmpDir . '/', $path)) { - $tmpFile = $tmpDir . '/' . preg_replace('@^/@', '', $path); - $filename = Element\Service::getValidKey(basename($path), 'asset'); - $relativePath = ''; - if (dirname($path) !== '.') { - $relativePath = dirname($path); - } - $parentPath = $importAsset->getRealFullPath() . '/' . preg_replace('@^/@', '', $relativePath); - $parent = Asset\Service::createFolderByPath($parentPath); - // check for duplicate filename - if ($request->request->has('allowOverwrite') && $request->request->get('allowOverwrite') !== 'true') { - $filename = $this->getSafeFilename($parent->getRealFullPath(), $filename); - } - - if ($parent->isAllowed('create')) { - if ($request->request->has('allowOverwrite') && $request->request->get('allowOverwrite') === 'true' - && Asset\Service::pathExists($parent->getRealFullPath().'/'.$filename)) { - $asset = Asset::getByPath($parent->getRealFullPath().'/'.$filename); - $asset->setStream(fopen($tmpFile, 'rb', false, File::getContext())); - $asset->save(); - } else { - Asset::create($parent->getId(), [ - 'filename' => $filename, - 'sourcePath' => $tmpFile, - 'userOwner' => $this->getAdminUser()->getId(), - 'userModification' => $this->getAdminUser()->getId(), - ]); - } - - @unlink($tmpFile); - } else { - Logger::debug('prevented creating asset because of missing permissions'); - } - } - } - $zip->close(); - } - - if ($request->request->get('last')) { - unlink($zipFile); - } - - return $this->adminJson([ - 'success' => true, - ]); - } - - #[Route('/clear-thumbnail', name: 'opendxp_admin_asset_clearthumbnail', methods: ['POST'])] - public function clearThumbnailAction(Request $request): JsonResponse - { - $success = false; - - if ($asset = Asset::getById((int) $request->request->get('id'))) { - if (!$asset->isAllowed('publish')) { - throw $this->createAccessDeniedException('not allowed to publish'); - } - - $asset->clearThumbnails(true); // force clear - $asset->save(); - - $success = true; - } - - return $this->adminJson(['success' => $success]); - } - - #[Route('/grid-proxy', name: 'opendxp_admin_asset_gridproxy', methods: ['GET', 'POST', 'PUT'])] - public function gridProxyAction(Request $request, EventDispatcherInterface $eventDispatcher, GridHelperService $gridHelperService, CsrfProtectionHandler $csrfProtection): JsonResponse - { - $allParams = [...$request->request->all(), ...$request->query->all()]; - - $filterPrepareEvent = new GenericEvent($this, [ - 'requestParams' => $allParams, - ]); - $language = $request->query->get('language') !== 'default' ? $request->query->get('language') : null; - - $eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE); - - $allParams = $filterPrepareEvent->getArgument('requestParams'); - - $loader = OpenDxp::getContainer()->get('opendxp.implementation_loader.asset.metadata.data'); - - if (isset($allParams['data']) && $allParams['data']) { - $csrfProtection->checkCsrfToken($request); - if ($allParams['xaction'] === 'update') { - try { - $data = $this->decodeJson($allParams['data']); - - $updateEvent = new GenericEvent($this, [ - 'data' => $data, - 'processed' => false, - ]); - - $eventDispatcher->dispatch($updateEvent, AdminEvents::ASSET_LIST_BEFORE_UPDATE); - - $processed = $updateEvent->getArgument('processed'); - - if ($processed) { - // update already processed by event handler - return $this->adminJson(['success' => true]); - } - - $data = $updateEvent->getArgument('data'); - - // save - $asset = Asset::getById((int) $data['id']); - - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - - if (!$asset->isAllowed('publish')) { - throw $this->createAccessDeniedException("Permission denied. You don't have the rights to save this asset."); - } - - $metadata = $asset->getMetadata(null, null, false, true); - $dirty = false; - - unset($data['id']); - foreach ($data as $key => $value) { - $fieldDef = explode('~', $key); - $key = $fieldDef[0]; - if (isset($fieldDef[1])) { - $language = ($fieldDef[1] === 'none' ? '' : $fieldDef[1]); - } - - foreach ($metadata as &$em) { - if ($em['name'] == $key && $em['language'] == $language) { - try { - $dataImpl = $loader->build($em['type']); - $value = $dataImpl->getDataFromListfolderGrid($value, $em); - } catch (UnsupportedException) { - Logger::error('could not resolve metadata implementation for ' . $em['type']); - } - - $em['data'] = $value; - $dirty = true; - - break; - } - } - - if (!$dirty) { - $defaulMetadata = ['title', 'alt', 'copyright']; - if (in_array($key, $defaulMetadata)) { - $newEm = [ - 'name' => $key, - 'language' => $language, - 'type' => 'input', - 'data' => $value, - ]; - - try { - $dataImpl = $loader->build($newEm['type']); - $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm); - } catch (UnsupportedException) { - Logger::error('could not resolve metadata implementation for ' . $newEm['type']); - } - - $metadata[] = $newEm; - - $dirty = true; - } else { - $predefined = Model\Metadata\Predefined::getByName($key); - if ($predefined && (empty($predefined->getTargetSubtype()) - || $predefined->getTargetSubtype() === $asset->getType())) { - $newEm = [ - 'name' => $key, - 'language' => $language, - 'type' => $predefined->getType(), - 'data' => $value, - ]; - - try { - $dataImpl = $loader->build($newEm['type']); - $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm); - } catch (UnsupportedException) { - Logger::error('could not resolve metadata implementation for ' . $newEm['type']); - } - - $metadata[] = $newEm; - $dirty = true; - } - } - } - } - - if ($dirty) { - $metadataEvent = new GenericEvent($this, [ - 'id' => $asset->getId(), - 'metadata' => $metadata, - ]); - $eventDispatcher->dispatch($metadataEvent, AdminEvents::ASSET_METADATA_PRE_SET); - - // $metadata = Asset\Service::minimizeMetadata($metadata, "grid"); - $asset->setMetadataRaw($metadata); - $asset->save(); - - return $this->adminJson(['success' => true]); - } - - return $this->adminJson(['success' => false, 'message' => 'something went wrong.']); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - } else { - $list = $gridHelperService->prepareAssetListingForGrid($allParams, $this->getAdminUser()); - - $beforeListLoadEvent = new GenericEvent($this, [ - 'list' => $list, - 'context' => $allParams, - ]); - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD); - /** @var Asset\Listing $list */ - $list = $beforeListLoadEvent->getArgument('list'); - - $list->load(); - - $assets = []; - foreach ($list->getAssets() as $asset) { - // Like for treeGetChildrenByIdAction, so we respect isAllowed method which can be extended (object DI) for custom permissions, so relying only users_workspaces_asset is insufficient and could lead security breach - if ($asset->isAllowed('list')) { - $a = GridData\Asset::getData($asset, $allParams['fields'], $allParams['language'] ?? ''); - $assets[] = $a; - } - } - - $result = ['data' => $assets, 'success' => true, 'total' => $list->getTotalCount()]; - - $afterListLoadEvent = new GenericEvent($this, [ - 'list' => $result, - 'context' => $allParams, - ]); - $eventDispatcher->dispatch($afterListLoadEvent, AdminEvents::ASSET_LIST_AFTER_LIST_LOAD); - $result = $afterListLoadEvent->getArgument('list'); - - return $this->adminJson($result); - } - - return $this->adminJson(['success' => false]); - } - - #[Route('/get-text', name: 'opendxp_admin_asset_gettext', methods: ['GET'])] - public function getTextAction(Request $request): JsonResponse - { - $asset = Asset::getById((int) $request->query->get('id')); - - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - - if (!$asset->isAllowed('view')) { - throw $this->createAccessDeniedException('not allowed to view'); - } - - $page = $request->query->get('page'); - $text = null; - if ($asset instanceof Asset\Document) { - $text = $asset->getText(empty($page) ? null : (int)$page); - } - - return $this->adminJson(['success' => 'true', 'text' => $text]); - } - - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - $this->checkActionPermission($event, 'assets', [ - 'getImageThumbnailAction', 'getVideoThumbnailAction', 'getDocumentThumbnailAction', - ]); - - $this->_assetService = new Asset\Service($this->getAdminUser()); - } - - /** - * @throws ValidationException - */ - private function validateManyToManyRelationAssetType(array $context, string $filename, string $sourcePath): void - { - if (isset($context['containerType'], $context['objectId'], $context['fieldname']) - && 'object' === $context['containerType'] - && $object = Concrete::getById($context['objectId']) - ) { - $fieldDefinition = $object->getClass()->getFieldDefinition($context['fieldname']); - if (!$fieldDefinition instanceof ManyToManyRelation) { - return; - } - - $mimeType = MimeTypes::getDefault()->guessMimeType($sourcePath); - $type = Asset::getTypeFromMimeMapping($mimeType, $filename); - - $allowedAssetTypes = $fieldDefinition->getAssetTypes(); - $allowedAssetTypes = array_column($allowedAssetTypes, 'assetTypes'); - - if ( - !( - $fieldDefinition->getAssetsAllowed() - && ($allowedAssetTypes === [] || in_array($type, $allowedAssetTypes, true)) - ) - ) { - throw new ValidationException(sprintf('Invalid relation in field `%s` [type: %s]', $context['fieldname'], $type)); - } - } + return $this->elementService->getElementTreeNodeConfig($element); } } diff --git a/src/Controller/Admin/Asset/AssetCopyController.php b/src/Controller/Admin/Asset/AssetCopyController.php new file mode 100644 index 00000000..5eb9cd93 --- /dev/null +++ b/src/Controller/Admin/Asset/AssetCopyController.php @@ -0,0 +1,121 @@ +value)] +class AssetCopyController extends AdminAbstractController +{ + #[Route('/copy-info', name: 'opendxp_admin_asset_copyinfo', methods: ['GET'])] + public function copyInfoAction( + GetAssetChildIdsHandler $getChildIds, + Request $request, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] int $sourceId = 0, + #[MapQueryParameter] ?string $targetId = null, + ): JsonResponse { + $transactionId = time(); + $pasteJobs = []; + + Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { + $session->set((string) $transactionId, []); + }, 'opendxp_copy'); + + if ($type === 'recursive') { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_asset_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $sourceId, + 'targetId' => $targetId, + 'type' => 'child', + 'transactionId' => $transactionId, + 'saveParentId' => true, + ], + ]]; + + $childIds = $getChildIds($sourceId)->ids; + foreach ($childIds as $id) { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_asset_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $id, + 'targetParentId' => $targetId, + 'sourceParentId' => $sourceId, + 'type' => 'child', + 'transactionId' => $transactionId, + ], + ]]; + } + } elseif ($type === 'child' || $type === 'replace') { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_asset_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $sourceId, + 'targetId' => $targetId, + 'type' => $type, + 'transactionId' => $transactionId, + ], + ]]; + } + + return $this->adminJson(['pastejobs' => $pasteJobs]); + } + + #[Route('/copy', name: 'opendxp_admin_asset_copy', methods: ['POST'])] + public function copyAction(CopyAssetHandler $copyAsset, Request $request): JsonResponse + { + $sourceId = (int) $request->request->get('sourceId'); + $targetId = (int) $request->request->get('targetId'); + $type = (string) $request->request->get('type'); + + $session = Tool\Session::getSessionBag($request->getSession(), 'opendxp_copy'); + $sessionBag = $session->get($request->request->get('transactionId')); + + $sourceParentId = $request->request->has('targetParentId') ? (int) $request->request->get('sourceParentId') : null; + $targetParentId = $request->request->has('targetParentId') ? (int) $request->request->get('targetParentId') : null; + $sessionParentId = $sessionBag['parentId'] ? (int) $sessionBag['parentId'] : null; + + $result = $copyAsset($sourceId, $targetId, $type, $sourceParentId, $targetParentId, $sessionParentId); + + if ($result->newAsset !== null && $request->request->get('saveParentId')) { + $sessionBag['parentId'] = $result->newAsset->getId(); + $session->set($request->request->get('transactionId'), $sessionBag); + } + + return $this->adminJson(ApiResponse::ok()); + } +} diff --git a/src/Controller/Admin/Asset/AssetDownloadController.php b/src/Controller/Admin/Asset/AssetDownloadController.php new file mode 100644 index 00000000..7012b306 --- /dev/null +++ b/src/Controller/Admin/Asset/AssetDownloadController.php @@ -0,0 +1,161 @@ +value)] +class AssetDownloadController extends AdminAbstractController +{ + #[Route('/download', name: 'opendxp_admin_asset_download', methods: ['GET'])] + public function downloadAction(DownloadAssetHandler $downloadAsset, #[MapQueryParameter] int $id): StreamedResponse + { + $result = $downloadAsset($id); + $asset = $result->asset; + $stream = $asset->getStream(); + + if (!is_resource($stream)) { + throw $this->createNotFoundException('Unable to get resource for asset ' . $asset->getId()); + } + + return new StreamedResponse(static function () use ($stream): void { + fpassthru($stream); + }, 200, [ + 'Content-Type' => $asset->getMimeType(), + 'Content-Disposition' => sprintf('attachment; filename: "%s"', $asset->getFilename()), + 'Content-Length' => $asset->getFileSize(), + ]); + } + + #[Route('/download-image-thumbnail', name: 'opendxp_admin_asset_downloadimagethumbnail', methods: ['GET'])] + public function downloadImageThumbnailAction( + DownloadImageThumbnailHandler $downloadImageThumbnail, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $thumbnail = null, + #[MapQueryParameter] ?string $config = null, + #[MapQueryParameter] ?string $type = null, + ): BinaryFileResponse { + $configData = null; + if ($config !== null) { + $configData = $this->decodeJson($config); + } elseif ($type !== null) { + $predefined = [ + 'web' => ['resize_mode' => 'scaleByWidth', 'width' => 3500, 'dpi' => 72, 'format' => 'JPEG', 'quality' => 85], + 'print' => ['resize_mode' => 'scaleByWidth', 'width' => 6000, 'dpi' => 300, 'format' => 'JPEG', 'quality' => 95], + 'office' => ['resize_mode' => 'scaleByWidth', 'width' => 1190, 'dpi' => 144, 'format' => 'JPEG', 'quality' => 90], + ]; + $configData = $predefined[$type]; + } + + $result = $downloadImageThumbnail($id, $thumbnail, $config, $configData); + + $downloadFilename = preg_replace( + '/\.' . preg_quote(pathinfo($result->image->getFilename(), PATHINFO_EXTENSION), '/') . '$/i', + '.' . $result->thumbnail->getFileExtension(), + $result->image->getFilename() + ); + + clearstatcache(); + + $response = new BinaryFileResponse($result->localFile); + $response->headers->set('Content-Type', $result->thumbnail->getMimeType()); + $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $downloadFilename); + $this->addThumbnailCacheHeaders($response); + $response->deleteFileAfterSend($result->deleteThumbnail); + + return $response; + } + + #[Route('/download-as-zip-jobs', name: 'opendxp_admin_asset_downloadaszipjobs', methods: ['GET'])] + public function downloadAsZipJobsAction( + GetDownloadZipJobsHandler $getZipJobs, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] string $selectedIds = '', + ): JsonResponse { + $result = $getZipJobs($id, $selectedIds); + + return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs, 'jobId' => $result->jobId])); + } + + #[Route('/download-as-zip-add-files', name: 'opendxp_admin_asset_downloadaszipaddfiles', methods: ['GET'])] + public function downloadAsZipAddFilesAction( + AddFilesToZipHandler $addFilesToZip, + #[MapQueryParameter] ?string $jobId = null, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $selectedIds = null, + #[MapQueryParameter] int $offset = 0, + #[MapQueryParameter] int $limit = 0, + ): JsonResponse { + $addFilesToZip($id, $selectedIds, $offset, $limit, (string) $jobId); + + return $this->adminJson(ApiResponse::ok()); + } + + /** + * Download all assets contained in the folder with parameter id as ZIP file. + * The suggested filename is either [folder name].zip or assets.zip for the root folder. + */ + #[Route('/download-as-zip', name: 'opendxp_admin_asset_downloadaszip', methods: ['GET'])] + public function downloadAsZipAction( + DownloadZipHandler $downloadZip, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $jobId = null, + ): BinaryFileResponse { + $result = $downloadZip($id, (string) $jobId); + + $response = new BinaryFileResponse($result->zipFile); + $response->headers->set('Content-Type', 'application/zip'); + $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $result->suggestedFilename . '.zip'); + $response->deleteFileAfterSend(true); + + return $response; + } + + private function addThumbnailCacheHeaders(Response $response): void + { + $lifetime = 300; + $date = new DateTime('now'); + $date->add(new DateInterval('PT' . $lifetime . 'S')); + + $response->setMaxAge($lifetime); + $response->setPublic(); + $response->setExpires($date); + $response->headers->set('Pragma', ''); + } +} diff --git a/src/Controller/Admin/Asset/AssetEditorController.php b/src/Controller/Admin/Asset/AssetEditorController.php new file mode 100644 index 00000000..ba28c934 --- /dev/null +++ b/src/Controller/Admin/Asset/AssetEditorController.php @@ -0,0 +1,57 @@ +value)] +class AssetEditorController extends AdminAbstractController +{ + #[Route('/image-editor', name: 'opendxp_admin_asset_imageeditor', methods: ['GET'])] + public function imageEditorAction(LoadAssetForEditorHandler $loadForEditor, #[MapQueryParameter] int $id): Response + { + $result = $loadForEditor($id); + + return $this->render('@OpenDxpAdmin/admin/asset/image_editor.html.twig', ['asset' => $result->asset]); + } + + #[Route('/image-editor-save', name: 'opendxp_admin_asset_imageeditorsave', methods: ['PUT'])] + public function imageEditorSaveAction( + Request $request, + SaveImageEditorHandler $saveImageEditor, + #[MapQueryParameter] int $id, + ): JsonResponse { + $saveImageEditor($id, (string) $request->request->get('dataUri')); + + return $this->adminJson(ApiResponse::ok()); + } +} diff --git a/src/Controller/Admin/Asset/AssetHelperController.php b/src/Controller/Admin/Asset/AssetHelperController.php index 019f3beb..e5a5459a 100644 --- a/src/Controller/Admin/Asset/AssetHelperController.php +++ b/src/Controller/Admin/Asset/AssetHelperController.php @@ -16,41 +16,28 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\Asset; -use Doctrine\DBAL\ArrayParameterType; -use Doctrine\DBAL\ParameterType; -use Exception; -use League\Flysystem\FilesystemException; -use League\Flysystem\UnableToReadFile; -use OpenDxp; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Helper\GridHelperService; -use OpenDxp\Bundle\AdminBundle\Model\GridConfig; -use OpenDxp\Bundle\AdminBundle\Model\GridConfigFavourite; -use OpenDxp\Bundle\AdminBundle\Model\GridConfigShare; -use OpenDxp\Bundle\AdminBundle\Tool; -use OpenDxp\Db; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DeleteGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatchHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetMetadataForColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavouriteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\SaveGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Service\Grid\AssetGridColumnConfigResolver; +use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; use OpenDxp\File; -use OpenDxp\Loader\ImplementationLoader\Exception\UnsupportedException; -use OpenDxp\Logger; -use OpenDxp\Model\Asset; -use OpenDxp\Model\Element; -use OpenDxp\Model\Metadata; -use OpenDxp\Model\User; -use OpenDxp\Security\SecurityHelper; use OpenDxp\Tool\Session; -use OpenDxp\Tool\Storage; -use OpenDxp\Version; use stdClass; -use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\HeaderUtils; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @internal @@ -58,319 +45,54 @@ #[Route('/asset-helper')] class AssetHelperController extends AdminAbstractController { - public function __construct(protected EventDispatcherInterface $eventDispatcher) - { - } - - public function getMyOwnGridColumnConfigs(int $userId, string $classId, string $searchType): array - { - $db = Db::get(); - $configListingConditionParts = []; - $configListingConditionParts[] = 'ownerId = ' . $userId; - $configListingConditionParts[] = 'classId = ' . $db->quote($classId); - - if ($searchType) { - $configListingConditionParts[] = 'searchType = ' . $db->quote($searchType); - } - - $configCondition = implode(' AND ', $configListingConditionParts); - $configListing = new GridConfig\Listing(); - $configListing->setOrderKey('name'); - $configListing->setOrder('ASC'); - $configListing->setCondition($configCondition); - $configListing = $configListing->load(); - - $configData = []; - foreach ($configListing as $config) { - $configData[] = $config->getObjectVars(); - } - - return $configData; - } - - public function getSharedGridColumnConfigs(User $user, string $classId, ?string $searchType = null): array - { - $db = Db::get(); - - $configListing = []; - - $userIds = [$user->getId()]; - // collect all roles - $userIds = [...$userIds, ...$user->getRoles()]; - - $ids = $db->fetchFirstColumn( - 'SELECT DISTINCT c1.id FROM gridconfigs c1, gridconfig_shares s - WHERE (c1.searchType = ? AND c1.id = s.gridConfigId AND s.sharedWithUserId IN (?) AND c1.classId = ?) - UNION DISTINCT SELECT c2.id FROM gridconfigs c2 WHERE shareGlobally = 1 AND c2.classId = ? AND c2.ownerId != ?', - [$searchType, $userIds, $classId, $classId, $user->getId()], - [ParameterType::STRING, ArrayParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER] - ); - - if ($ids) { - $ids = implode(',', $ids); - $configListing = new GridConfig\Listing(); - $configListing->setOrderKey('name'); - $configListing->setOrder('ASC'); - $configListing->setCondition('id in (' . $ids . ')'); - $configListing = $configListing->load(); - } - - $configData = []; - foreach ($configListing as $config) { - $configData[] = $config->getObjectVars(); - } - - return $configData; + public function __construct( + private readonly AssetGridColumnConfigResolver $gridConfigResolver, + ) { } #[Route('/grid-delete-column-config', name: 'opendxp_admin_asset_assethelper_griddeletecolumnconfig', methods: ['DELETE'])] - public function gridDeleteColumnConfigAction(Request $request): JsonResponse - { + public function gridDeleteColumnConfigAction( + DeleteGridColumnConfigHandler $deleteGridColumnConfig, + Request $request, + #[MapQueryParameter(name: 'no_system_columns')] bool $noSystemColumns = false, + ): JsonResponse { $params = [ 'id' => $request->request->get('id'), 'type' => $request->request->get('type'), 'types' => $request->request->get('types'), 'gridConfigId' => $request->request->get('gridConfigId'), 'searchType' => $request->request->get('searchType'), - 'noSystemColumns' => $request->query->getBoolean('no_system_columns'), + 'noSystemColumns' => $noSystemColumns, ]; - $gridConfigId = (int) $request->request->get('gridConfigId'); - $gridConfig = GridConfig::getById($gridConfigId); - $success = false; - if ($gridConfig) { - if ($gridConfig->getOwnerId() !== $this->getAdminUser()->getId()) { - throw new Exception("don't mess with someone elses grid config"); - } - - $gridConfig->delete(); - $success = true; - } + $deleteGridColumnConfig( + (int) $request->request->get('gridConfigId'), + ); - $newGridConfig = $this->doGetGridColumnConfig($params, true); - $newGridConfig['deleteSuccess'] = $success; + $resolverResult = $this->gridConfigResolver->resolve($params, true); - return $this->adminJson($newGridConfig); + return $this->adminJson([...$resolverResult->jsonSerialize(), 'deleteSuccess' => true]); } #[Route('/grid-get-column-config', name: 'opendxp_admin_asset_assethelper_gridgetcolumnconfig', methods: ['GET'])] - public function gridGetColumnConfigAction(Request $request): JsonResponse - { + public function gridGetColumnConfigAction( + #[MapQueryParameter] ?string $id = null, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] ?string $types = null, + #[MapQueryParameter] ?string $gridConfigId = null, + #[MapQueryParameter] ?string $searchType = null, + #[MapQueryParameter(name: 'no_system_columns')] bool $noSystemColumns = false, + ): JsonResponse { $params = [ - 'id' => $request->query->get('id'), - 'type' => $request->query->get('type'), - 'types' => $request->query->get('types'), - 'gridConfigId' => $request->query->get('gridConfigId'), - 'searchType' => $request->query->get('searchType'), - 'noSystemColumns' => $request->query->getBoolean('no_system_columns'), + 'id' => $id, + 'type' => $type, + 'types' => $types, + 'gridConfigId' => $gridConfigId, + 'searchType' => $searchType, + 'noSystemColumns' => $noSystemColumns, ]; - $result = $this->doGetGridColumnConfig($params); - - return $this->adminJson($result); - } - - private function doGetGridColumnConfig(array $params, bool $isDelete = false): array - { - $gridConfigId = null; - - $classId = $params['id']; - $context = ['purpose' => 'gridconfig']; - - $types = []; - if (!empty($params['types'])) { - $types = explode(',', $params['types']); - } - - $userId = $this->getAdminUser()->getId(); - - $requestedGridConfigId = $isDelete ? '' : $params['gridConfigId'] ?? ''; - - // grid config - $gridConfig = []; - $searchType = $params['searchType']; - - if ((string) $requestedGridConfigId === '') { - // check if there is a favourite view - $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId($userId, $classId, 0, $searchType); - - if ($favourite) { - $requestedGridConfigId = $favourite->getGridConfigId(); - } - } - - if (is_numeric($requestedGridConfigId) && $requestedGridConfigId > 0) { - $db = Db::get(); - $savedGridConfig = GridConfig::getById((int) $requestedGridConfigId); - - if ($savedGridConfig) { - $shared = false; - if (!$this->getAdminUser()->isAdmin()) { - $userIds = [$this->getAdminUser()->getId()]; - $userIds = [...$userIds, ...$this->getAdminUser()->getRoles()]; - $isSharedGlobally = $savedGridConfig->getOwnerId() !== $userId && $savedGridConfig->isShareGlobally(); - - $isSharedWithUser = (bool) $db->fetchOne( - 'SELECT 1 FROM gridconfig_shares WHERE sharedWithUserId IN (?) AND gridConfigId = ?', - [$userIds, $savedGridConfig->getId()], - [ArrayParameterType::INTEGER, ParameterType::INTEGER] - ); - - $shared = $isSharedGlobally || $isSharedWithUser; - - if (!$shared && $savedGridConfig->getOwnerId() !== $this->getAdminUser()->getId()) { - throw new Exception('You are neither the owner of this config nor it is shared with you'); - } - } - - $gridConfigId = $savedGridConfig->getId(); - $gridConfig = $savedGridConfig->getConfig(); - $gridConfig = json_decode($gridConfig, true); - $gridConfigName = SecurityHelper::convertHtmlSpecialChars($savedGridConfig->getName()); - $gridConfigDescription = SecurityHelper::convertHtmlSpecialChars($savedGridConfig->getDescription()); - $sharedGlobally = $savedGridConfig->isShareGlobally(); - $setAsFavourite = $savedGridConfig->isSetAsFavourite(); - - foreach ($gridConfig['columns'] as &$column) { - if (array_key_exists('isOperator', $column) && $column['isOperator']) { - $colAttributes = &$column['fieldConfig']['attributes']; - SecurityHelper::convertHtmlSpecialCharsArrayKeys($colAttributes, ['label', 'attribute', 'param1']); - } - } - } - } - - $availableFields = []; - $language = ''; - - if (empty($gridConfig)) { - $availableFields = $this->getDefaultGridFields( - $params['noSystemColumns'], - [], //maybe required for types other than metadata - $context, - $types - ); - } else { - $savedColumns = $gridConfig['columns']; - - foreach ($savedColumns as $sc) { - if (!$sc['hidden']) { - $colConfig = $this->getFieldGridConfig($sc, $language); - if ($colConfig) { - $availableFields[] = $colConfig; - } - } - } - } - usort($availableFields, static fn ($a, $b) => $a['position'] <=> $b['position']); - - $availableConfigs = $classId ? $this->getMyOwnGridColumnConfigs($userId, $classId, $searchType) : []; - $sharedConfigs = $classId ? $this->getSharedGridColumnConfigs($this->getAdminUser(), $classId, $searchType) : []; - $settings = $this->getShareSettings((int)$gridConfigId); - $settings['gridConfigId'] = (int)$gridConfigId; - $settings['gridConfigName'] = $gridConfigName ?? null; - $settings['gridConfigDescription'] = $gridConfigDescription ?? null; - $settings['shareGlobally'] = $sharedGlobally ?? null; - $settings['setAsFavourite'] = $setAsFavourite ?? null; - $settings['isShared'] = !$gridConfigId || ($shared ?? null); - - $context = $gridConfig['context'] ?? null; - if ($context) { - $context = json_decode($context, true); - } - - return [ - 'sortinfo' => $gridConfig['sortinfo'] ?? false, - 'availableFields' => $availableFields, - 'settings' => $settings, - 'onlyDirectChildren' => $gridConfig['onlyDirectChildren'] ?? false, - 'onlyUnreferenced' => $gridConfig['onlyUnreferenced'] ?? false, - 'pageSize' => $gridConfig['pageSize'] ?? false, - 'availableConfigs' => $availableConfigs, - 'sharedConfigs' => $sharedConfigs, - 'context' => $context, - ]; - } - - protected function getFieldGridConfig(array $field, string $language = '', ?string $keyPrefix = null): ?array - { - $defaulMetadataFields = ['copyright', 'alt', 'title']; - $predefined = null; - - if (isset($field['fieldConfig']['layout']['name'])) { - $predefined = Metadata\Predefined::getByName($field['fieldConfig']['layout']['name']); - } - - $key = $field['name']; - if ($keyPrefix) { - $key = $keyPrefix . $key; - } - $fieldDef = explode('~', $field['name']); - $field['name'] = $fieldDef[0]; - - if (isset($fieldDef[1]) && $fieldDef[1] === 'system') { - $type = 'system'; - } elseif (in_array($fieldDef[0], $defaulMetadataFields)) { - $type = 'input'; - } else { - $type = $field['fieldConfig']['type']; - if (isset($fieldDef[1])) { - $field['fieldConfig']['label'] = $field['fieldConfig']['layout']['title'] = $fieldDef[0] . ' (' . $fieldDef[1] . ')'; - $field['fieldConfig']['layout']['icon'] = Tool::getLanguageFlagFile($fieldDef[1], true); - } - } - - $result = [ - 'key' => $key, - 'type' => $type, - 'label' => $field['fieldConfig']['label'] ?? $key, - 'width' => $field['width'], - 'position' => $field['position'], - 'language' => $field['fieldConfig']['language'] ?? null, - 'layout' => $field['fieldConfig']['layout'] ?? null, - ]; - - if (isset($field['locked'])) { - $result['locked'] = $field['locked']; - } - - if ($type === 'select' && $predefined) { - $field['fieldConfig']['layout']['config'] = $predefined->getConfig(); - $result['layout'] = $field['fieldConfig']['layout']; - } elseif (in_array($type, ['document', 'asset', 'object'], true)) { - $result['layout']['fieldtype'] = 'manyToOneRelation'; - $result['layout']['subtype'] = $type; - } - - $assetGetFieldGridConfig = new GenericEvent($this, [ - 'field' => $field, - 'result' => $result, - ]); - - $this->eventDispatcher->dispatch($assetGetFieldGridConfig, AdminEvents::ASSET_GET_FIELD_GRID_CONFIG); - - return $assetGetFieldGridConfig->getArgument('result'); - } - - public function getDefaultGridFields(bool $noSystemColumns, array $fields, array $context, array $types = []): array - { - $count = 0; - $availableFields = []; - - if (!$noSystemColumns) { - foreach (Asset\Service::GRID_SYSTEM_COLUMNS as $sc) { - if ($types === []) { - $availableFields[] = [ - 'key' => $sc . '~system', - 'type' => 'system', - 'label' => $sc, - 'position' => $count, ]; - $count++; - } - } - } - - return $availableFields; + return $this->adminJson($this->gridConfigResolver->resolve($params)); } #[Route('/prepare-helper-column-configs', name: 'opendxp_admin_asset_assethelper_preparehelpercolumnconfigs', methods: ['POST'])] @@ -399,680 +121,131 @@ public function prepareHelperColumnConfigs(Request $request): JsonResponse $session->set('helpercolumns', $helperColumns); }, 'opendxp_gridconfig'); - return $this->adminJson(['success' => true, 'columns' => $newData]); + return $this->adminJson(ApiResponse::ok(['columns' => $newData])); } #[Route('/grid-mark-favourite-column-config', name: 'opendxp_admin_asset_assethelper_gridmarkfavouritecolumnconfig', methods: ['POST'])] - public function gridMarkFavouriteColumnConfigAction(Request $request): JsonResponse - { - $classId = $request->request->get('classId'); - $asset = Asset::getById(is_numeric($classId) ? (int) $classId : 0); - - if ($asset->isAllowed('list')) { - $gridConfigId = (int) $request->request->get('gridConfigId'); - $searchType = $request->request->get('searchType'); - $type = $request->request->get('type'); - $user = $this->getAdminUser(); - - $favourite = new GridConfigFavourite(); - $favourite->setOwnerId($user->getId()); - $favourite->setClassId($classId); - $favourite->setSearchType($searchType); - $favourite->setType($type); - - try { - if ($gridConfigId !== 0) { - $gridConfig = GridConfig::getById($gridConfigId); - $favourite->setGridConfigId($gridConfig->getId()); - } - - $favourite->setObjectId(0); - $favourite->save(); - } catch (Exception) { - $favourite->delete(); - } - - return $this->adminJson(['success' => true, 'specializedConfigs' => false]); - } - - throw $this->createAccessDeniedHttpException(); - } - - protected function getShareSettings(int $gridConfigId): array - { - $result = [ - 'sharedUserIds' => [], - 'sharedRoleIds' => [], - ]; - - $db = Db::get(); - $allShares = $db->fetchAllAssociative( - 'SELECT s.sharedWithUserId, u.type FROM gridconfig_shares s, users u - WHERE s.sharedWithUserId = u.id AND s.gridConfigId = ?', - [$gridConfigId] - ); - - foreach ($allShares as $share) { - $type = $share['type']; - $key = 'shared' . ucfirst($type) . 'Ids'; - $result[$key][] = $share['sharedWithUserId']; - } - - foreach ($result as $idx => $value) { - $value = $value ? implode(',', $value) : ''; - $result[$idx] = $value; - } + public function gridMarkFavouriteColumnConfigAction( + MarkGridConfigFavouriteHandler $markFavourite, + Request $request, + ): JsonResponse { + $result = $markFavourite( + $request->request->get('classId'), + (int) $request->request->get('gridConfigId'), + $request->request->get('searchType'), + $request->request->get('type'), + ); - return $result; + return $this->adminJson(ApiResponse::ok(['specializedConfigs' => $result->specializedConfigs])); } #[Route('/grid-save-column-config', name: 'opendxp_admin_asset_assethelper_gridsavecolumnconfig', methods: ['POST'])] - public function gridSaveColumnConfigAction(Request $request): JsonResponse - { - $asset = Asset::getById((int) $request->request->get('id')); - - if (!$asset) { - throw $this->createNotFoundException(); - } - - if ($asset->isAllowed('list')) { - try { - $classId = $request->request->get('class_id'); - $context = $request->request->get('context'); - - $searchType = $request->request->get('searchType'); - $type = $request->request->get('type'); - - // grid config - $gridConfigData = $this->decodeJson($request->request->get('gridconfig')); - $gridConfigData['opendxp_version'] = Version::getVersion(); - $gridConfigData['opendxp_revision'] = Version::getRevision(); - $gridConfigData['context'] = $context; - unset($gridConfigData['settings']['isShared']); - - $metadata = $request->request->get('settings'); - $metadata = json_decode($metadata, true); - - $gridConfigId = $metadata['gridConfigId']; - $gridConfig = null; - if ($gridConfigId) { - $gridConfig = GridConfig::getById($gridConfigId); - } - - if ($gridConfig && $gridConfig->getOwnerId() !== $this->getAdminUser()->getId()) { - throw new Exception("don't mess around with somebody else's configuration"); - } - - $this->updateGridConfigShares($gridConfig, $metadata); - - if ($metadata['setAsFavourite'] && $this->getAdminUser()->isAdmin()) { - $this->updateGridConfigFavourites($gridConfig, $metadata); - } - - if (!$gridConfig) { - $gridConfig = new GridConfig(); - $gridConfig->setName(date('c')); - $gridConfig->setClassId($classId); - $gridConfig->setSearchType($searchType); - $gridConfig->setType($type); - - $gridConfig->setOwnerId($this->getAdminUser()->getId()); - } - - if ($metadata) { - $gridConfig->setName($metadata['gridConfigName']); - $gridConfig->setDescription($metadata['gridConfigDescription']); - $gridConfig->setShareGlobally($metadata['shareGlobally'] && $this->getAdminUser()->isAdmin()); - $gridConfig->setSetAsFavourite($metadata['setAsFavourite'] && $this->getAdminUser()->isAdmin()); - } - - $gridConfigData = json_encode($gridConfigData); - $gridConfig->setConfig($gridConfigData); - $gridConfig->save(); - - $userId = $this->getAdminUser()->getId(); - - $availableConfigs = $this->getMyOwnGridColumnConfigs($userId, $classId, $searchType); - $sharedConfigs = $this->getSharedGridColumnConfigs($this->getAdminUser(), $classId, $searchType); - - $settings = $this->getShareSettings($gridConfig->getId()); - $settings['gridConfigId'] = (int)$gridConfig->getId(); - $settings['gridConfigName'] = $gridConfig->getName(); - $settings['gridConfigDescription'] = $gridConfig->getDescription(); - $settings['shareGlobally'] = $gridConfig->isShareGlobally(); - $settings['setAsFavourite'] = $gridConfig->isSetAsFavourite(); - $settings['isShared'] = $gridConfig->getOwnerId() !== $this->getAdminUser()->getId(); - - return $this->adminJson([ - 'success' => true, - 'settings' => $settings, - 'availableConfigs' => $availableConfigs, - 'sharedConfigs' => $sharedConfigs, - ]); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - throw $this->createAccessDeniedHttpException(); - } - - /** - * @throws Exception - */ - protected function updateGridConfigShares(?GridConfig $gridConfig, array $metadata): void - { - $user = $this->getAdminUser(); - if (!$gridConfig || !$user->isAllowed('share_configurations')) { - // nothing to do - return; - } - - if ($gridConfig->getOwnerId() !== $this->getAdminUser()->getId()) { - throw new Exception("don't mess with someone elses grid config"); - } - $combinedShares = []; - $sharedUserIds = $metadata['sharedUserIds']; - $sharedRoleIds = $metadata['sharedRoleIds']; - - if ($sharedUserIds) { - $combinedShares = explode(',', $sharedUserIds); - } - - if ($sharedRoleIds) { - $sharedRoleIds = explode(',', $sharedRoleIds); - $combinedShares = [...$combinedShares, ...$sharedRoleIds]; - } - - $db = Db::get(); - $db->delete('gridconfig_shares', ['gridConfigId' => $gridConfig->getId()]); - - foreach ($combinedShares as $id) { - $share = new GridConfigShare(); - $share->setGridConfigId($gridConfig->getId()); - $share->setSharedWithUserId((int) $id); - $share->save(); - } - } - - /** - * @throws Exception - */ - protected function updateGridConfigFavourites(?GridConfig $gridConfig, array $metadata): void - { - $currentUser = $this->getAdminUser(); - - if (!$gridConfig || $currentUser === null || !$currentUser->isAllowed('share_configurations')) { - // nothing to do - return; - } - - if (!$currentUser->isAdmin() && (int) $gridConfig->getOwnerId() !== $currentUser->getId()) { - throw new Exception("don't mess with someone elses grid config"); - } - - $sharedUsers = []; - - if ($metadata['shareGlobally'] === false) { - $sharedUserIds = $metadata['sharedUserIds']; - - if ($sharedUserIds) { - $sharedUsers = array_map(intval(...), explode(',', $sharedUserIds)); - } - } - - if ($metadata['shareGlobally'] === true) { - $users = new User\Listing(); - $users->setCondition('id = ?', $currentUser->getId()); - - foreach ($users as $user) { - $sharedUsers[] = $user->getId(); - } - } - - foreach ($sharedUsers as $id) { - // Check if the user has already a favourite - $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId( - $id, - $gridConfig->getClassId(), - 0, - $gridConfig->getSearchType() + public function gridSaveColumnConfigAction( + SaveGridColumnConfigHandler $saveGridColumnConfig, + Request $request, + ): JsonResponse { + $gridConfigData = $this->decodeJson($request->request->get('gridconfig')); + $metadata = json_decode($request->request->get('settings'), true); + + $result = $saveGridColumnConfig( + (int) $request->request->get('id'), + $request->request->get('class_id'), + $request->request->get('context'), + $request->request->get('searchType'), + $request->request->get('type'), + $gridConfigData, + $metadata, ); - if ($favourite instanceof GridConfigFavourite) { - $favouriteGridConfig = GridConfig::getById($favourite->getGridConfigId()); - - if ($favouriteGridConfig instanceof GridConfig) { - // Check if the grid config was shared globally if that is *not* the case we also not update - if ($favouriteGridConfig->isShareGlobally() === false) { - continue; - } - - // Check if the user is the owner. If that is the case we do not update the favourite - if ($favouriteGridConfig->getOwnerId() === $id) { - continue; - } - } - } - - $favourite = new GridConfigFavourite(); - $favourite->setGridConfigId($gridConfig->getId()); - $favourite->setClassId($gridConfig->getClassId()); - $favourite->setObjectId(0); - $favourite->setOwnerId($id); - $favourite->setType($gridConfig->getType()); - $favourite->setSearchType($gridConfig->getSearchType()); - $favourite->save(); - } + return $this->adminJson(ApiResponse::ok([ + 'settings' => $result->settings, + 'availableConfigs' => $result->availableConfigs, + 'sharedConfigs' => $result->sharedConfigs, + ])); } #[Route('/get-export-jobs', name: 'opendxp_admin_asset_assethelper_getexportjobs', methods: ['POST'])] - public function getExportJobsAction(Request $request, GridHelperService $gridHelperService): JsonResponse + public function getExportJobsAction(GetExportJobsHandler $getExportJobs, Request $request): JsonResponse { $allParams = [...$request->request->all(), ...$request->query->all()]; - $list = $gridHelperService->prepareAssetListingForGrid($allParams, $this->getAdminUser()); - - if (empty($ids = $allParams['ids'] ?? '')) { - $ids = $list->loadIdList(); - } + $result = $getExportJobs($allParams); - $jobs = array_chunk($ids, 20); - - $fileHandle = uniqid('asset-export-', false); - $storage = Storage::get('temp'); - $storage->write($this->getCsvFile($fileHandle), ''); - - return $this->adminJson(['success' => true, 'jobs' => $jobs, 'fileHandle' => $fileHandle]); + return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs, 'fileHandle' => $result->fileHandle])); } - /** - * @throws FilesystemException - */ #[Route('/do-export', name: 'opendxp_admin_asset_assethelper_doexport', methods: ['POST'])] - public function doExportAction(Request $request): JsonResponse + public function doExportAction(DoAssetExportHandler $doExport, Request $request): JsonResponse { $fileHandle = File::getValidFilename($request->request->get('fileHandle')); - $ids = $request->request->all('ids'); $settings = json_decode($request->request->get('settings'), true); - $delimiter = $settings['delimiter'] ?? ';'; - $language = str_replace('default', '', $request->request->get('language')); - $header = $settings['header'] ?? 'title'; - - $list = new Asset\Listing(); - - $quotedIds = []; - foreach ($ids as $id) { - $quotedIds[] = $list->quote($id); - } - - $list->setCondition('id IN (' . implode(',', $quotedIds) . ')'); - $list->setOrderKey(' FIELD(id, ' . implode(',', $quotedIds) . ')', false); - $fields = json_decode($request->request->all('fields')[0], true); - $addTitles = (bool) $request->request->get('initial'); - - $csv = $this->getCsvData($language, $list, $fields, $header, $addTitles); - - $temp = tmpfile(); - - try { - $storage = Storage::get('temp'); - $csvFile = $this->getCsvFile($fileHandle); - - $fileStream = $storage->readStream($csvFile); - - stream_copy_to_stream($fileStream, $temp, null, 0); - - $firstLine = true; - if ($request->request->get('initial') && $header === 'no_header') { - $firstLine = false; - } - - foreach ($csv as $line) { - if ($addTitles && $firstLine) { - $firstLine = false; - $line = implode($delimiter, $line) . "\r\n"; - fwrite($temp, $line); - } else { - fwrite($temp, implode($delimiter, array_map($this->encodeFunc(...), $line)) . "\r\n"); - } - } - $storage->writeStream($csvFile, $temp); - } catch (UnableToReadFile $exception) { - Logger::err($exception->getMessage()); - - return $this->adminJson( - [ - 'success' => false, - 'message' => sprintf('export file not found: %s', $fileHandle), - ] - ); - } finally { - if (is_resource($temp)) { - fclose($temp); - } - } - - return $this->adminJson(['success' => true]); - } - - public function encodeFunc(null|string|array $value): string - { - if (is_array($value)) { - $value = implode(',', $value); - } - $value = str_replace('"', '""', $value ?? ''); - - //force wrap value in quotes and return - return '"' . $value . '"'; - } - - protected function getCsvData( - string $language, - Asset\Listing $list, - array $fields, - string $header, - bool $addTitles = true - ): array { - //create csv - $csv = []; - - $unsupportedFields = [0 => 'preview~system', 1 => 'size~system']; - $fields = array_filter($fields, fn ($field) => !in_array($field['key'], $unsupportedFields)); - - if ($addTitles && $header !== 'no_header') { - $columns = $fields; - $titleIdx = $header === 'name' ? 'key' : 'label'; - foreach ($columns as $columnIdx => $columnKeys) { - $columns[$columnIdx] = '"' . $columnKeys[$titleIdx] . '"'; - } - $csv[] = $columns; - } - - foreach ($list->load() as $asset) { - if ($fields) { - $dataRows = []; - foreach ($fields as $field) { - $fieldDef = explode('~', $field['key']); - $getter = 'get' . ucfirst($fieldDef[0]); - - if (isset($fieldDef[1])) { - if ($fieldDef[1] === 'system' && method_exists($asset, $getter)) { - $data = $asset->$getter($language); - } else { - $fieldDef[1] = str_replace('none', '', $fieldDef[1]); - $data = $asset->getMetadata($fieldDef[0], $fieldDef[1], true); - } - } else { - $data = $asset->getMetadata($field['key'], $language, true); - } - - if ($data instanceof Element\ElementInterface) { - $data = $data->getRealFullPath(); - } - $dataRows[] = $data; - } - $dataRows = Element\Service::escapeCsvRecord($dataRows); - $csv[] = $dataRows; - } - } - - return $csv; - } + $doExport( + $fileHandle, + $request->request->all('ids'), + $settings['delimiter'] ?? ';', + str_replace('default', '', $request->request->get('language')), + $settings['header'] ?? 'title', + $fields, + (bool) $request->request->get('initial'), + ); - protected function getCsvFile(string $fileHandle): string - { - return $fileHandle . '.csv'; + return $this->adminJson(ApiResponse::ok()); } #[Route('/download-csv-file', name: 'opendxp_admin_asset_assethelper_downloadcsvfile', methods: ['GET'])] - public function downloadCsvFileAction(Request $request): Response - { - $storage = Storage::get('temp'); - $fileHandle = File::getValidFilename($request->query->get('fileHandle')); - $csvFile = $this->getCsvFile($fileHandle); - + public function downloadCsvFileAction( + GridExportService $gridExportService, + #[MapQueryParameter] ?string $fileHandle = null, + ): Response { try { - $csvData = $storage->read($csvFile); - $response = new Response($csvData); - $response->headers->set('Content-Type', 'application/csv'); - $disposition = HeaderUtils::makeDisposition( - HeaderUtils::DISPOSITION_ATTACHMENT, - 'export.csv' - ); - - $response->headers->set('Content-Disposition', $disposition); - $storage->delete($csvFile); - - return $response; - } catch (FilesystemException | UnableToReadFile) { - // handle the error + return $gridExportService->downloadCsvFile($fileHandle); + } catch (\RuntimeException) { throw $this->createNotFoundException('CSV file not found'); } } #[Route('/download-xlsx-file', name: 'opendxp_admin_asset_assethelper_downloadxlsxfile', methods: ['GET'])] - public function downloadXlsxFileAction(Request $request, GridHelperService $gridHelperService): BinaryFileResponse - { - $storage = Storage::get('temp'); - $fileHandle = File::getValidFilename($request->query->get('fileHandle')); - $csvFile = $this->getCsvFile($fileHandle); - + public function downloadXlsxFileAction( + GridExportService $gridExportService, + #[MapQueryParameter] ?string $fileHandle = null, + ): BinaryFileResponse { try { - return $gridHelperService->createXlsxExportFile($storage, $fileHandle, $csvFile); - } catch (Exception | FilesystemException | UnableToReadFile) { - // handle the error + return $gridExportService->downloadXlsxFile($fileHandle); + } catch (\RuntimeException) { throw $this->createNotFoundException('XLSX file not found'); } } #[Route('/get-metadata-for-column-config', name: 'opendxp_admin_asset_assethelper_getmetadataforcolumnconfig', methods: ['GET'])] - public function getMetadataForColumnConfigAction(Request $request): JsonResponse + public function getMetadataForColumnConfigAction(GetAssetMetadataForColumnConfigHandler $getMetadata): JsonResponse { - $result = []; - - //default metadata - $defaultMetadataNames = ['copyright', 'alt', 'title']; - foreach ($defaultMetadataNames as $defaultMetadata) { - $defaultColumns[] = ['title' => $defaultMetadata, 'name' => $defaultMetadata, 'datatype' => 'data', 'fieldtype' => 'input']; - } - $result['defaultColumns']['nodeLabel'] = 'default_metadata'; - $result['defaultColumns']['nodeType'] = 'image'; - $result['defaultColumns']['children'] = $defaultColumns; - - //predefined metadata - $list = Metadata\Predefined\Listing::getByTargetType('asset'); - $metadataItems = []; - $tmp = []; - foreach ($list as $item) { - //only allow unique metadata columns with subtypes - $uniqueKey = $item->getName().'_'.$item->getTargetSubtype(); - if (!in_array($uniqueKey, $tmp) && !in_array($item->getName(), $defaultMetadataNames)) { - $tmp[] = $uniqueKey; - $item->expand(); - $name = SecurityHelper::convertHtmlSpecialChars($item->getName()); - $metadataItems[] = [ - 'title' => $name, - 'name' => $name, - 'subtype' => $item->getTargetSubtype(), - 'datatype' => 'data', - 'fieldtype' => $item->getType(), - 'config' => $item->getConfig(), - ]; - } - } - - $result['metadataColumns']['children'] = $metadataItems; - $result['metadataColumns']['nodeLabel'] = 'predefined_metadata'; - $result['metadataColumns']['nodeType'] = 'metadata'; + $result = $getMetadata(); - //system columns - $systemColumnNames = Asset\Service::GRID_SYSTEM_COLUMNS; - $systemColumns = []; - foreach ($systemColumnNames as $systemColumn) { - $systemColumns[] = ['title' => $systemColumn, 'name' => $systemColumn, 'datatype' => 'data', 'fieldtype' => 'system']; - } - $result['systemColumns']['nodeLabel'] = 'system_columns'; - $result['systemColumns']['nodeType'] = 'system'; - $result['systemColumns']['children'] = $systemColumns; - - return $this->adminJson($result); + return $this->adminJson($result->data); } #[Route('/get-batch-jobs', name: 'opendxp_admin_asset_assethelper_getbatchjobs', methods: ['POST'])] - public function getBatchJobsAction(Request $request, GridHelperService $gridHelperService): JsonResponse + public function getBatchJobsAction(GetAssetBatchJobsHandler $getAssetBatchJobs, Request $request): JsonResponse { if ($request->request->get('language')) { $request->setLocale($request->request->get('language')); } $allParams = [...$request->request->all(), ...$request->query->all()]; - $list = $gridHelperService->prepareAssetListingForGrid($allParams, $this->getAdminUser()); + $result = $getAssetBatchJobs($allParams); - $jobs = $list->loadIdList(); - - return $this->adminJson(['success' => true, 'jobs' => $jobs]); + return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs])); } #[Route('/batch', name: 'opendxp_admin_asset_assethelper_batch', methods: ['PUT'])] - public function batchAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse + public function batchAction(ExecuteAssetBatchHandler $executeAssetBatch, Request $request): JsonResponse { - try { - if ($request->request->has('data')) { - $loader = OpenDxp::getContainer()->get('opendxp.implementation_loader.asset.metadata.data'); - - $data = $this->decodeJson($request->request->get('data'), true); - - $updateEvent = new GenericEvent($this, [ - 'data' => $data, - 'processed' => false, - ]); - - $eventDispatcher->dispatch($updateEvent, AdminEvents::ASSET_LIST_BEFORE_BATCH_UPDATE); - - $processed = $updateEvent->getArgument('processed'); - - if ($processed) { - return $this->adminJson(['success' => true]); - } - - $language = null; - if (isset($data['language'])) { - $language = $data['language'] !== 'default' ? $data['language'] : null; - } - - $asset = Asset::getById((int) $data['job']); - - if ($asset) { - if (!$asset->isAllowed('publish')) { - throw new Exception("Permission denied. You don't have the rights to save this asset."); - } - - $metadata = $asset->getMetadata(null, null, false, true); - $dirty = false; - - $name = $data['name']; - $value = $data['value']; - - if ($data['valueType'] === 'object') { - $value = $this->decodeJson($value); - } - - $fieldDef = explode('~', $name); - $name = $fieldDef[0]; - if (count($fieldDef) > 1) { - $language = ($fieldDef[1] === 'none' ? '' : $fieldDef[1]); - } - - foreach ($metadata as &$em) { - if ($em['name'] == $name && $em['language'] == $language) { - try { - $dataImpl = $loader->build($em['type']); - $value = $dataImpl->getDataFromListfolderGrid($value, $em); - } catch (UnsupportedException) { - Logger::error('could not resolve metadata implementation for ' . $em['type']); - } - $em['data'] = $value; - $dirty = true; - - break; - } - } - - if (!$dirty) { - $defaulMetadata = ['title', 'alt', 'copyright']; - if (in_array($name, $defaulMetadata)) { - $newEm = [ - 'name' => $name, - 'language' => $language, - 'type' => 'input', - 'data' => $value, - ]; - - try { - $dataImpl = $loader->build($newEm['type']); - $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm); - } catch (UnsupportedException) { - Logger::error('could not resolve metadata implementation for ' . $newEm['type']); - } - - $metadata[] = $newEm; - $dirty = true; - } else { - $predefined = Metadata\Predefined::getByName($name); - if ($predefined && (empty($predefined->getTargetSubtype()) - || $predefined->getTargetSubtype() === $asset->getType())) { - $newEm = [ - 'name' => $name, - 'language' => $language, - 'type' => $predefined->getType(), - 'data' => $value, - ]; - - try { - $dataImpl = $loader->build($newEm['type']); - $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm); - } catch (UnsupportedException) { - Logger::error('could not resolve metadata implementation for ' . $newEm['type']); - } - - $metadata[] = $newEm; - - $dirty = true; - } - } - } - - try { - if ($dirty) { - $metadataEvent = new GenericEvent($this, [ - 'id' => $asset->getId(), - 'metadata' => $metadata, - ]); - - $eventDispatcher->dispatch($metadataEvent, AdminEvents::ASSET_METADATA_PRE_SET); - - $asset->setMetadataRaw($metadata); - $asset->save(); - - return $this->adminJson(['success' => true]); - } - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } else { - Logger::debug('AssetHelperController::batchAction => There is no asset left to update.'); - - return $this->adminJson(['success' => false, 'message' => 'AssetHelperController::batchAction => There is no asset left to update.']); - } - } - } catch (Exception $e) { - Logger::err((string)$e); - - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); + if ($request->request->has('data')) { + $data = $this->decodeJson($request->request->get('data'), true); + $executeAssetBatch($data); } - return $this->adminJson(['success' => false, 'message' => 'something went wrong.']); + return $this->adminJson(ApiResponse::ok()); } } diff --git a/src/Controller/Admin/Asset/AssetMediaController.php b/src/Controller/Admin/Asset/AssetMediaController.php new file mode 100644 index 00000000..6cb98232 --- /dev/null +++ b/src/Controller/Admin/Asset/AssetMediaController.php @@ -0,0 +1,158 @@ +value)] +class AssetMediaController extends AdminAbstractController +{ + #[Route('/get-asset', name: 'opendxp_admin_asset_getasset', methods: ['GET'])] + public function getAssetAction(DownloadAssetHandler $downloadAsset, #[MapQueryParameter] int $id): StreamedResponse + { + $result = $downloadAsset($id); + $asset = $result->asset; + $stream = $asset->getStream(); + + if (!is_resource($stream)) { + throw $this->createNotFoundException('Unable to get resource for asset ' . $asset->getId()); + } + + $response = new StreamedResponse(static function () use ($stream): void { + fpassthru($stream); + }, 200, [ + 'Content-Type' => $asset->getMimeType(), + 'Access-Control-Allow-Origin' => '*', + ]); + $this->addThumbnailCacheHeaders($response); + + return $response; + } + + #[Route('/get-preview-document', name: 'opendxp_admin_asset_getpreviewdocument', methods: ['GET'])] + public function getPreviewDocumentAction(GetDocumentPreviewHandler $getDocumentPreview, #[MapQueryParameter] int $id): StreamedResponse|Response + { + $result = $getDocumentPreview($id); + $asset = $result->asset; + + if ($result->thumbnailPath !== null) { + return $this->render('@OpenDxpAdmin/admin/asset/get_preview_pdf_open_in_new_tab.html.twig', [ + 'thumbnailPath' => $result->thumbnailPath, + 'assetPath' => $result->assetPath, + ]); + } + + if ($result->scanStatus === PdfScanStatus::IN_PROGRESS) { + return $this->render('@OpenDxpAdmin/admin/asset/get_preview_pdf_in_progress.html.twig'); + } + + if ($result->scanStatus === PdfScanStatus::UNSAFE) { + return $this->render('@OpenDxpAdmin/admin/asset/get_preview_pdf_unsafe.html.twig'); + } + + if ($result->stream) { + return new StreamedResponse(static function () use ($result): void { + fpassthru($result->stream); + }, 200, [ + 'Content-Type' => 'application/pdf', + ]); + } + + throw $this->createNotFoundException('Unable to get preview for asset ' . $asset->getId()); + } + + #[Route('/get-preview-video', name: 'opendxp_admin_asset_getpreviewvideo', methods: ['GET'])] + public function getPreviewVideoAction( + GetVideoPreviewHandler $getVideoPreview, + #[MapQueryParameter] int $id, + #[MapQueryParameter] ?string $config = null, + ): Response { + $result = $getVideoPreview($id, $config); + $previewData = [ + 'asset' => $result->asset, + 'thumbnail' => $result->thumbnail, + 'config' => $result->configName, + ]; + + if ($result->thumbnail && $result->isFinished) { + return $this->render('@OpenDxpAdmin/admin/asset/get_preview_video_display.html.twig', $previewData); + } + + return $this->render('@OpenDxpAdmin/admin/asset/get_preview_video_error.html.twig', $previewData); + } + + #[Route('/serve-video-preview', name: 'opendxp_admin_asset_servevideopreview', methods: ['GET'])] + public function serveVideoPreviewAction( + ServeVideoPreviewHandler $serveVideoPreview, + #[MapQueryParameter] int $id, + #[MapQueryParameter] ?string $config = null, + ): StreamedResponse { + $result = $serveVideoPreview($id, $config); + + return new StreamedResponse(static function () use ($result): void { + fpassthru($result->stream); + }, 200, [ + 'Content-Type' => 'video/mp4', + 'Content-Length' => $result->fileSize, + 'Accept-Ranges' => 'bytes', + ]); + } + + #[Route('/get-text', name: 'opendxp_admin_asset_gettext', methods: ['GET'])] + public function getTextAction( + GetAssetTextHandler $getAssetText, + #[MapQueryParameter] int $id, + #[MapQueryParameter] ?int $page = null, + ): JsonResponse { + $result = $getAssetText($id, $page); + + return $this->adminJson(ApiResponse::ok(['text' => $result->text])); + } + + private function addThumbnailCacheHeaders(Response $response): void + { + $lifetime = 300; + $date = new DateTime('now'); + $date->add(new DateInterval('PT' . $lifetime . 'S')); + + $response->setMaxAge($lifetime); + $response->setPublic(); + $response->setExpires($date); + $response->headers->set('Pragma', ''); + } +} diff --git a/src/Controller/Admin/Asset/AssetThumbnailController.php b/src/Controller/Admin/Asset/AssetThumbnailController.php new file mode 100644 index 00000000..5a6af57d --- /dev/null +++ b/src/Controller/Admin/Asset/AssetThumbnailController.php @@ -0,0 +1,210 @@ +decodeJson($config) : null; + $hasCropPercent = $cropPercent !== null && filter_var($cropPercent, FILTER_VALIDATE_BOOLEAN); + + $result = $getImageThumbnail( + $id, + $fileinfo !== null, + $thumbnail, + $configDecoded, + $request->query->all(), + $treepreview !== null, + $origin, + $hasCropPercent, + $cropWidth, + $cropHeight, + $cropTop, + $cropLeft, + ); + + if ($result->returnLoadingGif) { + $response = new BinaryFileResponse(OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/video-loading.gif'); + $response->headers->set('Cache-Control', 'no-store'); + + return $response; + } + + if ($result->thumbnailResult === null) { + throw $this->createNotFoundException(sprintf('Tree preview thumbnail not available for asset %s', $id)); + } + + if ($result->returnFileinfo) { + return $this->adminJson([ + 'width' => $result->thumbnailResult->getWidth(), + 'height' => $result->thumbnailResult->getHeight(), + ]); + } + + $stream = $result->thumbnailResult->getStream(); + if (!$stream) { + return new BinaryFileResponse(OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/filetype-not-supported.svg'); + } + + $response = new StreamedResponse(static function () use ($stream): void { + fpassthru($stream); + }, 200, [ + 'Content-Type' => $result->thumbnailResult->getMimeType(), + 'Access-Control-Allow-Origin' => '*', + ]); + $this->addThumbnailCacheHeaders($response); + + return $response; + } + + #[Route('/get-folder-thumbnail', name: 'opendxp_admin_asset_getfolderthumbnail', methods: ['GET'])] + #[IsGranted(CorePermission::Assets->value)] + public function getFolderThumbnailAction(GetFolderThumbnailHandler $getFolderThumbnail, #[MapQueryParameter] ?int $id = null): StreamedResponse + { + $result = $getFolderThumbnail($id); + + $response = new StreamedResponse(static function () use ($result): void { + fpassthru($result->stream); + }, 200, [ + 'Content-Type' => 'image/jpg', + ]); + $this->addThumbnailCacheHeaders($response); + + return $response; + } + + #[Route('/get-video-thumbnail', name: 'opendxp_admin_asset_getvideothumbnail', methods: ['GET'])] + public function getVideoThumbnailAction( + GetVideoThumbnailHandler $getVideoThumbnail, + Request $request, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $path = null, + #[MapQueryParameter] ?string $time = null, + #[MapQueryParameter] int $image = 0, + #[MapQueryParameter] ?string $origin = null, + ): StreamedResponse { + $result = $getVideoThumbnail( + $request->query->has('id') ? $id : null, + $path, + $request->query->has('treepreview'), + $request->query->has('settime'), + $request->query->has('setimage'), + $request->query->has('image'), + $image, + $time, + $origin, + $request->query->all(), + ); + + $response = new StreamedResponse(static function () use ($result): void { + fpassthru($result->stream); + }, 200, [ + 'Content-Type' => 'image/' . $result->fileExtension, + ]); + $this->addThumbnailCacheHeaders($response); + + return $response; + } + + #[Route('/get-document-thumbnail', name: 'opendxp_admin_asset_getdocumentthumbnail', methods: ['GET'])] + public function getDocumentThumbnailAction( + GetDocumentThumbnailHandler $getDocumentThumbnail, + Request $request, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $treepreview = null, + #[MapQueryParameter] ?int $page = null, + #[MapQueryParameter] ?string $origin = null, + ): BinaryFileResponse|StreamedResponse { + $result = $getDocumentThumbnail($id, $treepreview !== null, $page, $origin, $request->query->all()); + + if ($result->stream) { + $response = new StreamedResponse(static function () use ($result): void { + fpassthru($result->stream); + }, 200, [ + 'Content-Type' => 'image/' . $result->fileExtension, + ]); + } else { + $response = new BinaryFileResponse(OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/filetype-not-supported.svg'); + } + + $this->addThumbnailCacheHeaders($response); + + return $response; + } + + #[Route('/get-folder-content-preview', name: 'opendxp_admin_asset_getfoldercontentpreview', methods: ['GET'])] + #[IsGranted(CorePermission::Assets->value)] + public function getFolderContentPreviewAction( + GetFolderContentPreviewHandler $getFolderContentPreview, + Request $request, + ): JsonResponse { + $result = $getFolderContentPreview($request->query->all()); + + return $this->adminJson(ApiResponse::ok(['assets' => $result->assets, 'total' => $result->total])); + } + + private function addThumbnailCacheHeaders(Response $response): void + { + $lifetime = 300; + $date = new DateTime('now'); + $date->add(new DateInterval('PT' . $lifetime . 'S')); + + $response->setMaxAge($lifetime); + $response->setPublic(); + $response->setExpires($date); + $response->headers->set('Pragma', ''); + } +} diff --git a/src/Controller/Admin/Asset/AssetUploadController.php b/src/Controller/Admin/Asset/AssetUploadController.php new file mode 100644 index 00000000..e7567d72 --- /dev/null +++ b/src/Controller/Admin/Asset/AssetUploadController.php @@ -0,0 +1,144 @@ +value)] +class AssetUploadController extends AdminAbstractController +{ + public function __construct( + private readonly AssetUploadService $assetUploadService, + ) {} + + #[Route('/add-asset', name: 'opendxp_admin_asset_addasset', methods: ['POST'])] + public function addAssetAction(Request $request): JsonResponse + { + $res = $this->assetUploadService->addAsset($request); + + if ($res['success']) { + return $this->adminJson(ApiResponse::ok([ + 'asset' => [ + 'id' => $res['asset']->getId(), + 'path' => $res['asset']->getFullPath(), + 'type' => $res['asset']->getType(), + ], + ])); + } + + throw new BadRequestHttpException(); + } + + #[Route('/add-asset-compatibility', name: 'opendxp_admin_asset_addassetcompatibility', methods: ['POST'])] + public function addAssetCompatibilityAction(Request $request): JsonResponse + { + $res = $this->assetUploadService->addAsset($request); + + $response = $this->adminJson(ApiResponse::fromBool($res['success'], [ + 'msg' => $res['success'] ? 'Success' : 'Error', + 'id' => $res['asset'] ? $res['asset']->getId() : null, + 'fullpath' => $res['asset'] ? $res['asset']->getRealFullPath() : null, + 'type' => $res['asset'] ? $res['asset']->getType() : null, + ])); + $response->headers->set('Content-Type', 'text/html'); + + return $response; + } + + #[Route('/exists', name: 'opendxp_admin_asset_exists', methods: ['GET'])] + public function existsAction( + CheckAssetExistsHandler $checkAssetExists, + #[MapQueryParameter] int $parentId, + #[MapQueryParameter] string $filename = '', + #[MapQueryParameter] string $dir = '', + ): JsonResponse { + return new JsonResponse([ + 'exists' => $checkAssetExists($parentId, $filename, $dir), + ]); + } + + #[Route('/replace-asset', name: 'opendxp_admin_asset_replaceasset', methods: ['POST', 'PUT'])] + public function replaceAssetAction(ReplaceAssetHandler $replaceAsset, Request $request, #[MapQueryParameter] int $id): JsonResponse + { + /** @var UploadedFile $file */ + $file = $request->files->get('Filedata'); + + $asset = $replaceAsset($id, $file->getPathname(), $file->getClientOriginalName()); + + $response = $this->adminJson(ApiResponse::ok(['id' => $asset->getId(), 'path' => $asset->getRealFullPath()])); + $response->headers->set('Content-Type', 'text/html'); + + return $response; + } + + #[Route('/import-zip', name: 'opendxp_admin_asset_importzip', methods: ['POST'])] + public function importZipAction( + ImportZipHandler $importZip, + Request $request, + #[MapQueryParameter] int $parentId = 0, + #[MapQueryParameter] ?string $allowOverwrite = null, + ): Response { + if (!$request->files->has('Filedata')) { + throw new BadRequestHttpException('Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions on the file system'); + } + + /** @var UploadedFile $file */ + $file = $request->files->get('Filedata'); + if (!is_file($file->getPathname())) { + throw new BadRequestHttpException('Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions on the file system'); + } + + $importResult = $importZip($parentId, $file->getPathname(), $allowOverwrite); + + return new Response($this->encodeJson(ApiResponse::ok(['jobs' => $importResult->jobs, 'jobId' => $importResult->jobId]))); + } + + #[Route('/import-zip-files', name: 'opendxp_admin_asset_importzipfiles', methods: ['POST'])] + public function importZipFilesAction(ImportZipFilesHandler $importZipFiles, Request $request): JsonResponse + { + $importZipFiles( + (int) $request->request->get('parentId'), + (string) $request->request->get('jobId'), + (int) $request->request->get('offset'), + (int) $request->request->get('limit'), + $request->request->get('allowOverwrite') === 'true', + (bool) $request->request->get('last'), + ); + + return $this->adminJson(ApiResponse::ok()); + } +} diff --git a/src/Controller/Admin/Asset/AssetVersionController.php b/src/Controller/Admin/Asset/AssetVersionController.php new file mode 100644 index 00000000..4a2b2f10 --- /dev/null +++ b/src/Controller/Admin/Asset/AssetVersionController.php @@ -0,0 +1,95 @@ +value)] +class AssetVersionController extends AdminAbstractController +{ + #[Route('/publish-version', name: 'opendxp_admin_asset_publishversion', methods: ['POST'])] + public function publishVersionAction( + Request $request, + PublishVersionHandler $publishVersion, + ElementServiceInterface $elementService, + ): JsonResponse { + + $result = $publishVersion( + (int) $request->request->get('id'), + ); + + return $this->adminJson(ApiResponse::ok([ + 'treeData' => $elementService->getElementTreeNodeConfig($result->asset), + ])); + } + + #[Route('/show-version', name: 'opendxp_admin_asset_showversion', methods: ['GET'])] + public function showVersionAction( + Environment $twig, + ShowVersionHandler $showVersion, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $userTimezone = null, + ): Response { + $result = $showVersion($id); + + if ($result->isPdf) { + return $this->render( + '@OpenDxpAdmin/admin/asset/get_preview_pdf_open_in_new_tab.html.twig', + [ + 'thumbnailPath' => '', + 'assetPath' => $result->pdfPath + ], + ); + } + + Tool\UserTimezone::setUserTimezone($userTimezone); + if ($timezone = Tool\UserTimezone::getUserTimezone()) { + $twig->getExtension(CoreExtension::class)->setTimezone($timezone); + } + + $loader = OpenDxp::getContainer()->get('opendxp.implementation_loader.asset.metadata.data'); + + return $this->render( + '@OpenDxpAdmin/admin/asset/show_version_' . strtolower($result->asset->getType()) . '.html.twig', + [ + 'asset' => $result->asset, + 'version' => $result->version, + 'loader' => $loader, + ], + ); + } +} diff --git a/src/Controller/Admin/DataObject/ClassController.php b/src/Controller/Admin/DataObject/ClassController.php index c73ae76a..ac7170e5 100644 --- a/src/Controller/Admin/DataObject/ClassController.php +++ b/src/Controller/Admin/DataObject/ClassController.php @@ -16,1099 +16,143 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; -use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Controller\KernelControllerEventInterface; -use OpenDxp\Db; -use OpenDxp\Helper\FileSystemHelper; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommitHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkExportPrepareHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DoBulkExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetAssetTypesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassBulkExportListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIconsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetDocumentTypesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetVideoAllowedTypesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinitionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SuggestClassIdentifierHandler; use OpenDxp\Logger; -use OpenDxp\Model\Asset; use OpenDxp\Model\DataObject; -use OpenDxp\Model\Document; -use OpenDxp\Model\Exception\ConfigWriteException; -use OpenDxp\Model\Translation; -use OpenDxp\Tool\Session; -use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -use Symfony\Component\HttpKernel\Event\ControllerEvent; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal */ #[Route('/class', name: 'opendxp_admin_dataobject_class_')] -class ClassController extends AdminAbstractController implements KernelControllerEventInterface +class ClassController extends AdminAbstractController { + #[IsGranted(CorePermission::Classes->value)] #[Route('/get-document-types', name: 'getdocumenttypes', methods: ['GET'])] - public function getDocumentTypesAction(Request $request): JsonResponse + public function getDocumentTypesAction(GetDocumentTypesHandler $handler): JsonResponse { - $documentTypes = Document::getTypes(); - $typeItems = []; - foreach ($documentTypes as $documentType) { - $typeItems[] = [ - 'text' => $documentType, - ]; - } - - return $this->adminJson($typeItems); - } - - #[Route('/get-asset-types', name: 'getassettypes', methods: ['GET'])] - public function getAssetTypesAction(Request $request): JsonResponse - { - $assetTypes = Asset::getTypes(); - $typeItems = []; - foreach ($assetTypes as $assetType) { - $typeItems[] = [ - 'text' => $assetType, - ]; - } - - return $this->adminJson($typeItems); - } - - #[Route('/get-tree', name: 'gettree', methods: ['GET', 'POST'])] - public function getTreeAction(Request $request): JsonResponse - { - try { - // we need to check objects permission for listing in opendxp.model.objecttypes ext model - $this->checkPermission('objects'); - } catch (AccessDeniedHttpException) { - Logger::log('[Startup] Object types are not loaded as "objects" permission is missing'); - - //return empty string to avoid error on startup - return $this->adminJson([]); - } - - $defaultIcon = '/bundles/opendxpadmin/img/flat-color-icons/class.svg'; - - $classesList = new DataObject\ClassDefinition\Listing(); - $classesList->setOrderKey('name'); - $classesList->setOrder('asc'); - $classes = $classesList->load(); - - // filter classes - if ($request->query->get('createAllowed')) { - $tmpClasses = []; - foreach ($classes as $class) { - if ($this->getAdminUser()->isAllowed($class->getId(), 'class')) { - $tmpClasses[] = $class; - } - } - $classes = $tmpClasses; - } - - $withId = $request->query->get('withId'); - $useTitle = $request->query->get('useTitle'); - $getClassConfig = static function ($class) use ($defaultIcon, $withId, $useTitle) { - $text = $class->getName(); - if ($useTitle) { - $text = $class->getTitle() ?: $class->getName(); - } - if ($withId) { - $text .= ' (' . $class->getId() . ')'; - } - - $hasBrickField = false; - foreach ($class->getFieldDefinitions() as $fieldDefinition) { - if ($fieldDefinition instanceof DataObject\ClassDefinition\Data\Objectbricks) { - $hasBrickField = true; - - break; - } - } - - return [ - 'id' => $class->getId(), - 'text' => $text, - 'leaf' => true, - 'icon' => $class->getIcon() ? htmlspecialchars($class->getIcon()) : $defaultIcon, - 'cls' => 'opendxp_class_icon', - 'propertyVisibility' => $class->getPropertyVisibility(), - 'enableGridLocking' => $class->isEnableGridLocking(), - 'hasBrickField' => $hasBrickField, - ]; - }; - - // build groups - $groups = []; - foreach ($classes as $class) { - $groupName = null; - - if ($class->getGroup()) { - $type = 'manual'; - $groupName = $class->getGroup(); - } else { - $type = 'auto'; - if (preg_match('@^([A-Za-z])([^A-Z]+)@', $class->getName(), $matches)) { - $groupName = $matches[0]; - } - - if (!$groupName) { - // this is eg. the case when class name uses only capital letters - $groupName = $class->getName(); - } - } - - $groupName = Translation::getByKeyLocalized($groupName, Translation::DOMAIN_ADMIN, true, true); - - if (!isset($groups[$groupName])) { - $groups[$groupName] = [ - 'classes' => [], - 'type' => $type, - ]; - } - $groups[$groupName]['classes'][] = $class; - } - - $treeNodes = []; - if ($groups !== []) { - $types = array_column($groups, 'type'); - array_multisort($types, SORT_ASC, array_keys($groups), SORT_ASC, $groups); - } - - if (!$request->query->get('grouped')) { - // list output - foreach ($groups as $groupName => $groupData) { - foreach ($groupData['classes'] as $class) { - $node = $getClassConfig($class); - if (count($groupData['classes']) > 1 || $groupData['type'] === 'manual') { - $node['group'] = $groupName; - } - $treeNodes[] = $node; - } - } - } else { - // create json output - foreach ($groups as $groupName => $groupData) { - if (count($groupData['classes']) === 1 && $groupData['type'] === 'auto') { - // no group, only one child - $node = $getClassConfig($groupData['classes'][0]); - } else { - // group classes - $node = [ - 'id' => 'folder_' . $groupName, - 'text' => $groupName, - 'leaf' => false, - 'expandable' => true, - 'allowChildren' => true, - 'iconCls' => 'opendxp_icon_folder', - 'children' => [], - ]; - - foreach ($groupData['classes'] as $class) { - $node['children'][] = $getClassConfig($class); - } - } - - $treeNodes[] = $node; - } - } - - return $this->adminJson($treeNodes); - } - - #[Route('/get', name: 'get', methods: ['GET'])] - public function getAction(Request $request): JsonResponse - { - $class = DataObject\ClassDefinition::getById($request->query->get('id')); - if (!$class) { - throw $this->createNotFoundException(); - } - $class->setFieldDefinitions([]); - $isWriteable = $class->isWritable(); - $class = $class->getObjectVars(); - $class['isWriteable'] = $isWriteable; - - return $this->adminJson($class); - } - - #[Route('/get-custom-layout', name: 'getcustomlayout', methods: ['GET'])] - public function getCustomLayoutAction(Request $request): JsonResponse - { - $customLayout = DataObject\ClassDefinition\CustomLayout::getById($request->query->get('id')); - if (!$customLayout) { - $brickLayoutSeparator = strpos($request->query->get('id'), '.brick.'); - if ($brickLayoutSeparator !== false) { - $customLayout = DataObject\ClassDefinition\CustomLayout::getById(substr($request->query->get('id'), 0, $brickLayoutSeparator)); - if ($customLayout instanceof DataObject\ClassDefinition\CustomLayout) { - $customLayout = DataObject\ClassDefinition\CustomLayout::create( - [ - 'name' => $customLayout->getName().' '.substr($request->query->get('id'), $brickLayoutSeparator+strlen('.brick.')), - 'userOwner' => $this->getAdminUser()->getId(), - 'classId' => $customLayout->getClassId(), - ] - ); - - $customLayout->setId($request->query->get('id')); - if (!$customLayout->isWriteable()) { - throw new ConfigWriteException(); - } - $customLayout->save(); - } - } - - if (!$customLayout) { - throw $this->createNotFoundException(); - } - } - $isWriteable = $customLayout->isWriteable(); - $customLayout = $customLayout->getObjectVars(); - $customLayout['isWriteable'] = $isWriteable; - - return $this->adminJson(['success' => true, 'data' => $customLayout]); - } - - #[Route('/add', name: 'add', methods: ['POST'])] - public function addAction(Request $request): JsonResponse - { - $className = $request->request->get('className'); - $className = $this->correctClassname($className); - - $classId = $request->request->get('classIdentifier'); - $existingClass = DataObject\ClassDefinition::getById($classId); - if ($existingClass) { - throw new Exception('Class identifier already exists'); - } - - $class = DataObject\ClassDefinition::create( - ['name' => $className, - 'userOwner' => $this->getAdminUser()->getId(), ] - ); - - $class->setId($classId); - - $class->save(true); - - return $this->adminJson(['success' => true, 'id' => $class->getId()]); - } - - #[Route('/add-custom-layout', name: 'addcustomlayout', methods: ['POST'])] - public function addCustomLayoutAction(Request $request): JsonResponse - { - $layoutId = $request->request->get('layoutIdentifier'); - $existingLayout = DataObject\ClassDefinition\CustomLayout::getById($layoutId); - - if ($existingLayout) { - throw new Exception('Custom Layout identifier already exists'); - } - - $customLayout = DataObject\ClassDefinition\CustomLayout::create( - [ - 'name' => $request->request->get('layoutName'), - 'userOwner' => $this->getAdminUser()->getId(), - 'classId' => $request->request->get('classId'), - ] - ); - - $customLayout->setId($layoutId); - if (!$customLayout->isWriteable()) { - throw new ConfigWriteException(); - } - - $customLayout->save(); - - $isWriteable = $customLayout->isWriteable(); - $data = $customLayout->getObjectVars(); - $data['isWriteable'] = $isWriteable; - - return $this->adminJson([ - 'success' => true, - 'id' => $customLayout->getId(), - 'name' => $customLayout->getName(), - 'data' => $data, - ]); - } - - #[Route('/delete', name: 'delete', methods: ['DELETE'])] - public function deleteAction(Request $request): Response - { - $class = DataObject\ClassDefinition::getById($request->request->get('id')); - if ($class) { - $class->delete(); - } - - return new Response(); - } - - #[Route('/delete-custom-layout', name: 'deletecustomlayout', methods: ['DELETE'])] - public function deleteCustomLayoutAction(Request $request): JsonResponse - { - $customLayouts = new DataObject\ClassDefinition\CustomLayout\Listing(); - $id = $request->request->get('id'); - $customLayouts->setFilter(function (DataObject\ClassDefinition\CustomLayout $layout) use ($id) { - $currentLayoutId = $layout->getId(); - - return $currentLayoutId === $id || str_starts_with($currentLayoutId, $id . '.brick.'); - }); - - foreach ($customLayouts->getLayoutDefinitions() as $customLayout) { - $customLayout->delete(); - } - - return $this->adminJson(['success' => true]); - } - - #[Route('/save-custom-layout', name: 'savecustomlayout', methods: ['PUT'])] - public function saveCustomLayoutAction(Request $request): JsonResponse - { - $customLayout = DataObject\ClassDefinition\CustomLayout::getById($request->request->get('id')); - if (!$customLayout) { - throw $this->createNotFoundException(); - } - - $configuration = $this->decodeJson($request->request->get('configuration')); - $values = $this->decodeJson($request->request->get('values')); - - $modificationDate = (int)$values['modificationDate']; - if ($modificationDate < $customLayout->getModificationDate()) { - return $this->adminJson(['success' => false, 'msg' => 'custom_layout_changed']); - } - - $configuration['datatype'] = 'layout'; - $configuration['fieldtype'] = 'panel'; - $configuration['name'] = 'opendxp_root'; - - try { - $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($configuration, true); - $customLayout->setLayoutDefinitions($layout); - $customLayout->setName($values['name']); - $customLayout->setDescription($values['description']); - $customLayout->setDefault($values['default']); - if (!$customLayout->isWriteable()) { - throw new ConfigWriteException(); - } - $customLayout->save(); - - return $this->adminJson(['success' => true, 'id' => $customLayout->getId(), 'data' => $customLayout->getObjectVars()]); - } catch (Exception $e) { - Logger::error($e->getMessage()); - - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - /** - * @throws Exception - */ - #[Route('/save', name: 'save', methods: ['PUT'])] - public function saveAction(Request $request): JsonResponse - { - $class = DataObject\ClassDefinition::getById($request->request->get('id')); - if (!$class) { - throw $this->createNotFoundException(); - } - - $configuration = $this->decodeJson($request->request->get('configuration')); - $values = $this->decodeJson($request->request->get('values')); - - // check if the class was changed during editing in the frontend - if ($class->getModificationDate() != $values['modificationDate']) { - throw new Exception('The class was modified during editing, please reload the class and make your changes again'); - } - - if ($values['name'] != $class->getName()) { - $classByName = DataObject\ClassDefinition::getByName($values['name']); - if ($classByName && $classByName->getId() !== $class->getId()) { - throw new Exception('Class name already exists'); - } - - $values['name'] = $this->correctClassname($values['name']); - $class->rename($values['name']); - } - - if ($values['compositeIndices']) { - foreach ($values['compositeIndices'] as $index => $compositeIndex) { - if ($compositeIndex['index_key'] !== ($sanitizedKey = preg_replace('/[^a-za-z0-9_\-+]/', '', $compositeIndex['index_key']))) { - $values['compositeIndices'][$index]['index_key'] = $sanitizedKey; - } - } - } - - unset($values['creationDate'], $values['userOwner'], $values['layoutDefinitions'], $values['fieldDefinitions']); - - $configuration['datatype'] = 'layout'; - $configuration['fieldtype'] = 'panel'; - $configuration['name'] = 'opendxp_root'; - - $class->setValues($values); - - try { - $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($configuration, true); - - $class->setLayoutDefinitions($layout); - - $class->setUserModification($this->getAdminUser()->getId()); - $class->setModificationDate(time()); - - $propertyVisibility = []; - foreach ($values as $key => $value) { - if (false !== stripos($key, 'propertyVisibility')) { - if (preg_match("/\.grid\./i", $key)) { - $propertyVisibility['grid'][preg_replace("/propertyVisibility\.grid\./i", '', $key)] = (bool) $value; - } elseif (preg_match("/\.search\./i", $key)) { - $propertyVisibility['search'][preg_replace("/propertyVisibility\.search\./i", '', $key)] = (bool) $value; - } - } - } - if (!empty($propertyVisibility)) { - $class->setPropertyVisibility($propertyVisibility); - } - - $class->save(); - - // set the fielddefinitions to [] because we don't need them in the response - $class->setFieldDefinitions([]); - - return $this->adminJson(['success' => true, 'class' => $class]); - } catch (Exception $e) { - Logger::error($e->getMessage()); - - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - protected function correctClassname(string $name): string - { - $name = preg_replace('/[^a-zA-Z0-9_]+/', '', $name); - - return preg_replace('/^\d+/', '', $name); - } - - #[Route('/import-class', name: 'importclass', methods: ['POST', 'PUT'])] - public function importClassAction(Request $request): Response - { - $class = DataObject\ClassDefinition::getById($request->query->get('id')); - if (!$class) { - throw $this->createNotFoundException(); - } - - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $json = file_get_contents($file->getPathname()); - - $success = DataObject\ClassDefinition\Service::importClassDefinitionFromJson($class, $json, false, true); - - $response = $this->adminJson([ - 'success' => $success, - ]); - - // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in - // Ext.form.Action.Submit and mark the submission as failed - $response->headers->set('Content-Type', 'text/html'); - - return $response; - } - - #[Route('/import-custom-layout-definition', name: 'importcustomlayoutdefinition', methods: ['POST', 'PUT'])] - public function importCustomLayoutDefinitionAction(Request $request): Response - { - $success = false; - $responseContent = []; - - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $json = file_get_contents($file->getPathname()); - - $importData = $this->decodeJson($json); - - $existingLayout = null; - if (isset($importData['name'])) { - $existingLayout = DataObject\ClassDefinition\CustomLayout::getByName($importData['name']); - - if ($existingLayout instanceof DataObject\ClassDefinition\CustomLayout) { - $responseContent['nameAlreadyInUse'] = true; - } - } - - if (!$existingLayout instanceof DataObject\ClassDefinition\CustomLayout) { - $customLayoutId = $request->query->get('id'); - $customLayout = DataObject\ClassDefinition\CustomLayout::getById($customLayoutId); - if ($customLayout) { - try { - $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($importData['layoutDefinitions'], true); - $customLayout->setLayoutDefinitions($layout); - if (isset($importData['name'])) { - $customLayout->setName($importData['name']); - } - $customLayout->setDescription($importData['description']); - if (!$customLayout->isWriteable()) { - throw new ConfigWriteException(); - } - $customLayout->save(); - $success = true; - } catch (Exception $e) { - Logger::error($e->getMessage()); - } - } - - $responseContent['success'] = $success; - } - - $response = $this->adminJson($responseContent); - - // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in - // Ext.form.Action.Submit and mark the submission as failed - $response->headers->set('Content-Type', 'text/html'); - - return $response; - } - - #[Route('/get-custom-layout-definitions', name: 'getcustomlayoutdefinitions', methods: ['GET'])] - public function getCustomLayoutDefinitionsAction(Request $request): JsonResponse - { - $classIds = explode(',', $request->query->get('classId')); - $list = new DataObject\ClassDefinition\CustomLayout\Listing(); - - $list->setFilter(fn (DataObject\ClassDefinition\CustomLayout $layout) => in_array($layout->getClassId(), $classIds) && !str_contains($layout->getId(), '.brick.')); - $list = $list->load(); - $result = []; - foreach ($list as $item) { - $result[] = [ - 'id' => $item->getId(), - 'name' => $item->getName(), - 'default' => $item->getDefault(), - ]; - } - - return $this->adminJson(['success' => true, 'data' => $result]); - } - - #[Route('/get-all-layouts', name: 'getalllayouts', methods: ['GET'])] - public function getAllLayoutsAction(Request $request): JsonResponse - { - // get all classes - $resultList = []; - $mapping = []; - - $customLayouts = new DataObject\ClassDefinition\CustomLayout\Listing(); - $customLayouts->setFilter(fn (DataObject\ClassDefinition\CustomLayout $layout) => !str_contains($layout->getId(), '.brick.')); - $customLayouts->setOrder(fn (DataObject\ClassDefinition\CustomLayout $a, DataObject\ClassDefinition\CustomLayout $b) => strcmp($a->getName(), $b->getName())); - - $customLayouts = $customLayouts->load(); - foreach ($customLayouts as $layout) { - $mapping[$layout->getClassId()][] = $layout; - } - - $classList = new DataObject\ClassDefinition\Listing(); - $classList->setOrder('ASC'); - $classList->setOrderKey('name'); - $classList = $classList->load(); - - foreach ($classList as $class) { - if (isset($mapping[$class->getId()])) { - $classMapping = $mapping[$class->getId()]; - $resultList[] = [ - 'type' => 'main', - 'id' => $class->getId() . '_' . 0, - 'name' => $class->getName(), - ]; - - foreach ($classMapping as $layout) { - $resultList[] = [ - 'type' => 'custom', - 'id' => $class->getId() . '_' . $layout->getId(), - 'name' => $class->getName() . ' - ' . $layout->getName(), - ]; - } - } - } - - return $this->adminJson(['data' => $resultList]); - } - - #[Route('/export-class', name: 'exportclass', methods: ['GET'])] - public function exportClassAction(Request $request): Response - { - $id = $request->query->get('id'); - $class = DataObject\ClassDefinition::getById($id); - - if (!$class instanceof DataObject\ClassDefinition) { - $errorMessage = ': Class with id [ ' . $id . ' not found. ]'; - Logger::error($errorMessage); - - throw $this->createNotFoundException($errorMessage); - } - - $json = DataObject\ClassDefinition\Service::generateClassDefinitionJson($class); - - $response = new Response($json); - $response->headers->set('Content-type', 'application/json'); - $response->headers->set('Content-Disposition', 'attachment; filename: "class_' . $class->getName() . '_export.json"'); - - return $response; - } - - #[Route('/export-custom-layout-definition', name: 'exportcustomlayoutdefinition', methods: ['GET'])] - public function exportCustomLayoutDefinitionAction(Request $request): Response - { - $id = $request->query->get('id'); - - if ($id) { - $customLayout = DataObject\ClassDefinition\CustomLayout::getById($id); - if ($customLayout) { - $name = $customLayout->getName(); - $json = DataObject\ClassDefinition\Service::generateCustomLayoutJson($customLayout); - - $response = new Response($json); - $response->headers->set('Content-type', 'application/json'); - $response->headers->set('Content-Disposition', 'attachment; filename: "custom_definition_' . $name . '_export.json"'); - - return $response; - } - } - - $errorMessage = ': Custom Layout with id [ ' . $id . ' not found. ]'; - Logger::error($errorMessage); - - throw $this->createNotFoundException($errorMessage); - } - - /** - * FIELDCOLLECTIONS - */ - #[Route('/fieldcollection-get', name: 'fieldcollectionget', methods: ['GET'])] - public function fieldcollectionGetAction(Request $request): JsonResponse - { - $fc = DataObject\Fieldcollection\Definition::getByKey($request->query->get('id')); - - $isWriteable = $fc->isWritable(); - $fc = $fc->getObjectVars(); - $fc['isWriteable'] = $isWriteable; - - return $this->adminJson($fc); - } - - #[Route('/fieldcollection-update', name: 'fieldcollectionupdate', methods: ['PUT', 'POST'])] - public function fieldcollectionUpdateAction(Request $request): JsonResponse - { - try { - $key = $request->request->get('key'); - $title = $request->request->get('title'); - $group = $request->request->get('group'); - - if ($request->request->get('task') === 'add') { - // check for existing fieldcollection with same name with different lower/upper cases - $list = new DataObject\Fieldcollection\Definition\Listing(); - $list = $list->loadNames(); - - foreach ($list as $fcName) { - if (strtolower($key) === strtolower($fcName)) { - throw new Exception('FieldCollection with the same name already exists (lower/upper cases may be different)'); - } - } - } - - $fcDef = new DataObject\Fieldcollection\Definition(); - $fcDef->setKey($key); - $fcDef->setTitle($title); - $fcDef->setGroup($group); - - if ($request->request->has('values')) { - $values = $this->decodeJson($request->request->get('values')); - $fcDef->setParentClass($values['parentClass']); - $fcDef->setImplementsInterfaces($values['implementsInterfaces']); - } - - if ($request->request->has('configuration')) { - $configuration = $this->decodeJson($request->request->get('configuration')); - - $configuration['datatype'] = 'layout'; - $configuration['fieldtype'] = 'panel'; - - $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($configuration, true); - $fcDef->setLayoutDefinitions($layout); - } - - $fcDef->save(); - - return $this->adminJson(['success' => true, 'id' => $fcDef->getKey()]); - } catch (Exception $e) { - Logger::error($e->getMessage()); - - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - #[Route('/import-fieldcollection', name: 'importfieldcollection', methods: ['POST'])] - public function importFieldcollectionAction(Request $request): Response - { - $this->checkPermission('fieldcollections'); - - $fieldCollection = DataObject\Fieldcollection\Definition::getByKey($request->query->get('id')); - - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $data = file_get_contents($file->getPathname()); - - $success = DataObject\ClassDefinition\Service::importFieldCollectionFromJson($fieldCollection, $data); - - $response = $this->adminJson([ - 'success' => $success, - ]); - - // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in - // Ext.form.Action.Submit and mark the submission as failed - $response->headers->set('Content-Type', 'text/html'); - - return $response; - } - - #[Route('/export-fieldcollection', name: 'exportfieldcollection', methods: ['GET'])] - public function exportFieldcollectionAction(Request $request): Response - { - $this->checkPermission('fieldcollections'); - - $fieldCollection = DataObject\Fieldcollection\Definition::getByKey($request->query->get('id')); - - if (!$fieldCollection instanceof DataObject\Fieldcollection\Definition) { - $errorMessage = ': Field-Collection with id [ ' . $request->query->get('id') . ' not found. ]'; - Logger::error($errorMessage); - - throw $this->createNotFoundException($errorMessage); - } - - $json = DataObject\ClassDefinition\Service::generateFieldCollectionJson($fieldCollection); - $response = new Response($json); - $response->headers->set('Content-type', 'application/json'); - $response->headers->set('Content-Disposition', 'attachment; filename="fieldcollection_' . $fieldCollection->getKey() . '_export.json"'); - - return $response; - } - - #[Route('/fieldcollection-delete', name: 'fieldcollectiondelete', methods: ['DELETE'])] - public function fieldcollectionDeleteAction(Request $request): JsonResponse - { - $this->checkPermission('fieldcollections'); - - $fc = DataObject\Fieldcollection\Definition::getByKey($request->request->get('id')); - $fc->delete(); - - return $this->adminJson(['success' => true]); - } - - #[Route('/fieldcollection-tree', name: 'fieldcollectiontree', methods: ['GET', 'POST'])] - public function fieldcollectionTreeAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $list = new DataObject\Fieldcollection\Definition\Listing(); - $list = $list->load(); - - $forObjectEditor = $request->query->get('forObjectEditor'); - - $layoutDefinitions = []; - - $definitions = []; - - $allowedTypes = null; - if ($request->query->has('allowedTypes')) { - $allowedTypes = explode(',', $request->query->get('allowedTypes')); - } - - $object = $request->query->has('object_id') - ? DataObject\Concrete::getById((int) $request->query->get('object_id')) - : null; - - $currentLayoutId = $request->query->get('layoutId'); - $user = \OpenDxp\Tool\Admin::getCurrentUser(); - - $groups = []; - foreach ($list as $item) { - if ($allowedTypes && !in_array($item->getKey(), $allowedTypes)) { - continue; - } - - if ($item->getGroup()) { - if (!isset($groups[$item->getGroup()])) { - $groups[$item->getGroup()] = [ - 'id' => 'group_' . $item->getKey(), - 'text' => htmlspecialchars($item->getGroup()), - 'expandable' => true, - 'leaf' => false, - 'allowChildren' => true, - 'iconCls' => 'opendxp_icon_folder', - 'group' => $item->getGroup(), - 'children' => [], - ]; - } - if ($forObjectEditor) { - $itemLayoutDefinitions = $item->getLayoutDefinitions(); - DataObject\Service::enrichLayoutDefinition($itemLayoutDefinitions, $object); - - if ($currentLayoutId == -1 && $user->isAdmin()) { - DataObject\Service::createSuperLayout($itemLayoutDefinitions); - } - $layoutDefinitions[$item->getKey()] = $itemLayoutDefinitions; - } - $groups[$item->getGroup()]['children'][] = - [ - 'id' => $item->getKey(), - 'text' => $item->getKey(), - 'title' => $item->getTitle(), - 'key' => $item->getKey(), - 'leaf' => true, - 'iconCls' => 'opendxp_icon_fieldcollection', - ]; - } else { - if ($forObjectEditor) { - $itemLayoutDefinitions = $item->getLayoutDefinitions(); - DataObject\Service::enrichLayoutDefinition($itemLayoutDefinitions, $object); - - if ($currentLayoutId == -1 && $user->isAdmin()) { - DataObject\Service::createSuperLayout($itemLayoutDefinitions); - } - - $layoutDefinitions[$item->getKey()] = $itemLayoutDefinitions; - } - $definitions[] = [ - 'id' => $item->getKey(), - 'text' => $item->getKey(), - 'title' => $item->getTitle(), - 'key' => $item->getKey(), - 'leaf' => true, - 'iconCls' => 'opendxp_icon_fieldcollection', - ]; - } - } - - foreach ($groups as $group) { - $definitions[] = $group; - } - - $event = new GenericEvent($this, [ - 'list' => $definitions, - 'objectId' => $request->query->get('object_id'), - 'layoutDefinitions' => $layoutDefinitions, - ]); - $eventDispatcher->dispatch($event, AdminEvents::CLASS_FIELDCOLLECTION_LIST_PRE_SEND_DATA); - $definitions = $event->getArgument('list'); - $layoutDefinitions = $event->getArgument('layoutDefinitions'); - - if ($forObjectEditor) { - return $this->adminJson(['fieldcollections' => $definitions, 'layoutDefinitions' => $layoutDefinitions]); - } - - return $this->adminJson($definitions); - } - - #[Route('/fieldcollection-list', name: 'fieldcollectionlist', methods: ['GET'])] - public function fieldcollectionListAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $user = \OpenDxp\Tool\Admin::getCurrentUser(); - $currentLayoutId = $request->query->get('layoutId'); - - $list = new DataObject\Fieldcollection\Definition\Listing(); - $list = $list->load(); - - if ($request->query->has('allowedTypes')) { - $filteredList = []; - $allowedTypes = explode(',', $request->query->get('allowedTypes')); - foreach ($list as $type) { - if (in_array($type->getKey(), $allowedTypes)) { - $filteredList[] = $type; - - // mainly for objects-meta data-type - $layoutDefinitions = $type->getLayoutDefinitions(); - $context = [ - 'containerType' => 'fieldcollection', - 'containerKey' => $type->getKey(), - 'outerFieldname' => $request->query->get('field_name'), - ]; - - $object = DataObject\Concrete::getById((int) $request->query->get('object_id')); - - DataObject\Service::enrichLayoutDefinition($layoutDefinitions, $object, $context); - - if ($currentLayoutId == -1 && $user->isAdmin()) { - DataObject\Service::createSuperLayout($layoutDefinitions); - } - } - } - - $list = $filteredList; - } - - $event = new GenericEvent($this, [ - 'list' => $list, - 'objectId' => $request->query->get('object_id'), - ]); - $eventDispatcher->dispatch($event, AdminEvents::CLASS_FIELDCOLLECTION_LIST_PRE_SEND_DATA); - $list = $event->getArgument('list'); - - return $this->adminJson(['fieldcollections' => $list]); + return $this->adminJson($handler()->types); } - #[Route('/get-class-definition-for-column-config', name: 'getclassdefinitionforcolumnconfig', methods: ['GET'])] - public function getClassDefinitionForColumnConfigAction(Request $request): JsonResponse + #[IsGranted(CorePermission::Classes->value)] + #[Route('/get-asset-types', name: 'getassettypes', methods: ['GET'])] + public function getAssetTypesAction(GetAssetTypesHandler $handler): JsonResponse { - $class = DataObject\ClassDefinition::getById($request->query->get('id')); - if (!$class) { - throw $this->createNotFoundException(); - } - $objectId = (int)$request->query->get('oid'); - - $filteredDefinitions = DataObject\Service::getCustomLayoutDefinitionForGridColumnConfig($class, $objectId); - - /** @var DataObject\ClassDefinition\Layout $layoutDefinitions */ - $layoutDefinitions = $filteredDefinitions['layoutDefinition'] ?? false; - $filteredFieldDefinition = $filteredDefinitions['fieldDefinition'] ?? false; - - $class->setFieldDefinitions([]); - - $result = []; - - DataObject\Service::enrichLayoutDefinition($layoutDefinitions); + return $this->adminJson($handler()->types); + } - $result['objectColumns']['children'] = $layoutDefinitions->getChildren(); - $result['objectColumns']['nodeLabel'] = 'object_columns'; - $result['objectColumns']['nodeType'] = 'object'; + #[Route('/get-tree', name: 'gettree', methods: ['GET', 'POST'])] + public function getTreeAction( + GetClassTreeHandler $handler, + #[MapQueryParameter] ?string $createAllowed = null, + #[MapQueryParameter] ?string $withId = null, + #[MapQueryParameter] ?string $useTitle = null, + #[MapQueryParameter] ?string $grouped = null, + ): JsonResponse { + try { + $this->checkPermission('objects'); + } catch (AccessDeniedHttpException) { + Logger::log('[Startup] Object types are not loaded as "objects" permission is missing'); - // array("id", "fullpath", "published", "creationDate", "modificationDate", "filename", "classname"); - $systemColumnNames = DataObject\Concrete::SYSTEM_COLUMN_NAMES; - $systemColumns = []; - foreach ($systemColumnNames as $systemColumn) { - $systemColumns[] = ['title' => $systemColumn, 'name' => $systemColumn, 'datatype' => 'data', 'fieldtype' => 'system']; + return $this->adminJson([]); } - $result['systemColumns']['nodeLabel'] = 'system_columns'; - $result['systemColumns']['nodeType'] = 'system'; - $result['systemColumns']['children'] = $systemColumns; - - $list = new DataObject\Objectbrick\Definition\Listing(); - $list = $list->load(); - - foreach ($list as $brickDefinition) { - $classDefs = $brickDefinition->getClassDefinitions(); - if (!empty($classDefs)) { - foreach ($classDefs as $classDef) { - if ($classDef['classname'] == $class->getName()) { - $fieldName = $classDef['fieldname']; - if (isset($filteredFieldDefinition[$fieldName]) && !$filteredFieldDefinition[$fieldName]) { - continue; - } - - $key = $brickDefinition->getKey(); - - $brickLayoutDefinitions = $brickDefinition->getLayoutDefinitions(); - $context = [ - 'containerType' => 'objectbrick', - 'containerKey' => $key, - 'outerFieldname' => $fieldName, - ]; - DataObject\Service::enrichLayoutDefinition($brickLayoutDefinitions, null, $context); - - $result[$key]['nodeLabel'] = $key; - $result[$key]['brickField'] = $fieldName; - $result[$key]['nodeType'] = 'objectbricks'; - $result[$key]['children'] = $brickLayoutDefinitions->getChildren(); - break; - } - } - } - } + return $this->adminJson($handler( + createAllowed: (bool) $createAllowed, + withId: (bool) $withId, + useTitle: (bool) $useTitle, + grouped: (bool) $grouped, + )->nodes); + } - return $this->adminJson($result); + #[IsGranted(CorePermission::Classes->value)] + #[Route('/get', name: 'get', methods: ['GET'])] + public function getAction( + GetClassHandler $handler, + #[MapQueryParameter] ?string $id = null, + ): JsonResponse { + return $this->adminJson($handler($id)->classData); } - /** - * OBJECT BRICKS - */ - #[Route('/objectbrick-get', name: 'objectbrickget', methods: ['GET'])] - public function objectbrickGetAction(Request $request): JsonResponse + #[IsGranted(CorePermission::Classes->value)] + #[Route('/add', name: 'add', methods: ['POST'])] + public function addAction(Request $request, AddClassHandler $handler): JsonResponse { - $fc = DataObject\Objectbrick\Definition::getByKey($request->query->get('id')); - - $isWriteable = $fc->isWritable(); - $fc = $fc->getObjectVars(); - $fc['isWriteable'] = $isWriteable; + $result = $handler( + className: $request->request->get('className'), + classId: $request->request->get('classIdentifier'), + ); - return $this->adminJson($fc); + return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } - #[Route('/objectbrick-update', name: 'objectbrickupdate', methods: ['PUT', 'POST'])] - public function objectbrickUpdateAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse + #[IsGranted(CorePermission::Classes->value)] + #[Route('/delete', name: 'delete', methods: ['DELETE'])] + public function deleteAction(Request $request, DeleteClassHandler $handler): Response { - try { - $key = $request->request->get('key'); - $title = $request->request->get('title'); - $group = $request->request->get('group'); - - if ($request->request->get('task') === 'add') { - // check for existing brick with same name with different lower/upper cases - $list = new DataObject\Objectbrick\Definition\Listing(); - $list = $list->loadNames(); - - foreach ($list as $brickName) { - if (strtolower($key) === strtolower($brickName)) { - throw new Exception('Brick with the same name already exists (lower/upper cases may be different)'); - } - } - } - - // now we create a new definition - $brickDef = new DataObject\Objectbrick\Definition(); - $brickDef->setKey($key); - $brickDef->setTitle($title); - $brickDef->setGroup($group); - - if ($request->request->has('values')) { - $values = $this->decodeJson($request->request->get('values')); - - $brickDef->setParentClass($values['parentClass']); - $brickDef->setImplementsInterfaces($values['implementsInterfaces']); - $brickDef->setClassDefinitions($values['classDefinitions']); - } - - if ($request->request->has('configuration')) { - $configuration = $this->decodeJson($request->request->get('configuration')); + $handler($request->request->get('id')); - $configuration['datatype'] = 'layout'; - $configuration['fieldtype'] = 'panel'; - - $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($configuration, true); - $brickDef->setLayoutDefinitions($layout); - } - - $event = new GenericEvent($this, [ - 'brickDefinition' => $brickDef, - ]); - - $eventDispatcher->dispatch($event, AdminEvents::CLASS_OBJECTBRICK_UPDATE_DEFINITION); - $brickDef = $event->getArgument('brickDefinition'); - - $brickDef->save(); - - return $this->adminJson(['success' => true, 'id' => $brickDef->getKey()]); - } catch (Exception $e) { - Logger::error($e->getMessage()); - - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } + return new Response(); } - #[Route('/import-objectbrick', name: 'importobjectbrick', methods: ['POST'])] - public function importObjectbrickAction(Request $request): JsonResponse + #[IsGranted(CorePermission::Classes->value)] + #[Route('/save', name: 'save', methods: ['PUT'])] + public function saveAction(Request $request, SaveClassDefinitionHandler $handler): JsonResponse { - $this->checkPermission('objectbricks'); + $result = $handler( + id: $request->request->get('id'), + configuration: $this->decodeJson($request->request->get('configuration')), + values: $this->decodeJson($request->request->get('values')), + ); - $objectBrick = DataObject\Objectbrick\Definition::getByKey($request->query->get('id')); + return $this->adminJson(ApiResponse::ok(['class' => $result->class])); + } + #[IsGranted(CorePermission::Classes->value)] + #[Route('/import-class', name: 'importclass', methods: ['POST', 'PUT'])] + public function importClassAction( + Request $request, + ImportClassHandler $handler, + #[MapQueryParameter] ?string $id = null, + ): Response { /** @var UploadedFile $file */ $file = $request->files->get('Filedata'); - $data = file_get_contents($file->getPathname()); - $success = DataObject\ClassDefinition\Service::importObjectBrickFromJson($objectBrick, $data); + $handler($id, file_get_contents($file->getPathname())); - $response = $this->adminJson([ - 'success' => $success, - ]); + $response = $this->adminJson(ApiResponse::ok()); // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in // Ext.form.Action.Submit and mark the submission as failed @@ -1117,298 +161,42 @@ public function importObjectbrickAction(Request $request): JsonResponse return $response; } - #[Route('/export-objectbrick', name: 'exportobjectbrick', methods: ['GET'])] - public function exportObjectbrickAction(Request $request): Response - { - $this->checkPermission('objectbricks'); - - $objectBrick = DataObject\Objectbrick\Definition::getByKey($request->query->get('id')); - - if (!$objectBrick instanceof DataObject\Objectbrick\Definition) { - $errorMessage = ': Object-Brick with id [ ' . $request->query->get('id') . ' not found. ]'; - Logger::error($errorMessage); - - throw $this->createNotFoundException($errorMessage); - } + #[IsGranted(CorePermission::Classes->value)] + #[Route('/export-class', name: 'exportclass', methods: ['GET'])] + public function exportClassAction( + ExportClassHandler $handler, + #[MapQueryParameter] ?string $id = null, + ): Response { + $result = $handler($id); - $xml = DataObject\ClassDefinition\Service::generateObjectBrickJson($objectBrick); - $response = new Response($xml); + $response = new Response($result->json); $response->headers->set('Content-type', 'application/json'); - $response->headers->set('Content-Disposition', 'attachment; filename="objectbrick_' . $objectBrick->getKey() . '_export.json"'); + $response->headers->set('Content-Disposition', 'attachment; filename: "class_' . $result->className . '_export.json"'); return $response; } - #[Route('/objectbrick-delete', name: 'objectbrickdelete', methods: ['DELETE'])] - public function objectbrickDeleteAction(Request $request): JsonResponse - { - $this->checkPermission('objectbricks'); - - $fc = DataObject\Objectbrick\Definition::getByKey($request->request->get('id')); - $fc->delete(); - - return $this->adminJson(['success' => true]); - } - - #[Route('/objectbrick-tree', name: 'objectbricktree', methods: ['GET', 'POST'])] - public function objectbrickTreeAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $list = new DataObject\Objectbrick\Definition\Listing(); - $list = $list->load(); - - $forObjectEditor = $request->query->get('forObjectEditor'); - - $context = []; - $layoutDefinitions = []; - $groups = []; - $definitions = []; - $fieldname = null; - $className = null; - - $object = $request->query->has('object_id') - ? DataObject\Concrete::getById((int) $request->query->get('object_id')) - : null; - - if ($request->query->has('class_id') && $request->query->has('field_name')) { - $classId = $request->query->get('class_id'); - $fieldname = $request->query->get('field_name'); - $classDefinition = DataObject\ClassDefinition::getById($classId); - $className = $classDefinition->getName(); - } - - foreach ($list as $item) { - if ($forObjectEditor) { - $context = [ - 'containerType' => 'objectbrick', - 'containerKey' => $item->getKey(), - 'outerFieldname' => $fieldname, - ]; - } - if ($request->query->has('class_id') && $request->query->has('field_name')) { - $keep = false; - $clsDefs = $item->getClassDefinitions(); - if (!empty($clsDefs)) { - foreach ($clsDefs as $cd) { - if ($cd['classname'] == $className && $cd['fieldname'] == $fieldname) { - $keep = true; - - continue; - } - } - } - if (!$keep) { - continue; - } - } - - if ($item->getGroup()) { - if (!isset($groups[$item->getGroup()])) { - $groups[$item->getGroup()] = [ - 'id' => 'group_' . $item->getKey(), - 'text' => htmlspecialchars($item->getGroup()), - 'expandable' => true, - 'leaf' => false, - 'allowChildren' => true, - 'iconCls' => 'opendxp_icon_folder', - 'group' => $item->getGroup(), - 'children' => [], - ]; - } - if ($forObjectEditor) { - $layoutId = $request->query->get('layoutId'); - $itemLayoutDefinitions = null; - if ($layoutId) { - $layout = DataObject\ClassDefinition\CustomLayout::getById($layoutId.'.brick.'.$item->getKey()); - if ($layout instanceof DataObject\ClassDefinition\CustomLayout) { - $itemLayoutDefinitions = $layout->getLayoutDefinitions(); - } - } - - if (!$itemLayoutDefinitions instanceof \OpenDxp\Model\DataObject\ClassDefinition\Layout) { - $itemLayoutDefinitions = $item->getLayoutDefinitions(); - } - - DataObject\Service::enrichLayoutDefinition($itemLayoutDefinitions, $object, $context); - - $layoutDefinitions[$item->getKey()] = $itemLayoutDefinitions; - } - $groups[$item->getGroup()]['children'][] = - [ - 'id' => $item->getKey(), - 'text' => $item->getKey(), - 'title' => $item->getTitle(), - 'key' => $item->getKey(), - 'leaf' => true, - 'iconCls' => 'opendxp_icon_objectbricks', - ]; - } else { - if ($forObjectEditor) { - $layout = $item->getLayoutDefinitions(); - - $currentLayoutId = $request->query->get('layoutId'); - - $user = $this->getAdminUser(); - if ($currentLayoutId == -1 && $user->isAdmin()) { - DataObject\Service::createSuperLayout($layout); - } elseif ($currentLayoutId) { - $customLayout = DataObject\ClassDefinition\CustomLayout::getById($currentLayoutId.'.brick.'.$item->getKey()); - if ($customLayout instanceof DataObject\ClassDefinition\CustomLayout) { - $layout = $customLayout->getLayoutDefinitions(); - } - } - - DataObject\Service::enrichLayoutDefinition($layout, $object, $context); - - $layoutDefinitions[$item->getKey()] = $layout; - } - $definitions[] = [ - 'id' => $item->getKey(), - 'text' => $item->getKey(), - 'title' => $item->getTitle(), - 'key' => $item->getKey(), - 'leaf' => true, - 'iconCls' => 'opendxp_icon_objectbricks', - ]; - } - } - - foreach ($groups as $group) { - $definitions[] = $group; - } - - $event = new GenericEvent($this, [ - 'list' => $definitions, - 'objectId' => $request->query->get('object_id'), - 'forObjectEditor' => $forObjectEditor, - 'layoutDefinitions' => $layoutDefinitions, - 'fieldName' => $request->query->get('field_name'), - 'object' => $object, - ]); - $eventDispatcher->dispatch($event, AdminEvents::CLASS_OBJECTBRICK_LIST_PRE_SEND_DATA); - $definitions = $event->getArgument('list'); - $layoutDefinitions = $event->getArgument('layoutDefinitions'); - - if ($forObjectEditor) { - return $this->adminJson(['objectbricks' => $definitions, 'layoutDefinitions' => $layoutDefinitions]); - } - - return $this->adminJson($definitions); - } - - #[Route('/objectbrick-list', name: 'objectbricklist', methods: ['GET'])] - public function objectbrickListAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $list = new DataObject\Objectbrick\Definition\Listing(); - $list = $list->load(); - - if ($request->query->has('class_id') && $request->query->has('field_name')) { - $filteredList = []; - $classId = $request->query->get('class_id'); - $fieldname = $request->query->get('field_name'); - $classDefinition = DataObject\ClassDefinition::getById($classId); - $className = $classDefinition->getName(); - - foreach ($list as $type) { - $clsDefs = $type->getClassDefinitions(); - if (!empty($clsDefs)) { - foreach ($clsDefs as $cd) { - if ($cd['classname'] == $className && $cd['fieldname'] == $fieldname) { - $filteredList[] = $type; - - continue; - } - } - } - - $layout = $type->getLayoutDefinitions(); - - $currentLayoutId = $request->query->get('layoutId'); - - $user = $this->getAdminUser(); - if ($currentLayoutId == -1 && $user->isAdmin()) { - DataObject\Service::createSuperLayout($layout); - $objectData['layout'] = $layout; - } - - $context = [ - 'containerType' => 'objectbrick', - 'containerKey' => $type->getKey(), - 'outerFieldname' => $request->query->get('field_name'), - ]; - - $object = DataObject\Concrete::getById((int) $request->query->get('object_id')); - - DataObject\Service::enrichLayoutDefinition($layout, $object, $context); - $type->setLayoutDefinitions($layout); - } - - $list = $filteredList; - } - - $event = new GenericEvent($this, [ - 'list' => $list, - 'objectId' => $request->query->get('object_id'), - ]); - $eventDispatcher->dispatch($event, AdminEvents::CLASS_OBJECTBRICK_LIST_PRE_SEND_DATA); - $list = $event->getArgument('list'); - - return $this->adminJson(['objectbricks' => $list]); + #[Route('/get-class-definition-for-column-config', name: 'getclassdefinitionforcolumnconfig', methods: ['GET'])] + public function getClassDefinitionForColumnConfigAction( + GetClassDefinitionForColumnConfigHandler $handler, + #[MapQueryParameter] ?string $id = null, + #[MapQueryParameter] int $oid = 0, + ): JsonResponse { + return $this->adminJson($handler($id, $oid)->config); } /** * Add option to export/import all class definitions/brick definitions etc. at once */ + #[IsGranted(CorePermission::Classes->value)] #[Route('/bulk-import', name: 'bulkimport', methods: ['POST'])] - public function bulkImportAction(Request $request): JsonResponse + public function bulkImportAction(Request $request, BulkImportHandler $handler): JsonResponse { - $result = []; - /** @var UploadedFile $uploadFile */ $uploadFile = $request->files->get('Filedata'); + $result = $handler(file_get_contents($uploadFile->getPathname())); - $json = file_get_contents($uploadFile->getPathname()); - - $tmpName = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/bulk-import-' . uniqid('', false) . '.tmp'; - file_put_contents($tmpName, $json); - - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($tmpName): void { - $session->set('class_bulk_import_file', $tmpName); - }, 'opendxp_objects'); - - $json = json_decode($json, true); - - foreach ($json as $groupName => $group) { - foreach ($group as $groupItem) { - $displayName = null; - $icon = null; - - if ($groupName === 'class') { - $name = $groupItem['name']; - $icon = 'class'; - } elseif ($groupName === 'customlayout') { - $className = $groupItem['className']; - - $layoutData = ['className' => $className, 'name' => $groupItem['name']]; - $name = base64_encode(json_encode($layoutData)); - $displayName = $className . ' / ' . $groupItem['name']; - $icon = 'custom_views'; - } else { - if ($groupName === 'objectbrick') { - $icon = 'objectbricks'; - } elseif ($groupName === 'fieldcollection') { - $icon = 'fieldcollection'; - } - $name = $groupItem['key']; - } - - if (!$displayName) { - $displayName = $name; - } - $result[] = ['icon' => $icon, 'checked' => true, 'type' => $groupName, 'name' => $name, 'displayName' => $displayName]; - } - } - - $response = $this->adminJson(['success' => true, 'data' => $result]); + $response = $this->adminJson(ApiResponse::ok(['data' => $result->items])); $response->headers->set('Content-Type', 'text/html'); return $response; @@ -1416,657 +204,148 @@ public function bulkImportAction(Request $request): JsonResponse /** * Add option to export/import all class definitions/brick definitions etc. at once - * - * @throws Exception */ #[Route('/bulk-commit', name: 'bulkcommit', methods: ['POST'])] - public function bulkCommitAction(Request $request): JsonResponse + public function bulkCommitAction(Request $request, BulkCommitHandler $handler): JsonResponse { - $data = json_decode($request->request->get('data'), true); - - $session = Session::getSessionBag($request->getSession(), 'opendxp_objects'); - $filename = $session->get('class_bulk_import_file'); - $json = @file_get_contents($filename); - $json = json_decode($json, true); - - $type = $data['type']; - $name = $data['name']; - $list = $json[$type]; - - foreach ($list as $item) { - - unset($item['creationDate'], $item['modificationDate'], $item['userOwner'], $item['userModification']); - - if ($type === 'class' && $item['name'] == $name) { - $this->checkPermission('classes'); - $class = DataObject\ClassDefinition::getByName($name); - if (!$class) { - $class = new DataObject\ClassDefinition(); - $class->setName($name); - } - $success = DataObject\ClassDefinition\Service::importClassDefinitionFromJson($class, json_encode($item), true); - - return $this->adminJson(['success' => $success]); - } - - if ($type === 'objectbrick' && $item['key'] == $name) { - $this->checkPermission('objectbricks'); - if (!$brick = DataObject\Objectbrick\Definition::getByKey($name)) { - $brick = new DataObject\Objectbrick\Definition(); - $brick->setKey($name); - } - - $success = DataObject\ClassDefinition\Service::importObjectBrickFromJson($brick, json_encode($item), true); - - return $this->adminJson(['success' => $success]); - } - - if ($type === 'fieldcollection' && $item['key'] == $name) { - $this->checkPermission('fieldcollections'); - if (!$fieldCollection = DataObject\Fieldcollection\Definition::getByKey($name)) { - $fieldCollection = new DataObject\Fieldcollection\Definition(); - $fieldCollection->setKey($name); - } - - $success = DataObject\ClassDefinition\Service::importFieldCollectionFromJson($fieldCollection, json_encode($item), true); - - return $this->adminJson(['success' => $success]); - } + $handler(json_decode($request->request->get('data'), true)); - if ($type === 'customlayout') { - $this->checkPermission('classes'); - $layoutData = json_decode(base64_decode($data['name']), true); - $className = $layoutData['className']; - $layoutName = $layoutData['name']; - - if ($item['name'] == $layoutName && $item['className'] == $className) { - $class = DataObject\ClassDefinition::getByName($className); - if (!$class) { - throw new Exception('Class does not exist'); - } - - $classId = $class->getId(); - - $layoutList = new DataObject\ClassDefinition\CustomLayout\Listing(); - $layoutList->setFilter(fn (DataObject\ClassDefinition\CustomLayout $layout) => $layout->getName() === $layoutName && $layout->getClassId() === $classId); - $layoutList = $layoutList->load(); - - $layoutDefinition = null; - if ($layoutList) { - $layoutDefinition = array_values($layoutList)[0]; - } - - if (!$layoutDefinition) { - $layoutDefinition = new DataObject\ClassDefinition\CustomLayout(); - $layoutDefinition->setName($layoutName); - $layoutDefinition->setClassId($classId); - } - - try { - $layoutDefinition->setDescription($item['description']); - $layoutDef = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($item['layoutDefinitions'], true); - $layoutDefinition->setLayoutDefinitions($layoutDef); - $layoutDefinition->save(); - } catch (Exception $e) { - Logger::error($e->getMessage()); - - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - } - } - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } /** * Add option to export/import all class definitions/brick definitions etc. at once */ + #[IsGranted(CorePermission::Classes->value)] #[Route('/bulk-export-prepare', name: 'bulkexportprepare', methods: ['POST'])] - public function bulkExportPrepareAction(Request $request): Response + public function bulkExportPrepareAction(Request $request, BulkExportPrepareHandler $handler): Response { - $data = $request->request->get('data'); - - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($data): void { - $session->set('class_bulk_export_settings', $data); - }, 'opendxp_objects'); + $handler($request->request->get('data')); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/bulk-export', name: 'bulkexport', methods: ['GET'])] - public function bulkExportAction(Request $request): JsonResponse + public function bulkExportAction(GetClassBulkExportListHandler $handler): JsonResponse { - $result = []; - - if ($this->getAdminUser()->isAllowed('fieldcollections')) { - $fieldCollections = new DataObject\Fieldcollection\Definition\Listing(); - $fieldCollections = $fieldCollections->load(); - - foreach ($fieldCollections as $fieldCollection) { - $result[] = [ - 'icon' => 'fieldcollection', - 'checked' => true, - 'type' => 'fieldcollection', - 'name' => $fieldCollection->getKey(), - 'displayName' => $fieldCollection->getKey(), - ]; - } - } - - if ($this->getAdminUser()->isAllowed('classes')) { - $classes = new DataObject\ClassDefinition\Listing(); - $classes->setOrder('ASC'); - $classes->setOrderKey('id'); - $classes = $classes->load(); - - foreach ($classes as $class) { - $result[] = [ - 'icon' => 'class', - 'checked' => true, - 'type' => 'class', - 'name' => $class->getName(), - 'displayName' => $class->getName(), - ]; - } - } - - if ($this->getAdminUser()->isAllowed('objectbricks')) { - $objectBricks = new DataObject\Objectbrick\Definition\Listing(); - $objectBricks = $objectBricks->loadNames(); - - foreach ($objectBricks as $brickName) { - $result[] = [ - 'icon' => 'objectbricks', - 'checked' => true, - 'type' => 'objectbrick', - 'name' => $brickName, - 'displayName' => $brickName, - ]; - } - } - - if ($this->getAdminUser()->isAllowed('classes')) { - $customLayouts = new DataObject\ClassDefinition\CustomLayout\Listing(); - $customLayouts = $customLayouts->load(); - foreach ($customLayouts as $customLayout) { - $class = DataObject\ClassDefinition::getById($customLayout->getClassId()); - $displayName = $class->getName().' / '.$customLayout->getName(); - - $result[] = [ - 'icon' => 'custom_views', - 'checked' => true, - 'type' => 'customlayout', - 'name' => $customLayout->getId(), - 'displayName' => $displayName, - ]; - } - } - - return new JsonResponse(['success' => true, 'data' => $result]); + return $this->adminJson(ApiResponse::ok(['data' => $handler()->data])); } #[Route('/do-bulk-export', name: 'dobulkexport', methods: ['GET'])] - public function doBulkExportAction(Request $request): Response + public function doBulkExportAction(DoBulkExportHandler $handler): Response { - $session = Session::getSessionBag($request->getSession(), 'opendxp_objects'); - $list = $session->get('class_bulk_export_settings'); - $list = json_decode($list, true); - $result = []; - - foreach ($list as $item) { - if ($item['type'] === 'fieldcollection' && $this->getAdminUser()->isAllowed('fieldcollections')) { - if ($fieldCollection = DataObject\Fieldcollection\Definition::getByKey($item['name'])) { - $fieldCollectionJson = json_decode(DataObject\ClassDefinition\Service::generateFieldCollectionJson($fieldCollection)); - $fieldCollectionJson->key = $item['name']; - $result['fieldcollection'][] = $fieldCollectionJson; - } - } elseif ($item['type'] === 'class' && $this->getAdminUser()->isAllowed('classes')) { - if ($class = DataObject\ClassDefinition::getByName($item['name'])) { - $data = json_decode(DataObject\ClassDefinition\Service::generateClassDefinitionJson($class)); - $data->name = $item['name']; - $result['class'][] = $data; - } - } elseif ($item['type'] === 'objectbrick' && $this->getAdminUser()->isAllowed('objectbricks')) { - if ($objectBrick = DataObject\Objectbrick\Definition::getByKey($item['name'])) { - $objectBrickJson = json_decode(DataObject\ClassDefinition\Service::generateObjectBrickJson($objectBrick)); - $objectBrickJson->key = $item['name']; - $result['objectbrick'][] = $objectBrickJson; - } - } elseif ($item['type'] === 'customlayout' && $this->getAdminUser()->isAllowed('classes')) { - if ($customLayout = DataObject\ClassDefinition\CustomLayout::getById($item['name'])) { - $classId = $customLayout->getClassId(); - $class = DataObject\ClassDefinition::getById($classId); - $customLayoutJson = json_decode(DataObject\ClassDefinition\Service::generateCustomLayoutJson($customLayout)); - $customLayoutJson->name = $customLayout->getName(); - $customLayoutJson->className = $class->getName(); - $result['customlayout'][] = $customLayoutJson; - } - } - } + $result = $handler(); - $result = json_encode($result, JSON_PRETTY_PRINT); - $response = new Response($result); + $response = new Response($result->json); $response->headers->set('Content-type', 'application/json'); $response->headers->set('Content-Disposition', 'attachment; filename="bulk_export.json"'); return $response; } - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - // check permissions - $unrestrictedActions = [ - 'getTreeAction', 'fieldcollectionListAction', 'fieldcollectionTreeAction', 'fieldcollectionGetAction', - 'getClassDefinitionForColumnConfigAction', 'objectbrickListAction', 'objectbrickTreeAction', 'objectbrickGetAction', - 'objectbrickDeleteAction', 'objectbrickUpdateAction', 'importObjectbrickAction', 'exportObjectbrickAction', 'bulkCommitAction', 'doBulkExportAction', 'bulkExportAction', 'importFieldcollectionAction', 'exportFieldcollectionAction', // permissions for listed write operations handled separately in action methods - 'selectOptionsGetAction', 'selectOptionsTreeAction', 'selectOptionsUpdateAction', 'getSelectOptionsUsagesAction', 'selectOptionsDeleteAction', - ]; - - $this->checkActionPermission($event, 'classes', $unrestrictedActions); - } - - #[Route('/get-fieldcollection-usages', name: 'getfieldcollectionusages', methods: ['GET'])] - public function getFieldcollectionUsagesAction(Request $request): Response - { - $key = $request->query->get('key'); - $result = []; - - $classes = new DataObject\ClassDefinition\Listing(); - $classes = $classes->load(); - foreach ($classes as $class) { - $fieldDefs = $class->getFieldDefinitions(); - foreach ($fieldDefs as $fieldDef) { - if ($fieldDef instanceof DataObject\ClassDefinition\Data\Fieldcollections) { - $allowedKeys = $fieldDef->getAllowedTypes(); - if (in_array($key, $allowedKeys)) { - $result[] = [ - 'class' => $class->getName(), - 'field' => $fieldDef->getName(), - ]; - } - } - } - } - - return $this->adminJson($result); - } - - #[Route('/get-bricks-usages', name: 'getbrickusages', methods: ['GET'])] - public function getBrickUsagesAction(Request $request): Response - { - $classId = $request->query->get('classId'); - $myclass = DataObject\ClassDefinition::getById($classId); - - $result = []; - - $brickDefinitions = new DataObject\Objectbrick\Definition\Listing(); - $brickDefinitions = $brickDefinitions->load(); - foreach ($brickDefinitions as $brickDefinition) { - $classes = $brickDefinition->getClassDefinitions(); - foreach ($classes as $class) { - if ($myclass->getName() == $class['classname']) { - $result[] = [ - 'objectbrick' => $brickDefinition->getKey(), - 'field' => $class['fieldname'], - ]; - } - } - } - - return $this->adminJson($result); - } - #[Route('/get-select-options-usages', name: 'getselectoptionsusages', methods: [Request::METHOD_GET])] - public function getSelectOptionsUsagesAction(Request $request): Response - { - $usages = []; - $id = $request->query->get(DataObject\SelectOptions\Config::PROPERTY_ID); - $selectOptionsConfiguration = $this->getSelectOptionsConfig($id); - foreach ($selectOptionsConfiguration->getFieldsUsedIn() as $className => $fieldNames) { - foreach ($fieldNames as $fieldName) { - $usages[] = [ - 'class' => $className, - 'field' => $fieldName, - ]; - } - } - - return $this->adminJson($usages); + public function getSelectOptionsUsagesAction( + GetSelectOptionsUsagesHandler $handler, + #[MapQueryParameter(name: DataObject\SelectOptions\Config::PROPERTY_ID)] ?string $id = null, + ): Response { + return $this->adminJson($handler($id)->usages); } + #[IsGranted(CorePermission::Classes->value)] #[Route('/get-icons', name: 'geticons', methods: ['GET'])] - public function getIconsAction(Request $request, EventDispatcherInterface $eventDispatcher): Response - { - $classId = $request->query->get('classId'); - $type = $request->query->has('type') ? $request->query->getString('type') : null; - - $iconDir = OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img'; - if ($type === '') { - return $this->adminJson([]); - } - - if ($type === null) { - $classIcons = FileSystemHelper::scanDirectory($iconDir . '/object-icons/'); - $colorIcons = FileSystemHelper::scanDirectory($iconDir . '/flat-color-icons/'); - $twemoji = FileSystemHelper::scanDirectory($iconDir . '/twemoji/'); - $icons = [...$classIcons, ...$colorIcons, ...$twemoji]; - } else { - $icons = match($type) { - 'color' => FileSystemHelper::scanDirectory($iconDir . '/flat-color-icons/'), - 'white' => FileSystemHelper::scanDirectory($iconDir . '/flat-white-icons/'), - 'twemoji-1', 'twemoji-2', 'twemoji-3', - 'twemoji_variants-1', 'twemoji_variants-2', 'twemoji_variants-3' - => FileSystemHelper::scanDirectory($iconDir . '/twemoji/'), - default => [], - }; - } - - $style = ''; - if ($type === 'white') { - $style = 'background-color:#000'; - } - - foreach ($icons as &$icon) { - $icon = str_replace(OPENDXP_WEB_ROOT, '', $icon); - } - - $event = new GenericEvent($this, [ - 'icons' => $icons, - 'classId' => $classId, - ]); - $eventDispatcher->dispatch($event, AdminEvents::CLASS_OBJECT_ICONS_PRE_SEND_DATA); - $icons = $event->getArgument('icons'); - - $startIndex = 0; - $result = []; - - if ($type !== null && str_starts_with($type, 'twemoji')) { - foreach ($icons as $index => $twemojiIcon) { - $iconBase = basename($twemojiIcon); - - // All the variants (like skin color) have a hyphen in their base name - // Here we remove/unset wheter if the selected icon type is the variant list - $explodeByHyphen = explode('-', $iconBase); - if ( - (!str_starts_with($type, 'twemoji_variants') && isset($explodeByHyphen[1])) || - (str_starts_with($type, 'twemoji_variants') && !isset($explodeByHyphen[1])) - ) { - unset($icons[$index]); - } - } - - $icons = array_values($icons); - $limit = count($icons); - - if (str_ends_with($type, '-1')) { - $limit = floor($limit / 3); - } - if (str_ends_with($type, '-2')) { - $startIndex = floor($limit / 3); - $limit = floor($limit / 3 * 2); - } - if (str_ends_with($type, '-3')) { - $startIndex = floor($limit / 3 * 2); - } - } else { - $limit = count($icons); - } - - for ($i = $startIndex; $i < $limit; $i++) { - $icon = $icons[$i]; - $content = file_get_contents(OPENDXP_WEB_ROOT . $icon); - $result[] = [ - 'text' => sprintf( - '', - $style, - mime_content_type(OPENDXP_WEB_ROOT . $icon), - base64_encode($content) - ), - 'value' => $icon, - ]; - } - - return $this->adminJson($result); + public function getIconsAction( + GetClassIconsHandler $handler, + #[MapQueryParameter] ?string $classId = null, + #[MapQueryParameter] ?string $type = null, + ): Response { + return $this->adminJson($handler($type, $classId)->icons); } + #[IsGranted(CorePermission::Classes->value)] #[Route('/suggest-class-identifier', name: 'suggestclassidentifier')] - public function suggestClassIdentifierAction(): Response - { - $db = Db::get(); - $maxId = $db->fetchOne('SELECT MAX(CAST(id AS SIGNED)) FROM classes'); - - $existingIds = $db->fetchFirstColumn('SELECT LOWER(id) FROM classes'); - - $result = [ - 'suggestedIdentifier' => $maxId ? $maxId + 1 : 1, - 'existingIds' => $existingIds, - ]; - - return $this->adminJson($result); - } - - #[Route('/suggest-custom-layout-identifier', name: 'suggestcustomlayoutidentifier', methods: ['GET'])] - public function suggestCustomLayoutIdentifierAction(Request $request): Response + public function suggestClassIdentifierAction(SuggestClassIdentifierHandler $handler): Response { - $classId = $request->query->get('classId'); - - $identifier = DataObject\ClassDefinition\CustomLayout::getIdentifier($classId); - - $list = new DataObject\ClassDefinition\CustomLayout\Listing(); - - $list = $list->load(); - $existingIds = []; - $existingNames = []; - - foreach ($list as $item) { - $existingIds[] = $item->getId(); - if ($item->getClassId() == $classId) { - $existingNames[] = $item->getName(); - } - } - - $result = [ - 'suggestedIdentifier' => $identifier, - 'existingIds' => $existingIds, - 'existingNames' => $existingNames, - ]; + $result = $handler(); - return $this->adminJson($result); + return $this->adminJson([ + 'suggestedIdentifier' => $result->suggestedIdentifier, + 'existingIds' => $result->existingIds, + ]); } + #[IsGranted(CorePermission::Classes->value)] #[Route('/text-layout-preview', name: 'textlayoutpreview', methods: ['GET'])] - public function textLayoutPreviewAction(Request $request): Response - { - $objPath = $request->query->get('previewObject', ''); - $className = '\\OpenDxp\\Model\\DataObject\\' . $request->query->get('className'); - $obj = DataObject::getByPath($objPath) ?? new $className(); - - $textLayout = new DataObject\ClassDefinition\Layout\Text(); - $textLayout->setName('textLayoutPreview' . $className); - - $context = [ - 'data' => $request->query->get('renderingData'), - ]; - - if ($renderingClass = $request->query->get('renderingClass')) { - $textLayout->setRenderingClass($renderingClass); - $textLayout->setRenderingData($request->query->get('renderingData', '')); - } - - if ($staticHtml = $request->query->get('html')) { - $textLayout->setHtml($staticHtml); - } - - $html = $textLayout->enrichLayoutDefinition($obj, $context)->getHtml(); - - $content = - "\n" . - "\n" . - '\n" . - "\n\n" . - "\n" . - $html . - "\n\n\n" . - "\n"; - - $response = new Response($content); + public function textLayoutPreviewAction( + GetTextLayoutPreviewHandler $handler, + #[MapQueryParameter] string $previewObject = '', + #[MapQueryParameter] ?string $className = null, + #[MapQueryParameter] ?string $renderingData = null, + #[MapQueryParameter] ?string $renderingClass = null, + #[MapQueryParameter] ?string $html = null, + ): Response { + $response = new Response($handler( + objPath: $previewObject, + className: $className, + renderingData: $renderingData, + renderingClass: $renderingClass, + html: $html, + )->content); $response->headers->set('Content-Type', 'text/html'); return $response; } + #[IsGranted(CorePermission::Classes->value)] #[Route('/video-supported-types', name: 'videosupportedTypestypes', methods: ['GET'])] - public function videoAllowedTypesAction(Request $request, TranslatorInterface $translator): Response + public function videoAllowedTypesAction(GetVideoAllowedTypesHandler $handler): Response { - $videoDef = new DataObject\ClassDefinition\Data\Video(); - $res = []; - - foreach ($videoDef->getSupportedTypes() as $type) { - $res[] = [ - 'key' => $type, - 'value' => $translator->trans($type, [], 'admin'), - ]; - } - - return $this->adminJson($res); + return $this->adminJson($handler()->types); } + #[IsGranted(CorePermission::Selectoptions->value)] #[Route('/select-options-get', name: 'selectoptionsget', methods: [Request::METHOD_GET])] - public function selectOptionsGetAction(Request $request): JsonResponse - { - $this->checkPermission('selectoptions'); - $id = $request->query->get(DataObject\SelectOptions\Config::PROPERTY_ID); - $selectOptionsConfiguration = $this->getSelectOptionsConfig($id); - - $data = $selectOptionsConfiguration->getObjectVars(); - $data['isWriteable'] = $selectOptionsConfiguration->isWriteable(); - $data['enumName'] = $selectOptionsConfiguration->getEnumName(true); - - return $this->adminJson($data); + public function selectOptionsGetAction( + GetSelectOptionsHandler $handler, + #[MapQueryParameter(name: DataObject\SelectOptions\Config::PROPERTY_ID)] ?string $id = null, + ): JsonResponse { + return $this->adminJson($handler($id)->data); } + #[IsGranted(CorePermission::Selectoptions->value)] #[Route('/select-options-update', name: 'selectoptionsupdate', methods: [Request::METHOD_PUT, Request::METHOD_POST])] - public function selectOptionsUpdateAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $this->checkPermission('selectoptions'); - - try { - $id = $request->request->get(DataObject\SelectOptions\Config::PROPERTY_ID); - - if ($request->request->get('task') === 'add' && (new DataObject\SelectOptions\Config\Listing())->hasConfig($id)) { - throw new Exception('Select options with the same ID already exists (lower/upper cases may be different)'); - } - - $group = $request->request->get(DataObject\SelectOptions\Config::PROPERTY_GROUP); - $useTraits = $request->request->get(DataObject\SelectOptions\Config::PROPERTY_USE_TRAITS, ''); - $implementsInterfaces = $request->request->get(DataObject\SelectOptions\Config::PROPERTY_IMPLEMENTS_INTERFACES, ''); - $selectOptionsData = $request->request->get(DataObject\SelectOptions\Config::PROPERTY_SELECT_OPTIONS, 'null'); - $selectOptionsConfiguration = DataObject\SelectOptions\Config::createFromData( - [ - DataObject\SelectOptions\Config::PROPERTY_ID => $id, - DataObject\SelectOptions\Config::PROPERTY_GROUP => $group, - DataObject\SelectOptions\Config::PROPERTY_USE_TRAITS => $useTraits, - DataObject\SelectOptions\Config::PROPERTY_IMPLEMENTS_INTERFACES => $implementsInterfaces, - DataObject\SelectOptions\Config::PROPERTY_SELECT_OPTIONS => $this->decodeJson($selectOptionsData), - ] - ); - - $event = new GenericEvent($this, [ - 'selectOptionsConfiguration' => $selectOptionsConfiguration, - ]); - $eventDispatcher->dispatch($event, AdminEvents::CLASS_SELECTOPTIONS_UPDATE_CONFIGURATION); - /** @var DataObject\SelectOptions\Config $selectOptionsConfiguration */ - $selectOptionsConfiguration = $event->getArgument('selectOptionsConfiguration'); - - $selectOptionsConfiguration->save(); - - return $this->adminJson(['success' => true, 'id' => $selectOptionsConfiguration->getId()]); - } catch (Exception $exception) { - Logger::error($exception->getMessage()); + public function selectOptionsUpdateAction( + Request $request, + SaveSelectOptionsHandler $handler, + ): JsonResponse { + $result = $handler( + id: $request->request->get(DataObject\SelectOptions\Config::PROPERTY_ID), + task: $request->request->get('task', ''), + group: $request->request->get(DataObject\SelectOptions\Config::PROPERTY_GROUP), + useTraits: $request->request->get(DataObject\SelectOptions\Config::PROPERTY_USE_TRAITS, ''), + implementsInterfaces: $request->request->get(DataObject\SelectOptions\Config::PROPERTY_IMPLEMENTS_INTERFACES, ''), + selectOptionsData: $this->decodeJson($request->request->get(DataObject\SelectOptions\Config::PROPERTY_SELECT_OPTIONS, 'null')), + ); - return $this->adminJson(['success' => false, 'message' => $exception->getMessage()]); - } + return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } + #[IsGranted(CorePermission::Selectoptions->value)] #[Route('/select-options-tree', name: 'selectoptionstree', methods: [Request::METHOD_GET, Request::METHOD_POST])] - public function selectOptionsTreeAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $this->checkPermission('selectoptions'); - $configurations = $groups = []; - - $selectOptionConfigs = new DataObject\SelectOptions\Config\Listing(); - foreach ($selectOptionConfigs as $selectOptionConfig) { - $id = $selectOptionConfig->getId(); - $configurationData = [ - 'id' => $id, - 'text' => $id, - 'leaf' => true, - 'iconCls' => 'opendxp_icon_select', - ]; - - if ((int)$request->query->get('grouped', '0') === 0 || !$selectOptionConfig->hasGroup()) { - $configurations[] = $configurationData; - - continue; - } - - $group = $selectOptionConfig->getGroup(); - if (!isset($groups[$group])) { - $groups[$group] = [ - 'id' => 'group_' . $id, - 'text' => htmlspecialchars($group ?? ''), - 'expandable' => true, - 'leaf' => false, - 'allowChildren' => true, - 'iconCls' => 'opendxp_icon_folder', - 'group' => $group, - 'children' => [], - ]; - } - $groups[$group]['children'][] = $configurationData; - } - - foreach ($groups as $group) { - $configurations[] = $group; - } - - $event = new GenericEvent($this, [ - 'list' => $configurations, - ]); - $eventDispatcher->dispatch($event, AdminEvents::CLASS_SELECTOPTIONS_LIST_PRE_SEND_DATA); - - return $this->adminJson($configurations); + public function selectOptionsTreeAction( + GetSelectOptionsTreeHandler $handler, + #[MapQueryParameter] int $grouped = 0, + ): JsonResponse { + return $this->adminJson($handler($grouped)->configurations); } + #[IsGranted(CorePermission::Selectoptions->value)] #[Route('/select-options-delete', name: 'selectoptionsdelete', methods: [Request::METHOD_DELETE])] - public function selectOptionsDeleteAction(Request $request): JsonResponse - { - $this->checkPermission('selectoptions'); - - try { - $id = $request->request->get(DataObject\SelectOptions\Config::PROPERTY_ID); - $this->getSelectOptionsConfig($id)->delete(); - - return $this->adminJson(['success' => true]); - } catch (Exception $exception) { - return $this->adminJson(['success' => false, 'message' => $exception->getMessage()]); - } - } - - protected function getSelectOptionsConfig(string $id): DataObject\SelectOptions\Config - { - $selectOptions = DataObject\SelectOptions\Config::getById($id); - if (!$selectOptions instanceof \OpenDxp\Model\DataObject\SelectOptions\Config) { - throw new NotFoundHttpException('Not Found', code: 1677133720896); - } + public function selectOptionsDeleteAction( + Request $request, + DeleteSelectOptionsHandler $handler, + ): JsonResponse { + $handler($request->request->get(DataObject\SelectOptions\Config::PROPERTY_ID)); - return $selectOptions; + return $this->adminJson(ApiResponse::ok()); } } diff --git a/src/Controller/Admin/DataObject/ClassificationstoreController.php b/src/Controller/Admin/DataObject/ClassificationstoreController.php index b092e927..c5c28194 100644 --- a/src/Controller/Admin/DataObject/ClassificationstoreController.php +++ b/src/Controller/Admin/DataObject/ClassificationstoreController.php @@ -16,1481 +16,479 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; -use Doctrine\DBAL\ArrayParameterType; -use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Controller\KernelControllerEventInterface; -use OpenDxp\Db; -use OpenDxp\Helper\ArrayHelper; -use OpenDxp\Model\DataObject; -use OpenDxp\Model\DataObject\ClassDefinition\Data\LayoutDefinitionEnrichmentInterface; -use OpenDxp\Model\DataObject\Classificationstore; -use OpenDxp\Model\Translation; -use OpenDxp\Model\Translation\Listing; -use OpenDxp\Model\User; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollectionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroupsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddPropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStoreHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeletePropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStoreHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroupsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPropertiesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetStoreTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\ListStoresHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdatePropertyHandler; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Security\SecurityHelper; -use OpenDxp\Tool\Admin; -use stdClass; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Event\ControllerEvent; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal */ #[Route('/classificationstore', name: 'opendxp_admin_dataobject_classificationstore_')] -class ClassificationstoreController extends AdminAbstractController implements KernelControllerEventInterface +class ClassificationstoreController extends AdminAbstractController { + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-collection', name: 'deletecollection', methods: ['DELETE'])] - public function deleteCollectionAction(Request $request): JsonResponse + public function deleteCollectionAction(Request $request, DeleteCollectionHandler $handler): JsonResponse { - $this->checkPermission('classificationstore'); + $handler($request->request->getInt('id')); - $id = $request->request->getInt('id'); - - $configRelations = new Classificationstore\CollectionGroupRelation\Listing(); - $configRelations->setCondition('colId = ?', $id); - $list = $configRelations->load(); - foreach ($list as $item) { - $item->delete(); - } - - $config = Classificationstore\CollectionConfig::getById($id); - $config->delete(); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-collection-relation', name: 'deletecollectionrelation', methods: ['DELETE'])] - public function deleteCollectionRelationAction(Request $request): JsonResponse + public function deleteCollectionRelationAction(Request $request, DeleteCollectionRelationHandler $handler): JsonResponse { - $this->checkPermission('classificationstore'); - - $colId = $request->request->getInt('colId'); - $groupId = $request->request->getInt('groupId'); + $handler( + colId: $request->request->getInt('colId'), + groupId: $request->request->getInt('groupId'), + ); - $config = new Classificationstore\CollectionGroupRelation(); - $config->setColId($colId); - $config->setGroupId($groupId); - - $config->delete(); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-relation', name: 'deleterelation', methods: ['DELETE'])] - public function deleteRelationAction(Request $request): JsonResponse + public function deleteRelationAction(Request $request, DeleteRelationHandler $handler): JsonResponse { - $this->checkPermission('classificationstore'); - - $keyId = $request->request->getInt('keyId'); - $groupId = $request->request->getInt('groupId'); - - $config = new Classificationstore\KeyGroupRelation(); - $config->setKeyId($keyId); - $config->setGroupId($groupId); - - $config->delete(); + $handler( + keyId: $request->request->getInt('keyId'), + groupId: $request->request->getInt('groupId'), + ); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-group', name: 'deletegroup', methods: ['DELETE'])] - public function deleteGroupAction(Request $request): JsonResponse + public function deleteGroupAction(Request $request, DeleteGroupHandler $handler): JsonResponse { - $this->checkPermission('classificationstore'); + $handler($request->request->getInt('id')); - $id = $request->request->getInt('id'); - - $config = Classificationstore\GroupConfig::getById($id); - $config->delete(); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/create-group', name: 'creategroup', methods: ['POST'])] - public function createGroupAction(Request $request): JsonResponse + public function createGroupAction(Request $request, CreateGroupHandler $handler): JsonResponse { - $this->checkPermission('classificationstore'); - $name = SecurityHelper::convertHtmlSpecialChars($request->request->get('name')); - $storeId = $request->request->getInt('storeId'); - $config = Classificationstore\GroupConfig::getByName($name, $storeId); - - if (!$config) { - $config = new Classificationstore\GroupConfig(); - $config->setStoreId($storeId); - $config->setName($name); - $config->save(); + $result = $handler( + name: $name, + storeId: $request->request->getInt('storeId'), + ); - return $this->adminJson(['success' => true, 'id' => $config->getName()]); + if ($result->alreadyExists) { + throw new BadRequestHttpException('classificationstore_error_group_exists_msg'); } - return $this->adminJson(['success' => false, 'id' => $config->getName(), 'message' => 'classificationstore_error_group_exists_msg']); + return $this->adminJson(ApiResponse::ok(['id' => $result->name])); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/create-store', name: 'createstore', methods: ['POST'])] - public function createStoreAction(Request $request): JsonResponse + public function createStoreAction(Request $request, CreateStoreHandler $handler): JsonResponse { - $this->checkPermission('classificationstore'); - $name = SecurityHelper::convertHtmlSpecialChars($request->request->get('name')); + $result = $handler($name); - $config = Classificationstore\StoreConfig::getByName($name); - - if (!$config) { - $config = new Classificationstore\StoreConfig(); - $config->setName($name); - $config->save(); - } else { - throw new Exception('Store with the given name exists'); - } - - return $this->adminJson(['success' => true, 'storeId' => $config->getId()]); + return $this->adminJson(ApiResponse::ok(['storeId' => $result->storeId])); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/create-collection', name: 'createcollection', methods: ['POST'])] - public function createCollectionAction(Request $request): JsonResponse + public function createCollectionAction(Request $request, CreateCollectionHandler $handler): JsonResponse { - $this->checkPermission('classificationstore'); - $name = SecurityHelper::convertHtmlSpecialChars($request->request->get('name')); - $storeId = $request->request->getInt('storeId'); - $config = Classificationstore\CollectionConfig::getByName($name, $storeId); - - if (!$config) { - $config = new Classificationstore\CollectionConfig(); - $config->setName($name); - $config->setStoreId($storeId); - $config->save(); - } + $result = $handler( + name: $name, + storeId: $request->request->getInt('storeId'), + ); - return $this->adminJson(['success' => true, 'id' => $config->getName()]); + return $this->adminJson(ApiResponse::ok(['id' => $result->name])); } + #[IsGranted(CorePermission::Objects->value)] #[Route('/collections', name: 'collectionsactionget', methods: ['GET'])] - public function collectionsActionGet(Request $request): JsonResponse - { - $this->checkPermission('objects'); - - $start = 0; - $limit = $request->query->get('limit') ? (int) $request->query->get('limit') : 15; - - $orderKey = 'name'; - $order = 'ASC'; - - if ($request->query->has('dir')) { - $order = $request->query->get('dir'); - } - - if ($request->query->has('start')) { - $start = (int) $request->query->get('start'); - } - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->query->all()); - if ($sortingSettings['orderKey'] && $sortingSettings['order']) { - $orderKey = $sortingSettings['orderKey']; - $order = $sortingSettings['order']; - } - - if ($request->query->getBoolean('overrideSort')) { - $orderKey = 'id'; - $order = 'DESC'; - } - - $storeIdFromDefinition = 0; - $allowedCollectionIds = []; - if ($oid = $request->query->get('oid')) { - $object = DataObject\Concrete::getById((int) $oid); - $class = $object->getClass(); - /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ - $fd = $class->getFieldDefinition($request->query->get('fieldname')); - $allowedGroupIds = $fd->getAllowedGroupIds(); - - if ($allowedGroupIds) { - $db = \OpenDxp\Db::get(); - $relationList = $db->fetchAllAssociative( - 'SELECT * FROM classificationstore_collectionrelations WHERE groupId IN (?)', - [$allowedGroupIds], - [ArrayParameterType::INTEGER] - ); - - foreach ($relationList as $item) { - $allowedCollectionIds[] = $item['colId']; - } - } - - $storeIdFromDefinition = $fd->getStoreId(); - } - - $list = new Classificationstore\CollectionConfig\Listing(); - - $list->setLimit($limit); - $list->setOffset($start); - $list->setOrder($order); - $list->setOrderKey($orderKey); - - $conditionParts = []; - $db = Db::get(); - - $searchfilter = $request->query->get('searchfilter'); - if ($searchfilter) { - $searchFilterConditions = []; - - $searchTerms = [$searchfilter, ...$this->getTranslatedSearchFilterTerms($searchfilter)]; - foreach ($searchTerms as $searchFilterTerm) { - $searchFilterConditions[] = 'name LIKE '.$db->quote('%'.$searchFilterTerm.'%').' OR description LIKE '.$db->quote('%'.$searchFilterTerm.'%'); - } - - $conditionParts[] = '('.implode(' OR ', $searchFilterConditions).')'; - } - - $storeId = $request->query->get('storeId'); - $storeId = $storeId ? (int) $storeId : $storeIdFromDefinition; - - $conditionParts[] = ' (storeId = ' . $db->quote($storeId) . ')'; - - if ($request->query->has('filter')) { - $filterString = $request->query->get('filter'); - $filters = json_decode($filterString); - /** @var stdClass $f */ - foreach ($filters as $f) { - if (!isset($f->value)) { - continue; - } - - $conditionParts[] = $db->quoteIdentifier($f->property) . ' LIKE ' . $db->quote('%' . $f->value . '%'); - } - } - - if ($allowedCollectionIds) { - $conditionParts[] = ' id in (' . implode(',', $allowedCollectionIds) . ')'; - } - - $condition = implode(' AND ', $conditionParts); - - $list->setCondition($condition); - - $list->load(); - $configList = $list->getList(); - - $rootElement = []; - - $data = []; - foreach ($configList as $config) { - $name = $config->getName(); - if (!$name) { - $name = 'EMPTY'; - } - $item = [ - 'storeId' => $config->getStoreId(), - 'id' => $config->getId(), - 'name' => $name, - 'description' => $config->getDescription(), - ]; - if ($config->getCreationDate()) { - $item['creationDate'] = $config->getCreationDate(); - } - - if ($config->getModificationDate()) { - $item['modificationDate'] = $config->getModificationDate(); - } - - $data[] = $item; - } - $rootElement['data'] = $data; - $rootElement['success'] = true; - $rootElement['total'] = $list->getTotalCount(); - - return $this->adminJson($rootElement); + public function collectionsActionGet( + Request $request, + GetCollectionsHandler $handler, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $limit = null, + #[MapQueryParameter] ?string $dir = null, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] bool $overrideSort = false, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $oid = null, + #[MapQueryParameter] ?string $fieldname = null, + #[MapQueryParameter] ?string $searchfilter = null, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $storeId = null, + #[MapQueryParameter] ?string $filter = null, + ): JsonResponse { + $result = $handler( + queryAll: $request->query->all(), + limit: $limit ?? 15, + start: $start, + dir: $dir, + overrideSort: $overrideSort, + oid: $oid, + fieldname: $fieldname, + searchfilter: $searchfilter, + storeId: $storeId, + filter: $filter, + ); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/collections', name: 'collections', methods: ['POST', 'PUT'])] - public function collectionsAction(Request $request): JsonResponse + public function collectionsAction(Request $request, UpdateCollectionHandler $handler): JsonResponse { - if ($request->request->has('data')) { - $dataParam = $request->request->get('data'); - $data = $this->decodeJson($dataParam); - - $id = $data['id']; - $config = Classificationstore\CollectionConfig::getById($id); - - foreach ($data as $key => $value) { - if ($key !== 'id') { - $setter = 'set' . $key; - $config->$setter($value); - } - } - - $config->save(); - - return $this->adminJson(['success' => true, 'data' => $this->getConfigItem($config)]); + if (!$request->request->has('data')) { + throw new BadRequestHttpException(); } - return $this->adminJson(['success' => false]); + $data = $this->decodeJson($request->request->get('data')); + $result = $handler($data); + + return $this->adminJson(ApiResponse::ok(['data' => $result->item])); } + #[IsGranted(CorePermission::Objects->value)] #[Route('/groups', name: 'groupsactionget', methods: ['GET'])] - public function groupsActionGet(Request $request): JsonResponse - { - $this->checkPermission('objects'); - - $start = 0; - $limit = 15; - $orderKey = 'name'; - $order = 'ASC'; - - if ($request->query->has('dir')) { - $order = $request->query->get('dir'); - } - - if ($request->query->has('sort')) { - $orderKey = $request->query->get('sort'); - } - - if ($request->query->has('limit')) { - $limit = (int) $request->query->get('limit'); - } - if ($request->query->has('start')) { - $start = (int) $request->query->get('start'); - } - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->query->all()); - if ($sortingSettings['orderKey'] && $sortingSettings['order']) { - $orderKey = $sortingSettings['orderKey']; - $order = $sortingSettings['order']; - } - - if ($request->query->getBoolean('overrideSort')) { - $orderKey = 'id'; - $order = 'DESC'; - } - - $list = new Classificationstore\GroupConfig\Listing(); - - $list->setLimit($limit); - $list->setOffset($start); - $list->setOrder($order); - $list->setOrderKey($orderKey); - - $conditionParts = []; - $db = Db::get(); - - if ($request->query->has('searchfilter')) { - $searchfilter = $request->query->get('searchfilter'); - $searchFilterConditions = []; - - $searchTerms = [$searchfilter, ...$this->getTranslatedSearchFilterTerms($searchfilter)]; - foreach ($searchTerms as $searchFilterTerm) { - $searchFilterConditions[] = 'name LIKE '.$db->quote('%'.$searchFilterTerm.'%').' OR description LIKE '.$db->quote('%'.$searchFilterTerm.'%'); - } - - $conditionParts[] = '('.implode(' OR ', $searchFilterConditions).')'; - } - - if ($storeId = $request->query->getInt('storeId')) { - $conditionParts[] = '(storeId = ' . $db->quote($storeId) . ')'; - } - - if ($request->query->has('filter')) { - $filterString = $request->query->get('filter'); - $filters = json_decode($filterString); - /** @var stdClass $f */ - foreach ($filters as $f) { - if (!isset($f->value)) { - continue; - } - - $conditionParts[] = $db->quoteIdentifier($f->property) . ' LIKE ' . $db->quote('%' . $f->value . '%'); - } - } - - if ($request->query->has('oid')) { - $oid = $request->query->get('oid'); - $object = DataObject\Concrete::getById((int) $oid); - $class = $object->getClass(); - /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ - $fd = $class->getFieldDefinition($request->query->get('fieldname')); - $allowedGroupIds = $fd->getAllowedGroupIds(); - - if ($allowedGroupIds) { - $conditionParts[] = 'ID in (' . implode(',', $allowedGroupIds) . ')'; - } - } - - $condition = implode(' AND ', $conditionParts); - $list->setCondition($condition); - - $list->load(); - $configList = $list->getList(); - - $rootElement = []; - - $data = []; - foreach ($configList as $config) { - $name = $config->getName(); - if (!$name) { - $name = 'EMPTY'; - } - $item = [ - 'storeId' => $config->getStoreId(), - 'id' => $config->getId(), - 'name' => $name, - 'description' => $config->getDescription(), - ]; - if ($config->getCreationDate()) { - $item['creationDate'] = $config->getCreationDate(); - } - - if ($config->getModificationDate()) { - $item['modificationDate'] = $config->getModificationDate(); - } - - $data[] = $item; - } - $rootElement['data'] = $data; - $rootElement['success'] = true; - $rootElement['total'] = $list->getTotalCount(); - - return $this->adminJson($rootElement); + public function groupsActionGet( + Request $request, + GetGroupsHandler $handler, + #[MapQueryParameter] ?string $dir = null, + #[MapQueryParameter] ?string $sort = null, + #[MapQueryParameter] int $limit = 0, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] bool $overrideSort = false, + #[MapQueryParameter] ?string $searchfilter = null, + #[MapQueryParameter] int $storeId = 0, + #[MapQueryParameter] ?string $filter = null, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $oid = null, + #[MapQueryParameter] ?string $fieldname = null, + ): JsonResponse { + $result = $handler( + queryAll: $request->query->all(), + limit: $limit ?: 15, + start: $start, + dir: $dir, + sort: $sort, + overrideSort: $overrideSort, + searchfilter: $searchfilter, + storeId: $storeId, + filter: $filter, + oid: $oid, + fieldname: $fieldname, + ); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/groups', name: 'groupsaction', methods: ['POST', 'PUT'])] - public function groupsAction(Request $request): JsonResponse + public function groupsAction(Request $request, UpdateGroupHandler $handler): JsonResponse { - if ($request->request->has('data')) { - $dataParam = $request->request->get('data'); - $data = $this->decodeJson($dataParam); - - $id = $data['id']; - $config = Classificationstore\GroupConfig::getById($id); - - foreach ($data as $key => $value) { - if ($key !== 'id') { - $setter = 'set' . $key; - $config->$setter($value); - } - } - - $config->save(); - - return $this->adminJson(['success' => true, 'data' => $this->getConfigItem($config)]); + if (!$request->request->has('data')) { + throw new BadRequestHttpException(); } - return $this->adminJson(['success' => false]); + $data = $this->decodeJson($request->request->get('data')); + $result = $handler($data); + + return $this->adminJson(ApiResponse::ok(['data' => $result->item])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/collection-relations', name: 'collectionrelationsget', methods: ['GET'])] - public function collectionRelationsGetAction(Request $request): JsonResponse - { - $mapping = ['groupName' => 'name', 'groupDescription' => 'description']; - - $start = 0; - $limit = 15; - $orderKey = 'sorter'; - $order = 'ASC'; - - if ($request->query->has('dir')) { - $order = $request->query->get('dir'); - } - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->query->all()); - if ($sortingSettings['orderKey'] && $sortingSettings['order']) { - $orderKey = $sortingSettings['orderKey']; - $order = $sortingSettings['order']; - } - - if ($request->query->getBoolean('overrideSort')) { - $orderKey = 'id'; - $order = 'DESC'; - } - - if ($request->query->has('limit')) { - $limit = (int) $request->query->get('limit'); - } - if ($request->query->has('start')) { - $start = (int) $request->query->get('start'); - } - - $list = new Classificationstore\CollectionGroupRelation\Listing(); - - if ($limit > 0) { - $list->setLimit($limit); - } - $list->setOffset($start); - $list->setOrder($order); - $list->setOrderKey($mapping[$orderKey] ?? $orderKey); - $condition = ''; - - if ($request->query->has('filter')) { - $db = Db::get(); - $filterString = $request->query->get('filter'); - $filters = json_decode($filterString); - - $count = 0; - /** @var stdClass $f */ - foreach ($filters as $f) { - if (!isset($f->value)) { - continue; - } - - if ($count > 0) { - $condition .= ' AND '; - } - $count++; - $fieldname = $mapping[$f->field]; - $condition .= $db->quoteIdentifier($fieldname) . ' LIKE ' . $db->quote('%' . $f->value . '%'); - } - } - - $colId = $request->query->getInt('colId'); - if ($condition) { - $condition = '( ' . $condition . ' ) AND'; - } - $condition .= ' colId = ' . $list->quote($colId); - - $list->setCondition($condition); - - $listItems = $list->load(); - - $rootElement = []; - - $data = []; - foreach ($listItems as $config) { - $item = [ - 'colId' => $config->getColId(), - 'groupId' => $config->getGroupId(), - 'groupName' => $config->getName(), - 'groupDescription' => $config->getDescription(), - 'id' => $config->getColId() . '-' . $config->getGroupId(), - 'sorter' => $config->getSorter(), - ]; - $data[] = $item; - } - $rootElement['data'] = $data; - $rootElement['success'] = true; - $rootElement['total'] = $list->getTotalCount(); - - return $this->adminJson($rootElement); + public function collectionRelationsGetAction( + Request $request, + GetCollectionRelationsHandler $handler, + #[MapQueryParameter] ?string $dir = null, + #[MapQueryParameter] bool $overrideSort = false, + #[MapQueryParameter] int $limit = 0, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] ?string $filter = null, + #[MapQueryParameter] int $colId = 0, + ): JsonResponse { + $result = $handler( + queryAll: $request->query->all(), + limit: $limit ?: 15, + start: $start, + dir: $dir, + overrideSort: $overrideSort, + filter: $filter, + colId: $colId, + ); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/collection-relations', name: 'collectionrelations', methods: ['POST', 'PUT'])] - public function collectionRelationsAction(Request $request): JsonResponse + public function collectionRelationsAction(Request $request, SaveCollectionRelationsHandler $handler): JsonResponse { - if ($request->request->has('data')) { - $dataParam = $request->request->get('data'); - $data = $this->decodeJson($dataParam); - - if (count($data) === count($data, 1)) { - $data = [$data]; - } - - foreach ($data as &$row) { - $colId = $row['colId']; - $groupId = $row['groupId']; - $sorter = $row['sorter']; - - $config = new Classificationstore\CollectionGroupRelation(); - $config->setGroupId($groupId); - $config->setColId($colId); - $config->setSorter((int) $sorter); - - $config->save(); - - $row['id'] = $config->getColId() . '-' . $config->getGroupId(); - } - - return $this->adminJson(['success' => true, 'data' => $data]); + if (!$request->request->has('data')) { + throw new BadRequestHttpException(); } - return $this->adminJson(['success' => false]); + $data = $this->decodeJson($request->request->get('data')); + $result = $handler($data); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/list-stores', name: 'liststores', methods: ['GET'])] - public function listStoresAction(): JsonResponse + public function listStoresAction(ListStoresHandler $handler): JsonResponse { - $storeConfigs = []; - $storeConfigListing = new Classificationstore\StoreConfig\Listing(); - $storeConfigListing->load(); + $result = $handler(); - foreach ($storeConfigListing as $storeConfig) { - $storeConfigs[] = $storeConfig->getObjectVars(); - } - - return $this->adminJson($storeConfigs); + return $this->adminJson($result->storeConfigs); } #[Route('/search-relations', name: 'searchrelations', methods: ['GET'])] - public function searchRelationsAction(Request $request): JsonResponse - { - $db = Db::get(); - - $storeId = $request->query->get('storeId'); - - $mapping = [ - 'groupName' => DataObject\Classificationstore\GroupConfig\Dao::TABLE_NAME_GROUPS .'.name', - 'keyName' => DataObject\Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS .'.name', - 'keyDescription' => DataObject\Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS. '.description', - ]; - - $start = 0; - $limit = 15; - $orderKey = 'name'; - $order = 'ASC'; - - if ($request->query->get('dir')) { - $order = $request->query->get('dir'); - } - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->query->all()); - if ($sortingSettings['orderKey'] && $sortingSettings['order']) { - $orderKey = $sortingSettings['orderKey']; - if ($orderKey === 'keyName') { - $orderKey = 'name'; - } - $order = $sortingSettings['order']; - } - - if ($request->query->getBoolean('overrideSort')) { - $orderKey = 'id'; - $order = 'DESC'; - } - - if ($request->query->has('limit')) { - $limit = (int) $request->query->get('limit'); - } - if ($request->query->has('start')) { - $start = (int) $request->query->get('start'); - } - - $list = new Classificationstore\KeyGroupRelation\Listing(); - - if ($limit > 0) { - $list->setLimit($limit); - } - $list->setOffset($start); - $list->setOrder($order); - $list->setOrderKey($orderKey); - - $conditionParts = []; - - if ($request->query->has('filter')) { - $db = Db::get(); - $filterString = $request->query->get('filter'); - $filters = json_decode($filterString); - /** @var stdClass $f */ - foreach ($filters as $f) { - if (!isset($f->value)) { - continue; - } - - $fieldname = $mapping[$f->property]; - $conditionParts[] = $fieldname . ' LIKE ' . $db->quote('%' . $f->value . '%'); - } - } - - $conditionParts[] = ' groupId IN (select id from classificationstore_groups where storeId = ' . $db->quote($storeId) . ')'; - - $searchfilter = $request->query->get('searchfilter'); - if ($searchfilter) { - $searchFilterConditions = []; - - $searchTerms = [$searchfilter, ...$this->getTranslatedSearchFilterTerms($searchfilter)]; - foreach ($searchTerms as $searchFilterTerm) { - $searchFilterConditions[] = Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS.'.name LIKE '.$db->quote('%'.$searchFilterTerm.'%') - .' OR '.Classificationstore\GroupConfig\Dao::TABLE_NAME_GROUPS.'.name LIKE '.$db->quote('%'.$searchFilterTerm.'%') - .' OR '.Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS.'.description LIKE '.$db->quote('%'.$searchFilterTerm.'%'); - } - - $conditionParts[] = '('.implode(' OR ', $searchFilterConditions).')'; - } - - $condition = implode(' AND ', $conditionParts); - $list->setCondition($condition); - $list->setResolveGroupName(true); - - $rootElement = []; - - $data = []; - foreach ($list->getList() as $config) { - $item = [ - 'keyId' => $config->getKeyId(), - 'groupId' => $config->getGroupId(), - 'keyName' => $config->getName(), - 'keyDescription' => $config->getDescription(), - 'id' => $config->getGroupId() . '-' . $config->getKeyId(), - 'sorter' => $config->getSorter(), - ]; - - $groupConfig = Classificationstore\GroupConfig::getById($config->getGroupId()); - if ($groupConfig) { - $item['groupName'] = $groupConfig->getName(); - } - - $data[] = $item; - } - $rootElement['data'] = $data; - $rootElement['success'] = true; - $rootElement['total'] = $list->getTotalCount(); - - return $this->adminJson($rootElement); + public function searchRelationsAction( + Request $request, + SearchRelationsHandler $handler, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $storeId = null, + #[MapQueryParameter] ?string $dir = null, + #[MapQueryParameter] bool $overrideSort = false, + #[MapQueryParameter] int $limit = 0, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] ?string $filter = null, + #[MapQueryParameter] ?string $searchfilter = null, + ): JsonResponse { + $result = $handler( + queryAll: $request->query->all(), + storeId: $storeId, + limit: $limit ?: 15, + start: $start, + dir: $dir, + overrideSort: $overrideSort, + filter: $filter, + searchfilter: $searchfilter, + ); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[Route('/relations', name: 'relationsactionget', methods: ['GET'])] - public function relationsActionGet(Request $request): JsonResponse - { - $mapping = ['keyName' => 'name', 'keyDescription' => 'description']; - - $start = 0; - $limit = 15; - $orderKey = 'name'; - $order = 'ASC'; - $relationIds = $request->query->get('relationIds'); - - if ($relationIds) { - $relationIds = json_decode($relationIds, true); - } - - if ($request->query->has('dir')) { - $order = $request->query->get('dir'); - } - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->query->all()); - - if ($sortingSettings['orderKey'] && $sortingSettings['order']) { - $orderKey = $mapping[$sortingSettings['orderKey']] ?? $sortingSettings['orderKey']; - $order = $sortingSettings['order']; - } - - if ($request->query->getBoolean('overrideSort')) { - $orderKey = 'id'; - $order = 'DESC'; - } - - if ($request->query->has('limit')) { - $limit = (int) $request->query->get('limit'); - } elseif (is_array($relationIds)) { - $limit = count($relationIds); - } - - if ($request->query->has('start')) { - $start = (int) $request->query->get('start'); - } - - $list = new Classificationstore\KeyGroupRelation\Listing(); - - if ($limit > 0) { - $list->setLimit($limit); - } - - $list->setOffset($start); - $list->setOrder($order); - $list->setOrderKey($orderKey); - $conditionParts = []; - - if ($request->query->has('filter')) { - $db = Db::get(); - $filterString = $request->query->get('filter'); - $filters = json_decode($filterString); - /** @var stdClass $f */ - foreach ($filters as $f) { - if (!isset($f->value)) { - continue; - } - - $fieldname = $mapping[$f->field]; - $conditionParts[] = $db->quoteIdentifier($fieldname) . ' LIKE ' . $db->quote('%' . $f->value . '%'); - } - } - - if (!$request->query->has('relationIds')) { - $groupId = $request->query->get('groupId'); - $conditionParts[] = ' groupId = ' . $list->quote($groupId); - } - - if ($relationIds) { - $relationParts = []; - - foreach ($relationIds as $relationId) { - $keyId = $relationId['keyId']; - $groupId = $relationId['groupId']; - $relationParts[] = '(keyId = ' . $list->quote($keyId) . ' AND groupId = ' . $list->quote($groupId) . ')'; - } - - $conditionParts[] = '(' . implode(' OR ', $relationParts) . ')'; - } - - $condition = implode(' AND ', $conditionParts); - - $list->setCondition($condition); - - $listItems = $list->load(); - - $rootElement = []; - - $data = []; - foreach ($listItems as $config) { - $type = $config->getType(); - $definition = json_decode($config->getDefinition(), true); - $definition = \OpenDxp\Model\DataObject\Classificationstore\Service::getFieldDefinitionFromJson($definition, $type); - DataObject\Service::enrichLayoutDefinition($definition); - - $item = [ - 'keyId' => $config->getKeyId(), - 'groupId' => $config->getGroupId(), - 'keyName' => $config->getName(), - 'keyDescription' => $config->getDescription(), - 'id' => $config->getGroupId() . '-' . $config->getKeyId(), - 'sorter' => $config->getSorter(), - 'layout' => $definition, - 'mandatory' => $config->isMandatory(), - ]; - - $data[] = $item; - } - $rootElement['data'] = $data; - $rootElement['success'] = true; - $rootElement['total'] = $list->getTotalCount(); - - return $this->adminJson($rootElement); + public function relationsActionGet( + Request $request, + GetRelationsHandler $handler, + #[MapQueryParameter] ?string $relationIds = null, + #[MapQueryParameter] ?string $dir = null, + #[MapQueryParameter] bool $overrideSort = false, + #[MapQueryParameter] int $limit = 0, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] ?string $filter = null, + #[MapQueryParameter] ?string $groupId = null, + ): JsonResponse { + $result = $handler( + queryAll: $request->query->all(), + relationIds: $relationIds, + limit: $limit ?: 15, + start: $start, + dir: $dir, + overrideSort: $overrideSort, + filter: $filter, + groupId: $groupId, + ); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/relations', name: 'relations', methods: ['POST', 'PUT'])] - public function relationsAction(Request $request): JsonResponse + public function relationsAction(Request $request, SaveRelationHandler $handler): JsonResponse { - if ($request->request->has('data')) { - $dataParam = $request->request->get('data'); - $data = $this->decodeJson($dataParam); - - $keyId = $data['keyId']; - $groupId = $data['groupId']; - $sorter = $data['sorter']; - $mandatory = $data['mandatory']; - - $config = new Classificationstore\KeyGroupRelation(); - $config->setGroupId((int) $groupId); - $config->setKeyId((int) $keyId); - $config->setSorter($sorter); - $config->setMandatory($mandatory); - - $config->save(); - $data['id'] = $config->getGroupId() . '-' . $config->getKeyId(); - - return $this->adminJson(['success' => true, 'data' => $data]); + if (!$request->request->has('data')) { + throw new BadRequestHttpException(); } - return $this->adminJson(['success' => false]); + $data = $this->decodeJson($request->request->get('data')); + $result = $handler($data); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Objects->value)] #[Route('/add-collections', name: 'addcollections', methods: ['POST'])] - public function addCollectionsAction(Request $request): JsonResponse + public function addCollectionsAction(Request $request, AddCollectionsHandler $handler): JsonResponse { - $this->checkPermission('objects'); - $ids = $this->decodeJson($request->request->get('collectionIds')); - $data = []; - - if ($ids) { - $db = \OpenDxp\Db::get(); - $mappedData = []; - $groupsData = $db->fetchAllAssociative( - 'SELECT * FROM classificationstore_groups g, classificationstore_collectionrelations c - WHERE colId IN (?) AND g.id = c.groupId', - [array_values(array_filter($ids, is_numeric(...)))], - [ArrayParameterType::INTEGER] - ); - - foreach ($groupsData as $groupData) { - $mappedData[$groupData['id']] = $groupData; - } - - $groupIdList = []; - $groupId = null; - - $allowedGroupIds = null; - - $oid = $request->request->getInt('oid'); - $object = DataObject\Concrete::getById($oid); - if ($object) { - $class = $object->getClass(); - /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ - $fd = $class->getFieldDefinition($request->request->get('fieldname')); - $allowedGroupIds = $fd->getAllowedGroupIds(); - } - - foreach ($groupsData as $groupItem) { - $groupId = $groupItem['groupId']; - if (!$allowedGroupIds || in_array($groupId, $allowedGroupIds)) { - $groupIdList[] = $groupId; - } - } - - if ($groupIdList) { - $fieldname = $request->request->get('fieldname'); - $groupList = new Classificationstore\GroupConfig\Listing(); - $groupCondition = 'id in (' . implode(',', $groupIdList) . ')'; - $groupList->setCondition($groupCondition); - - $groupList = $groupList->load(); - - $keyCondition = 'groupId in (' . implode(',', $groupIdList) . ')'; - - $keyList = new Classificationstore\KeyGroupRelation\Listing(); - $keyList->setCondition($keyCondition); - $keyList->setOrderKey(['sorter', 'id']); - $keyList->setOrder(['ASC', 'ASC']); - $keyList = $keyList->load(); + $result = $handler( + ids: $ids ?: [], + oid: $request->request->getInt('oid'), + fieldname: $request->request->get('fieldname') ?? '', + ); - foreach ($groupList as $groupData) { - $data[$groupData->getId()] = [ - 'name' => $groupData->getName(), - 'id' => $groupData->getId(), - 'description' => $groupData->getDescription(), - 'keys' => [], - 'sorter' => (int) $mappedData[$groupData->getId()]['sorter'], - 'collectionId' => $mappedData[$groupId]['colId'], - ]; - } - - foreach ($keyList as $keyData) { - $groupId = $keyData->getGroupId(); - - $keyList = $data[$groupId]['keys']; - $type = $keyData->getType(); - $definition = json_decode($keyData->getDefinition(), true); - $definition = \OpenDxp\Model\DataObject\Classificationstore\Service::getFieldDefinitionFromJson($definition, $type); - - if (method_exists($definition, '__wakeup')) { - $definition->__wakeup(); - } - - $context['object'] = $object; - $context['class'] = $object ? $object->getClass() : null; - $context['ownerType'] = 'classificationstore'; - $context['ownerName'] = $fieldname; - $context['keyId'] = $keyData->getKeyId(); - $context['groupId'] = $groupId; - $context['keyDefinition'] = $definition; - - if ($definition instanceof LayoutDefinitionEnrichmentInterface) { - $definition = $definition->enrichLayoutDefinition($object, $context); - } - - $keyList[] = [ - 'name' => $keyData->getName(), - 'id' => $keyData->getKeyId(), - 'description' => $keyData->getDescription(), - 'definition' => $definition, - ]; - $data[$groupId]['keys'] = $keyList; - } - } - } - - return $this->adminJson($data); + return $this->adminJson($result->data); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Objects->value)] #[Route('/add-groups', name: 'addgroups', methods: ['POST'])] - public function addGroupsAction(Request $request): JsonResponse + public function addGroupsAction(Request $request, AddGroupsHandler $handler): JsonResponse { - $this->checkPermission('objects'); - $ids = $this->decodeJson($request->request->get('groupIds')); - $oid = $request->request->getInt('oid'); - $object = $oid === 0 ? null : DataObject\Concrete::getById($oid); - $fieldname = $request->request->get('fieldname'); - - $keyCondition = 'groupId in (' . implode(',', array_fill(0, count($ids), '?')) . ')'; - - $keyList = new Classificationstore\KeyGroupRelation\Listing(); - $keyList->setCondition($keyCondition, $ids); - $keyList->setOrderKey(['sorter', 'id']); - $keyList->setOrder(['ASC', 'ASC']); - $keyList = $keyList->load(); + $result = $handler( + ids: $ids, + oid: $request->request->getInt('oid'), + fieldname: $request->request->get('fieldname'), + ); - $groupCondition = 'id in (' . implode(',', array_fill(0, count($ids), '?')) . ')'; - - $groupList = new Classificationstore\GroupConfig\Listing(); - $groupList->setCondition($groupCondition, $ids); - $groupList->setOrder('ASC'); - $groupList->setOrderKey('id'); - $groupList = $groupList->load(); - - $data = []; - - foreach ($groupList as $groupData) { - $data[$groupData->getId()] = [ - 'name' => $groupData->getName(), - 'id' => $groupData->getId(), - 'description' => $groupData->getDescription(), - 'keys' => [], - ]; - } - - foreach ($keyList as $keyData) { - $groupId = $keyData->getGroupId(); - - $keyList = $data[$groupId]['keys']; - $type = $keyData->getType(); - $definition = json_decode($keyData->getDefinition(), true); - $definition = \OpenDxp\Model\DataObject\Classificationstore\Service::getFieldDefinitionFromJson($definition, $type); - - if (method_exists($definition, '__wakeup')) { - $definition->__wakeup(); - } - - $context['object'] = $object; - $context['class'] = $object ? $object->getClass() : null; - $context['ownerType'] = 'classificationstore'; - $context['ownerName'] = $fieldname; - $context['keyId'] = $keyData->getKeyId(); - $context['groupId'] = $groupId; - $context['keyDefinition'] = $definition; - - if ($definition instanceof LayoutDefinitionEnrichmentInterface) { - $definition = $definition->enrichLayoutDefinition($object, $context); - } - - $keyList[] = [ - 'name' => $keyData->getName(), - 'id' => $keyData->getKeyId(), - 'description' => $keyData->getDescription(), - 'definition' => $definition, - ]; - $data[$groupId]['keys'] = $keyList; - } - - return $this->adminJson($data); + return $this->adminJson($result->data); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/properties', name: 'propertiesget', methods: ['GET'])] - public function propertiesGetAction(Request $request): JsonResponse - { - $storeId = (int) $request->query->get('storeId'); - $frameName = $request->query->get('frameName'); - $db = \OpenDxp\Db::get(); - - $conditionParts = []; - - if ($frameName) { - $keyCriteria = ' FALSE '; - $frameConfig = Classificationstore\CollectionConfig::getByName($frameName, $storeId); - if ($frameConfig) { - // get all keys within that collection / frame - $frameId = $frameConfig->getId(); - $groupList = new Classificationstore\CollectionGroupRelation\Listing(); - $groupList->setCondition('colId = ' . $db->quote($frameId)); - $groupList = $groupList->load(); - $groupIdList = []; - foreach ($groupList as $groupEntry) { - $groupIdList[] = $groupEntry->getGroupId(); - } - - if ($groupIdList) { - $keyIdList = new Classificationstore\KeyGroupRelation\Listing(); - $keyIdList->setCondition('groupId in (' . implode(',', $groupIdList) . ')'); - $keyIdList = $keyIdList->load(); - if ($keyIdList) { - $keyIds = []; - foreach ($keyIdList as $keyEntry) { - $keyIds[] = $keyEntry->getKeyId(); - } - - $keyCriteria = ' id in (' . implode(',', $keyIds) . ')'; - } - } - } - - $conditionParts[] = $keyCriteria; - } - - $start = 0; - $limit = 15; - $orderKey = 'name'; - $order = 'ASC'; - - if ($request->query->has('dir')) { - $order = $request->query->get('dir'); - } - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->query->all()); - if ($sortingSettings['orderKey'] && $sortingSettings['order']) { - $orderKey = $sortingSettings['orderKey']; - $order = $sortingSettings['order']; - } - - if ($request->query->getBoolean('overrideSort')) { - $orderKey = 'id'; - $order = 'DESC'; - } - - if ($request->query->has('limit')) { - $limit = (int) $request->query->get('limit'); - } - if ($request->query->has('start')) { - $start = (int) $request->query->get('start'); - } - - $list = new Classificationstore\KeyConfig\Listing(); - - if ($limit > 0 && !$request->query->get('groupIds') && !$request->query->get('keyIds')) { - $list->setLimit($limit); - } - $list->setOffset($start); - $list->setOrder($order); - $list->setOrderKey($orderKey); - - $searchfilter = $request->query->get('searchfilter'); - if ($searchfilter) { - $conditionParts[] = '(name LIKE ' . $db->quote('%' . $searchfilter . '%') . ' OR description LIKE ' . $db->quote('%'. $searchfilter . '%') . ')'; - } - - if ($storeId) { - $conditionParts[] = '(storeId = '. $db->quote($storeId) . ')'; - } - - if ($request->query->has('filter')) { - $filterString = $request->query->get('filter'); - $filters = json_decode($filterString); - /** @var stdClass $f */ - foreach ($filters as $f) { - if (!isset($f->value)) { - continue; - } - - $conditionParts[] = $db->quoteIdentifier($f->property) . ' LIKE ' . $db->quote('%' . $f->value . '%'); - } - } - $condition = implode(' AND ', $conditionParts); - $list->setCondition($condition); - - if ($request->query->get('groupIds') || $request->query->get('keyIds')) { - $db = Db::get(); - - if ($request->query->get('groupIds')) { - $ids = $this->decodeJson($request->query->get('groupIds')); - $col = 'group'; - } else { - $ids = $this->decodeJson($request->query->get('keyIds')); - $col = 'id'; - } - - $condition = $db->quoteIdentifier($col) . ' IN ('; - $count = 0; - foreach ($ids as $theId) { - if ($count > 0) { - $condition .= ','; - } - $condition .= $theId; - $count++; - } - - $condition .= ')'; - $list->setCondition($condition); - } - - $list->load(); - $configList = $list->getList(); - - $rootElement = []; - - $data = []; - foreach ($configList as $config) { - $item = $this->getKeyConfigItem($config); - $data[] = $item; - } - $rootElement['data'] = $data; - $rootElement['success'] = true; - $rootElement['total'] = $list->getTotalCount(); - - return $this->adminJson($rootElement); + public function propertiesGetAction( + Request $request, + GetPropertiesHandler $handler, + #[MapQueryParameter] int $storeId = 0, + #[MapQueryParameter] ?string $frameName = null, + #[MapQueryParameter] ?string $dir = null, + #[MapQueryParameter] bool $overrideSort = false, + #[MapQueryParameter] int $limit = 0, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] ?string $groupIds = null, + #[MapQueryParameter] ?string $keyIds = null, + #[MapQueryParameter] ?string $searchfilter = null, + #[MapQueryParameter] ?string $filter = null, + ): JsonResponse { + $result = $handler( + queryAll: $request->query->all(), + storeId: $storeId, + frameName: $frameName, + limit: $limit ?: 15, + start: $start, + dir: $dir, + overrideSort: $overrideSort, + groupIds: $groupIds, + keyIds: $keyIds, + searchfilter: $searchfilter, + filter: $filter, + ); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/properties', name: 'properties', methods: ['POST', 'PUT'])] - public function propertiesAction(Request $request): JsonResponse + public function propertiesAction(Request $request, UpdatePropertyHandler $handler): JsonResponse { - if ($request->request->has('data')) { - $dataParam = $request->request->get('data'); - $data = $this->decodeJson($dataParam); - - $id = $data['id']; - $config = Classificationstore\KeyConfig::getById($id); - - foreach ($data as $key => $value) { - if ($key !== 'id') { - $setter = 'set' . $key; - if (method_exists($config, $setter)) { - $config->$setter($value); - } - } - } - - $config->save(); - $item = $this->getKeyConfigItem($config); - - return $this->adminJson(['success' => true, 'data' => $item]); + if (!$request->request->has('data')) { + throw new BadRequestHttpException(); } - return $this->adminJson(['success' => false]); - } - - protected function getConfigItem(Classificationstore\KeyConfig|Classificationstore\CollectionConfig|Classificationstore\GroupConfig $config): array - { - $name = $config->getName(); - - $item = [ - 'storeId' => $config->getStoreId(), - 'id' => $config->getId(), - 'name' => $name, - 'description' => $config->getDescription(), - ]; - - if ($config->getCreationDate()) { - $item['creationDate'] = $config->getCreationDate(); - } - - if ($config->getModificationDate()) { - $item['modificationDate'] = $config->getModificationDate(); - } - - return $item; - } - - protected function getKeyConfigItem(Classificationstore\KeyConfig $config): array - { - $item = $this->getConfigItem($config); - $item['type'] = $config->getType() ?: 'input'; - $definition = $config->getDefinition(); - $item['definition'] = $definition; - - if ($definition) { - $definition = json_decode($definition, true); - if ($definition) { - $item['title'] = $definition['title']; - } - } + $data = $this->decodeJson($request->request->get('data')); + $result = $handler($data); - return $item; + return $this->adminJson(ApiResponse::ok(['data' => $result->item])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/add-property', name: 'addproperty', methods: ['POST'])] - public function addPropertyAction(Request $request): JsonResponse + public function addPropertyAction(Request $request, AddPropertyHandler $handler): JsonResponse { - $name = $request->request->get('name'); - $storeId = $request->request->getInt('storeId'); + $result = $handler( + name: $request->request->get('name'), + storeId: $request->request->getInt('storeId'), + ); - $definition = [ - 'fieldtype' => 'input', - 'name' => $name, - 'title' => $name, - 'datatype' => 'data', - ]; - - $config = new Classificationstore\KeyConfig(); - $config->setName($name); - $config->setTitle($name); - $config->setType('input'); - $config->setStoreId($storeId); - $config->setEnabled(true); - $config->setDefinition(json_encode($definition)); - $config->save(); - - return $this->adminJson(['success' => true, 'id' => $config->getName()]); + return $this->adminJson(ApiResponse::ok(['id' => $result->name])); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-property', name: 'deleteproperty', methods: ['DELETE'])] - public function deletePropertyAction(Request $request): JsonResponse + public function deletePropertyAction(Request $request, DeletePropertyHandler $handler): JsonResponse { - $id = $request->request->getInt('id'); + $handler($request->request->getInt('id')); - $config = Classificationstore\KeyConfig::getById($id); - // $config->delete(); - $config->setEnabled(false); - $config->save(); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/edit-store', name: 'editstore', methods: ['PUT'])] - public function editStoreAction(Request $request): JsonResponse + public function editStoreAction(Request $request, EditStoreHandler $handler): JsonResponse { $id = $request->request->getInt('id'); $data = json_decode($request->request->get('data'), true); - $name = $data['name']; - if (!$name) { - throw new Exception('Name must not be empty'); - } - - $description = $data['description']; - - $config = Classificationstore\StoreConfig::getByName($name); - if ($config && $config->getId() != $id) { - throw new Exception('There is already a config with the same name'); - } - $config = Classificationstore\StoreConfig::getById($id); + $handler( + id: $id, + name: $data['name'], + description: $data['description'], + ); - if (!$config) { - throw new Exception('Configuration does not exist'); - } - - $config->setName($name); - $config->setDescription($description); - $config->save(); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/storetree', name: 'storetree', methods: ['GET'])] - public function storetreeAction(Request $request): JsonResponse + public function storetreeAction(GetStoreTreeHandler $handler): JsonResponse { - $result = []; - $list = new Classificationstore\StoreConfig\Listing(); - $list = $list->load(); - foreach ($list as $item) { - $resultItem = [ - 'id' => $item->getId(), - 'text' => htmlspecialchars($item->getName() ?? '', ENT_QUOTES), - 'expandable' => false, - 'leaf' => true, - 'expanded' => true, - 'description' => htmlspecialchars($item->getDescription() ?? '', ENT_QUOTES), - 'iconCls' => 'opendxp_icon_classificationstore', - ]; - - $resultItem['qtitle'] = 'ID: ' . $item->getId(); - - if ($item->getDescription()) { - } - $resultItem['qtip'] = $item->getDescription() ? htmlspecialchars($item->getDescription(), ENT_QUOTES) : ' '; - $result[] = $resultItem; - } + $result = $handler(); - return $this->adminJson($result); + return $this->adminJson($result->items); } + #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/get-page', name: 'getpage', methods: ['GET'])] - public function getPageAction(Request $request): JsonResponse - { - $tableSuffix = $request->query->get('table'); - if (!ArrayHelper::inArrayCaseInsensitive($tableSuffix, ['keys', 'groups'])) { - $tableSuffix = 'keys'; - } - - $table = 'classificationstore_' . $tableSuffix; - $db = \OpenDxp\Db::get(); - $id = (int) $request->query->get('id'); - $storeId = (int) $request->query->get('storeId'); - $pageSize = (int) $request->query->get('pageSize'); - - if ($request->query->get('sortKey')) { - $sortKey = $request->query->get('sortKey'); - $sortDir = $request->query->get('sortDir'); - } else { - $sortKey = 'name'; - $sortDir = 'ASC'; - } - - if (!ArrayHelper::inArrayCaseInsensitive($sortDir, ['DESC', 'ASC'])) { - $sortDir = 'DESC'; - } - - if (!ArrayHelper::inArrayCaseInsensitive($sortKey, ['name', 'title', 'description', 'id', 'type', 'creationDate', 'modificationDate', 'enabled', 'parentId', 'storeId'])) { - $sortKey = 'name'; - } - - $sorter = ' order by `' . $sortKey . '` ' . $sortDir; - - if ($table === 'keys') { - $query = ' - select *, (item.pos - 1)/ ' . $pageSize . ' + 1 as page from ( - select * from ( - select @rownum := @rownum + 1 as pos, id, name, `type` - from `' . $table . '` - where enabled = 1 and storeId = ' . $storeId . $sorter . ' - ) all_rows) item where id = ' . $id . ';'; - } else { - $query = ' - select *, (item.pos - 1)/ ' . $pageSize . ' + 1 as page from ( - select * from ( - select @rownum := @rownum + 1 as pos, id, name - from `' . $table . '` - where storeId = ' . $storeId . $sorter . ' - ) all_rows) item where id = ' . $id . ';'; - } - - $db->executeStatement('SET @rownum = 0'); - $result = $db->fetchAllAssociative($query); - - $page = (int) $result[0]['page'] ; - - return $this->adminJson(['success' => true, 'page' => $page]); + public function getPageAction( + GetPageHandler $handler, + #[MapQueryParameter] ?string $table = null, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] int $storeId = 0, + #[MapQueryParameter] int $pageSize = 0, + #[MapQueryParameter] ?string $sortKey = null, + #[MapQueryParameter] ?string $sortDir = null, + ): JsonResponse { + $result = $handler( + table: $table, + id: $id, + storeId: $storeId, + pageSize: $pageSize, + sortKey: $sortKey, + sortDir: $sortDir, + ); + + return $this->adminJson(ApiResponse::ok(['page' => $result->page])); } - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - $unrestrictedActions = [ - 'collectionsActionGet', - 'groupsActionGet', - 'relationsActionGet', - 'addGroupsAction', - 'addCollectionsAction', - 'searchRelationsAction', - ]; - $this->checkActionPermission($event, 'classificationstore', $unrestrictedActions); - } - - /** - * @return string[] - */ - private function getTranslatedSearchFilterTerms(string $searchTerm): array - { - $terms = []; - - $user = Admin::getCurrentUser(); - if ($user instanceof User) { - $translationListing = new Listing(); - $translationListing->setDomain(Translation::DOMAIN_ADMIN); - $translationListing->setCondition( - $translationListing->quoteIdentifier('language') . ' = ? AND ' . - $translationListing->quoteIdentifier('text') . ' LIKE ?', - [ - $user->getLanguage(), - '%' . $searchTerm . '%', - ] - ); - - foreach ($translationListing as $translation) { - $terms[] = $translation->getKey(); - } - } - - return $terms; - } } diff --git a/src/Controller/Admin/DataObject/CustomLayoutController.php b/src/Controller/Admin/DataObject/CustomLayoutController.php new file mode 100644 index 00000000..07c11a07 --- /dev/null +++ b/src/Controller/Admin/DataObject/CustomLayoutController.php @@ -0,0 +1,153 @@ +value)] +class CustomLayoutController extends AdminAbstractController +{ + #[Route('/get-custom-layout', name: 'getcustomlayout', methods: ['GET'])] + public function getCustomLayoutAction(GetCustomLayoutHandler $getCustomLayout, #[MapQueryParameter] string $id): JsonResponse + { + $result = $getCustomLayout($id); + $data = $result->data; + $data['isWriteable'] = $result->isWriteable; + + return $this->adminJson(ApiResponse::ok(['data' => $data])); + } + + #[Route('/add-custom-layout', name: 'addcustomlayout', methods: ['POST'])] + public function addCustomLayoutAction(AddCustomLayoutHandler $addCustomLayout, Request $request): JsonResponse + { + $customLayout = $addCustomLayout( + (string) $request->request->get('layoutIdentifier'), + (string) $request->request->get('layoutName'), + (string) $request->request->get('classId'), + ); + + $data = $customLayout->getObjectVars(); + $data['isWriteable'] = $customLayout->isWriteable(); + + return $this->adminJson(ApiResponse::ok(['id' => $customLayout->getId(), 'name' => $customLayout->getName(), 'data' => $data])); + } + + #[Route('/save-custom-layout', name: 'savecustomlayout', methods: ['PUT'])] + public function saveCustomLayoutAction(SaveCustomLayoutHandler $saveCustomLayout, Request $request): JsonResponse + { + $customLayout = $saveCustomLayout( + (string) $request->request->get('id'), + $this->decodeJson($request->request->get('configuration')), + $this->decodeJson($request->request->get('values')), + ); + + return $this->adminJson(ApiResponse::ok(['id' => $customLayout->getId(), 'data' => $customLayout->getObjectVars()])); + } + + #[Route('/delete-custom-layout', name: 'deletecustomlayout', methods: ['DELETE'])] + public function deleteCustomLayoutAction(DeleteCustomLayoutHandler $deleteCustomLayout, Request $request): JsonResponse + { + $deleteCustomLayout((string) $request->request->get('id')); + + return $this->adminJson(ApiResponse::ok()); + } + + #[Route('/import-custom-layout-definition', name: 'importcustomlayoutdefinition', methods: ['POST', 'PUT'])] + public function importCustomLayoutDefinitionAction( + ImportCustomLayoutHandler $importCustomLayout, + Request $request, + #[MapQueryParameter] ?string $id = null, + ): Response { + /** @var UploadedFile $file */ + $file = $request->files->get('Filedata'); + $importData = $this->decodeJson(file_get_contents($file->getPathname())); + + if (isset($importData['name']) && DataObject\ClassDefinition\CustomLayout::getByName($importData['name']) instanceof DataObject\ClassDefinition\CustomLayout) { + $response = $this->adminJson(ApiResponse::error(null, ['nameAlreadyInUse' => true])); + $response->headers->set('Content-Type', 'text/html'); + + return $response; + } + + $importCustomLayout($id, $importData); + + $response = $this->adminJson(ApiResponse::ok()); + $response->headers->set('Content-Type', 'text/html'); + + return $response; + } + + #[Route('/export-custom-layout-definition', name: 'exportcustomlayoutdefinition', methods: ['GET'])] + public function exportCustomLayoutDefinitionAction(ExportCustomLayoutHandler $exportCustomLayout, #[MapQueryParameter] ?string $id = null): Response + { + $result = $exportCustomLayout($id); + + $response = new Response($result->json); + $response->headers->set('Content-type', 'application/json'); + $response->headers->set('Content-Disposition', 'attachment; filename: "custom_definition_' . $result->name . '_export.json"'); + + return $response; + } + + #[Route('/get-custom-layout-definitions', name: 'getcustomlayoutdefinitions', methods: ['GET'])] + public function getCustomLayoutDefinitionsAction(GetCustomLayoutDefinitionsHandler $getDefinitions, #[MapQueryParameter] string $classId): JsonResponse + { + return $this->adminJson(ApiResponse::ok(['data' => $getDefinitions($classId)->definitions])); + } + + #[Route('/get-all-layouts', name: 'getalllayouts', methods: ['GET'])] + public function getAllLayoutsAction(GetAllLayoutsHandler $getAllLayouts): JsonResponse + { + return $this->adminJson(['data' => $getAllLayouts()->layouts]); + } + + #[Route('/suggest-custom-layout-identifier', name: 'suggestcustomlayoutidentifier', methods: ['GET'])] + public function suggestCustomLayoutIdentifierAction(SuggestCustomLayoutIdentifierHandler $suggestIdentifier, #[MapQueryParameter] string $classId): Response + { + $result = $suggestIdentifier($classId); + + return $this->adminJson([ + 'suggestedIdentifier' => $result->suggestedIdentifier, + 'existingIds' => $result->existingIds, + 'existingNames' => $result->existingNames, + ]); + } +} diff --git a/src/Controller/Admin/DataObject/DataObjectController.php b/src/Controller/Admin/DataObject/DataObjectController.php index 313c2749..5ebd4252 100644 --- a/src/Controller/Admin/DataObject/DataObjectController.php +++ b/src/Controller/Admin/DataObject/DataObjectController.php @@ -13,60 +13,55 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ + namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; -use Exception; use OpenDxp\Bundle\AdminBundle\Controller\Admin\ElementControllerBase; -use OpenDxp\Bundle\AdminBundle\Controller\Traits\AdminStyleTrait; -use OpenDxp\Bundle\AdminBundle\Controller\Traits\ApplySchedulerDataTrait; -use OpenDxp\Bundle\AdminBundle\Controller\Traits\UserNameTrait; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObjectFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ChangeChildrenSortByHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\DeleteDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\DataObjectGridProxyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenByIdHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrlHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\UpdateDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; +use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Event\ElementAdminStyleEvent; -use OpenDxp\Bundle\AdminBundle\Helper\GridHelperService; use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler; -use OpenDxp\Controller\KernelControllerEventInterface; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; +use Symfony\Component\Security\Http\Attribute\IsGranted; use OpenDxp\Controller\Traits\ElementEditLockHelperTrait; use OpenDxp\Db; -use OpenDxp\Localization\LocaleServiceInterface; -use OpenDxp\Logger; -use OpenDxp\Model; use OpenDxp\Model\DataObject; -use OpenDxp\Model\DataObject\ClassDefinition\Data\ManyToManyObjectRelation; -use OpenDxp\Model\DataObject\ClassDefinition\Data\Relations\AbstractRelations; -use OpenDxp\Model\DataObject\ClassDefinition\Data\ReverseObjectRelation; -use OpenDxp\Model\DataObject\ClassDefinition\Helper\OptionsProviderResolver; -use OpenDxp\Model\DataObject\ClassDefinition\PreviewGeneratorInterface; use OpenDxp\Model\Element; use OpenDxp\Model\Element\ElementInterface; -use OpenDxp\Model\Element\Service; -use OpenDxp\Model\Schedule\Task; -use OpenDxp\Tool; use Override; -use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Throwable; -use Twig\Environment; -use Twig\Extension\CoreExtension; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal */ #[Route('/object', name: 'opendxp_admin_dataobject_dataobject_')] -class DataObjectController extends ElementControllerBase implements KernelControllerEventInterface +#[IsGranted(CorePermission::Objects->value)] +class DataObjectController extends ElementControllerBase { - use AdminStyleTrait; use ElementEditLockHelperTrait; - use ApplySchedulerDataTrait; - use DataObjectActionsTrait; - use UserNameTrait; /** On active edit lock answer with editlock response */ public const string TASK_RESPONSE = 'response'; @@ -77,163 +72,44 @@ class DataObjectController extends ElementControllerBase implements KernelContro /** On active edit lock keep existing entry */ public const string TASK_KEEP = 'keep'; - protected DataObject\Service $_objectService; - - private array $objectData = []; - - private array $metaData = []; - - private array $classFieldDefinitions = []; + public function __construct( + ElementServiceInterface $elementService, + private readonly SessionService $sessionService, + ) { + parent::__construct($elementService); + } #[Route('/tree-get-children-by-id', name: 'treegetchildrenbyid', methods: ['GET'])] - public function treeGetChildrenByIdAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $filter = $request->query->get('filter'); - $object = DataObject::getById((int) $request->query->get('node')); - $objectTypes = [DataObject::OBJECT_TYPE_OBJECT, DataObject::OBJECT_TYPE_FOLDER]; - $objects = []; - $cv = []; - $offset = $total = $limit = $filteredTotalCount = 0; - - if ($object instanceof DataObject\Concrete) { - $class = $object->getClass(); - if ($class->getShowVariants()) { - $objectTypes = DataObject::$types; - } - } - - if ($object->hasChildren($objectTypes)) { - $offset = (int)$request->query->get('start'); - $limit = (int)$request->query->get('limit', '100000000'); - if ($view = $request->query->get('view', '')) { - $cv = $this->elementService->getCustomViewById($view) ?? []; - } - - if (!is_null($filter)) { - if (!str_ends_with($filter, '*')) { - $filter .= '*'; - } - $filter = str_replace('*', '%', $filter); - $limit = 100; - } - - $childrenList = new DataObject\Listing(); - $childrenList->setCondition($this->buildChildrenCondition($object, $filter, (string)$view)); - $childrenList->setLimit($limit); - $childrenList->setOffset($offset); - - if ($object->getChildrenSortBy() === 'index') { - $childrenList->setOrderKey('objects.index ASC', false); - } else { - $childrenList->setOrderKey( - sprintf( - 'CAST(objects.%s AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci %s', - $object->getChildrenSortBy(), $object->getChildrenSortOrder() - ), - false - ); - } - $childrenList->setObjectTypes($objectTypes); - - Element\Service::addTreeFilterJoins($cv, $childrenList); - - $beforeListLoadEvent = new GenericEvent($this, [ - 'list' => $childrenList, - 'context' => $request->query->all(), - ]); - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::OBJECT_LIST_BEFORE_LIST_LOAD); - - /** @var DataObject\Listing $childrenList */ - $childrenList = $beforeListLoadEvent->getArgument('list'); - - $children = $childrenList->load(); - $filteredTotalCount = $childrenList->getTotalCount(); - - foreach ($children as $child) { - $objectTreeNode = $this->getTreeNodeConfig($child); - // this if is obsolete since as long as the change with #11714 about list on line 175-179 are working fine, we already filter the list=1 there - if ($objectTreeNode['permissions']['list'] == 1) { - $objects[] = $objectTreeNode; - } - } - - //pagination for custom view - $total = $cv - ? $filteredTotalCount - : $object->getChildAmount(null, $this->getAdminUser()); - } - - //Hook for modifying return value - e.g. for changing permissions based on object data - //data need to wrapped into a container in order to pass parameter to event listeners by reference so that they can change the values - $event = new GenericEvent($this, [ - 'objects' => $objects, - ]); - $eventDispatcher->dispatch($event, AdminEvents::OBJECT_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA); - - $objects = $event->getArgument('objects'); - - if ($limit) { + public function treeGetChildrenByIdAction( + TreeGetChildrenByIdHandler $handler, + Request $request, + #[MapQueryParameter] ?string $filter = null, + #[MapQueryParameter] int $node = 0, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] int $limit = 100000000, + #[MapQueryParameter] string $view = '', + #[MapQueryParameter] int $fromPaging = 0, + #[MapQueryParameter] int $inSearch = 0, + ): JsonResponse + { + $result = $handler($node, $filter, $start, $limit, $view, $fromPaging, $request->query->all()); + + if ($result->limit) { return $this->adminJson([ - 'offset' => $offset, - 'limit' => $limit, - 'total' => $total, - 'overflow' => !is_null($filter) && ($filteredTotalCount > $limit), - 'nodes' => $objects, - 'fromPaging' => (int)$request->query->get('fromPaging'), - 'filter' => $request->query->get('filter') ?: '', - 'inSearch' => (int)$request->query->get('inSearch'), + 'offset' => $result->offset, + 'limit' => $result->limit, + 'total' => $result->total, + 'overflow' => !is_null($result->filter) && ($result->filteredTotalCount > $result->limit), + 'nodes' => $result->objects, + 'fromPaging' => $result->fromPaging, + 'filter' => $result->filter ?: '', + 'inSearch' => $inSearch, ]); } - return $this->adminJson($objects); + return $this->adminJson($result->objects); } - private function buildChildrenCondition(DataObject\AbstractObject $object, ?string $filter, ?string $view): string - { - $condition = "objects.parentId = '" . $object->getId() . "'"; - - // custom views start - if ($view) { - $cv = $this->elementService->getCustomViewById($view); - - if (!empty($cv['classes'])) { - $cvConditions = []; - $cvClasses = $cv['classes']; - foreach ($cvClasses as $key => $cvClass) { - $cvConditions[] = "objects.classId = '" . $key . "'"; - } - - $cvConditions[] = "objects.type = 'folder'"; - $condition .= ' AND (' . implode(' OR ', $cvConditions) . ')'; - } - } - // custom views end - - if (!$this->getAdminUser()->isAdmin()) { - $userIds = $this->getAdminUser()->getRoles(); - $currentUserId = $this->getAdminUser()->getId(); - $userIds[] = $currentUserId; - - $inheritedPermission = $object->getDao()->isInheritingPermission('list', $userIds); - - $anyAllowedRowOrChildren = 'EXISTS(SELECT list FROM users_workspaces_object uwo WHERE userId IN (' . implode(',', $userIds) . ') AND list=1 AND LOCATE(CONCAT(objects.path,objects.key),cpath)=1 AND - NOT EXISTS(SELECT list FROM users_workspaces_object WHERE userId =' . $currentUserId . ' AND list=0 AND cpath = uwo.cpath))'; - $isDisallowedCurrentRow = 'EXISTS(SELECT list FROM users_workspaces_object WHERE userId IN (' . implode(',', $userIds) . ') AND cid = objects.id AND list=0)'; - - $condition .= ' AND IF(' . $anyAllowedRowOrChildren . ',1,IF(' . $inheritedPermission . ', ' . $isDisallowedCurrentRow . ' = 0, 0)) = 1'; - } - - if (!is_null($filter)) { - $db = Db::get(); - $condition .= ' AND CAST(objects.key AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ' . $db->quote($filter); - } - - return $condition; - } - - /** - * @throws Exception - */ #[Override] protected function getTreeNodeConfig(ElementInterface $element): array { @@ -241,1758 +117,243 @@ protected function getTreeNodeConfig(ElementInterface $element): array } #[Route('/get-id-path-paging-info', name: 'getidpathpaginginfo', methods: ['GET'])] - public function getIdPathPagingInfoAction(Request $request): JsonResponse + public function getIdPathPagingInfoAction( + GetIdPathPagingInfoHandler $handler, + #[MapQueryParameter] ?string $path = null, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $limit = null, + ): JsonResponse { - $path = $request->query->get('path'); - $pathParts = explode('/', $path); - $id = (int) array_pop($pathParts); - - $limit = $request->query->get('limit'); - - if (empty($limit)) { - $limit = 30; + if ($path === null) { + return $this->adminJson(['success' => false]); } - $data = []; - - $targetObject = DataObject::getById($id); - $object = $targetObject; - - while ($parent = $object->getParent()) { - $list = new DataObject\Listing(); - $list->setCondition('parentId = ?', $parent->getId()); - $list->setUnpublished(true); - $total = $list->getTotalCount(); - - $info = [ - 'total' => $total, - ]; - - if ($total > $limit) { - $idList = $list->loadIdList(); - $position = array_search($object->getId(), $idList); - $info['position'] = $position + 1; - $info['page'] = ceil($info['position'] / $limit); - } - - $data[$parent->getId()] = $info; - - $object = $parent; - } + $result = $handler($path, $limit ?? 30); - return $this->adminJson($data); + return $this->adminJson($result->data); } - /** - * @throws Exception - */ #[Route('/get', name: 'get', methods: ['GET'])] - public function getAction(Request $request, EventDispatcherInterface $eventDispatcher, PreviewGeneratorInterface $defaultPreviewGenerator): JsonResponse + public function getAction( + GetDataObjectHandler $getDataObject, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $layoutId = null, + ): JsonResponse { - $objectId = $request->query->getInt('id'); - $objectFromDatabase = DataObject\Concrete::getById($objectId); - - if (!$objectFromDatabase instanceof \OpenDxp\Model\DataObject\Concrete) { - return $this->adminJson(['success' => false, 'message' => 'element_not_found'], JsonResponse::HTTP_NOT_FOUND); - } - - $objectFromDatabase = clone $objectFromDatabase; - - // set the latest available version for editmode - $draftVersion = null; - $object = $this->getLatestVersion($objectFromDatabase, $draftVersion); - - // check for lock - if ($object->isAllowed('save') || $object->isAllowed('publish') || $object->isAllowed('unpublish') || $object->isAllowed('delete')) { - if (Element\Editlock::isLocked($objectId, 'object', $request->getSession()->getId())) { - //Hook for modifying editlock handling - e.g. no editLockResponse but keep old lock - $lockData = [ - 'task' => self::TASK_RESPONSE, - ]; - $event = new GenericEvent($this, [ - 'data' => $lockData, - 'object' => $object, - ]); - $eventDispatcher->dispatch($event, AdminEvents::OBJECT_GET_IS_LOCKED); - $lockData = $event->getArgument('data'); - - if ($lockData['task'] === self::TASK_RESPONSE) { - return $this->getEditLockResponse($objectId, 'object'); - } - - if ($lockData['task'] === self::TASK_OVERWRITE) { - Element\Editlock::lock($objectId, 'object', $request->getSession()->getId()); - } - } else { - Element\Editlock::lock($objectId, 'object', $request->getSession()->getId()); - } - } - - // we need to know if the latest version is published or not (a version), because of lazy loaded fields in $this->getDataForObject() - $objectFromVersion = $object !== $objectFromDatabase; - - if ($object->isAllowed('view')) { - $objectData = []; - - /** ------------------------------------------------------------- - * Load some general data from published object (if existing) - * ------------------------------------------------------------- */ - $objectData['idPath'] = Element\Service::getIdPath($objectFromDatabase); - - $linkGeneratorReference = $objectFromDatabase->getClass()->getLinkGeneratorReference(); - $previewGenerator = $objectFromDatabase->getClass()->getPreviewGenerator(); - if (empty($previewGenerator) && !empty($linkGeneratorReference)) { - $previewGenerator = $defaultPreviewGenerator; - } - - $objectData['hasPreview'] = false; - if ($linkGeneratorReference || $previewGenerator) { - $objectData['hasPreview'] = true; - } - - if ($draftVersion instanceof Model\Version && $objectFromDatabase->getModificationDate() < $draftVersion->getDate()) { - $objectData['draft'] = [ - 'id' => $draftVersion->getId(), - 'modificationDate' => $draftVersion->getDate(), - 'isAutoSave' => $draftVersion->isAutoSave(), - ]; - } - - $objectData['general'] = []; - - $allowedKeys = ['published', 'key', 'id', 'creationDate', 'classId', 'className', 'type', 'parentId', 'userOwner', 'userModification']; - foreach ($objectFromDatabase->getObjectVars() as $key => $value) { - if (in_array($key, $allowedKeys)) { - $objectData['general'][$key] = $value; - } - } - $objectData['general']['classTitle'] = $objectFromDatabase->getClass()->getTitle() ?: $objectFromDatabase->getClassName(); - $objectData['general']['fullpath'] = $objectFromDatabase->getRealFullPath(); - $objectData['general']['locked'] = $objectFromDatabase->isLocked(); - $objectData['general']['php'] = [ - 'classes' => [$objectFromDatabase::class, ...array_values(class_parents($objectFromDatabase))], - 'interfaces' => array_values(class_implements($objectFromDatabase)), - ]; - $objectData['general']['allowInheritance'] = $objectFromDatabase->getClass()->getAllowInherit(); - $objectData['general']['allowVariants'] = $objectFromDatabase->getClass()->getAllowVariants(); - $objectData['general']['showVariants'] = $objectFromDatabase->getClass()->getShowVariants(); - $objectData['general']['showAppLoggerTab'] = $objectFromDatabase->getClass()->getShowAppLoggerTab(); - $objectData['general']['showFieldLookup'] = $objectFromDatabase->getClass()->getShowFieldLookup(); - $objectData['general']['linkGeneratorReference'] = $linkGeneratorReference; - - if ($previewGenerator) { - $objectData['general']['previewConfig'] = $previewGenerator->getPreviewConfig($objectFromDatabase); - } - - $objectData['layout'] = $objectFromDatabase->getClass()->getLayoutDefinitions(); - $objectData['userPermissions'] = $objectFromDatabase->getUserPermissions($this->getAdminUser()); - $objectVersions = Element\Service::getSafeVersionInfo($objectFromDatabase->getVersions()); - $objectData['versions'] = array_splice($objectVersions, -1, 1); - $objectData['scheduledTasks'] = array_map( - static fn (Task $task) => $task->getObjectVars(), - $objectFromDatabase->getScheduledTasks() - ); - - $objectData['childdata']['id'] = $objectFromDatabase->getId(); - $objectData['childdata']['data']['classes'] = $this->prepareChildClasses($objectFromDatabase->getDao()->getClasses()); - $objectData['childdata']['data']['general'] = $objectData['general']; - - /** ------------------------------------------------------------- - * Load remaining general data from latest version - * ------------------------------------------------------------- */ - $allowedKeys = ['modificationDate', 'userModification']; - foreach ($object->getObjectVars() as $key => $value) { - if (in_array($key, $allowedKeys)) { - $objectData['general'][$key] = $value; - } - } - - try { - $this->getDataForObject($object, $objectFromVersion); - } catch (Throwable) { - $object = $objectFromDatabase; - $this->getDataForObject($object, false); - } - - $objectData['data'] = $this->objectData; - $objectData['metaData'] = $this->metaData; - $objectData['properties'] = Element\Service::minimizePropertiesForEditmode($object->getProperties()); - - // this used for the "this is not a published version" hint - // and for adding the published icon to version overview - $objectData['general']['versionDate'] = $objectFromDatabase->getModificationDate(); - $objectData['general']['versionCount'] = $objectFromDatabase->getVersionCount(); - - $userOwnerName = $this->getUserName($objectData['general']['userOwner']); - $userModificationName = ($objectData['general']['userOwner'] == $objectData['general']['userModification']) ? $userOwnerName : $this->getUserName($objectData['general']['userModification']); - $objectData['general']['userOwnerUsername'] = $userOwnerName['userName']; - $objectData['general']['userOwnerFullname'] = $userOwnerName['fullName']; - $objectData['general']['userModificationUsername'] = $userModificationName['userName']; - $objectData['general']['userModificationFullname'] = $userModificationName['fullName']; - - $this->addAdminStyle($object, ElementAdminStyleEvent::CONTEXT_EDITOR, $objectData['general']); - - $currentLayoutId = $request->query->get('layoutId'); - - $validLayouts = DataObject\Service::getValidLayouts($object); - - //Fallback if $currentLayoutId is not set or empty string - //Uses first valid layout instead of admin layout when empty - $ok = false; - foreach ($validLayouts as $layout) { - if ($currentLayoutId == $layout->getId()) { - $ok = true; - } - } - - if (!$ok) { - $currentLayoutId = null; - } - - //main layout has id 0 so we check for is_null() - if ($currentLayoutId === null && $validLayouts !== []) { - if (count($validLayouts) === 1) { - $firstLayout = reset($validLayouts); - $currentLayoutId = $firstLayout->getId(); - } else { - foreach ($validLayouts as $checkDefaultLayout) { - if ($checkDefaultLayout->getDefault()) { - $currentLayoutId = $checkDefaultLayout->getId(); - } - } - } - } - - if ($currentLayoutId === null && count($validLayouts) > 0) { - $currentLayoutId = reset($validLayouts)->getId(); - } - - if ($validLayouts !== []) { - $objectData['validLayouts'] = []; - - foreach ($validLayouts as $validLayout) { - $objectData['validLayouts'][] = ['id' => $validLayout->getId(), 'name' => $validLayout->getName()]; - } - - usort($objectData['validLayouts'], static function ($layoutData1, $layoutData2) { - if ($layoutData2['id'] === '-1') { - return 1; - } - - if ($layoutData1['id'] === '-1') { - return -1; - } - - if ($layoutData2['id'] === '0') { - return 1; - } - if ($layoutData1['id'] === '0') { - return -1; - } - - return strcasecmp($layoutData1['name'], $layoutData2['name']); - }); - - $user = Tool\Admin::getCurrentUser(); - - if ($currentLayoutId == -1 && $user->isAdmin()) { - $layout = DataObject\Service::getSuperLayoutDefinition($object); - $objectData['layout'] = $layout; - } elseif (!empty($currentLayoutId)) { - $objectData['layout'] = $validLayouts[$currentLayoutId]->getLayoutDefinitions(); - } - - $objectData['currentLayoutId'] = $currentLayoutId; - } - - //Hook for modifying return value - e.g. for changing permissions based on object data - //data need to wrapped into a container in order to pass parameter to event listeners by reference so that they can change the values - $event = new GenericEvent($this, [ - 'data' => $objectData, - 'object' => $object, - ]); - - DataObject\Service::enrichLayoutDefinition($objectData['layout'], $object); - $eventDispatcher->dispatch($event, AdminEvents::OBJECT_GET_PRE_SEND_DATA); - $data = $event->getArgument('data'); - - DataObject\Service::removeElementFromSession('object', $object->getId(), $request->getSession()->getId()); - - if ($data['layout'] ?? false) { - $layoutArray = json_decode($this->encodeJson($data['layout']), true); - $this->classFieldDefinitions = json_decode($this->encodeJson($object->getClass()->getFieldDefinitions()), true); - - if (is_array($layoutArray)) { - $this->injectValuesForCustomLayout($layoutArray); - } - - $data['layout'] = $layoutArray; - } - - return $this->adminJson($data); + try { + $result = $getDataObject($id, $layoutId); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - throw $this->createAccessDeniedHttpException(); - } + $this->sessionService->removeObject('object', $id); - private function injectValuesForCustomLayout(array &$layout): void - { - foreach ($layout['children'] as &$child) { - if ($child['datatype'] === 'layout') { - $this->injectValuesForCustomLayout($child); - } else { - foreach ($this->classFieldDefinitions[$child['name']] as $key => $value) { - if (array_key_exists($key, $child) && ($child[$key] === null || $child[$key] === '' || (is_array($child[$key]) && empty($child[$key])))) { - $child[$key] = $value; - } - } - } - } + return $this->adminJson($result->data); } - /** - * @throws Exception - */ #[Route('/get-select-options', name: 'getSelectOptions', methods: ['POST'])] - public function getSelectOptions(Request $request): JsonResponse + public function getSelectOptions(Request $request, GetSelectOptionsHandler $handler): JsonResponse { - $objectId = $request->request->getInt('objectId'); - $object = DataObject\Concrete::getById($objectId); - if (!$object instanceof DataObject\Concrete) { - return new JsonResponse(['success'=> false, 'message' => 'Object not found.']); - } - - if ($request->request->get('changedData')) { - $this->applyChanges($object, $this->decodeJson($request->request->get('changedData'))); - } - - $fieldDefinitionConfig = json_decode($request->request->get('fieldDefinition'), true); - /** - * @var DataObject\ClassDefinition\Data\Select|DataObject\ClassDefinition\Data\Multiselect $fieldDefinition - */ - $fieldDefinition = DataObject\Classificationstore\Service::getFieldDefinitionFromJson( - $fieldDefinitionConfig, - $fieldDefinitionConfig['fieldtype'] - ); - - $optionsProvider = OptionsProviderResolver::resolveProvider( - $fieldDefinition->getOptionsProviderClass(), - $fieldDefinition instanceof DataObject\ClassDefinition\Data\Multiselect - ? OptionsProviderResolver::MODE_MULTISELECT - : OptionsProviderResolver::MODE_SELECT - ); + $changedData = $request->request->has('changedData') + ? $this->decodeJson($request->request->get('changedData')) + : null; - $context = json_decode($request->request->get('context'), true) ?? []; - $options = $optionsProvider->getOptions( - [ - 'object' => $object, - 'fieldname' => $fieldDefinition->getName(), - 'class' => $object->getClass(), - 'context' => $context, - ], - $fieldDefinition + $options = $handler( + objectId: $request->request->getInt('objectId'), + changedData: is_array($changedData) ? $changedData : null, + fieldDefinitionConfig: json_decode($request->request->get('fieldDefinition'), true), + context: json_decode($request->request->get('context'), true) ?? [], ); - return new JsonResponse(['success' => true, 'options' => $options]); + return $this->adminJson(ApiResponse::ok(['options' => $options])); } - private function applyChanges(DataObject\Concrete $object, array $changes): void + #[Route('/get-folder', name: 'getfolder', methods: ['GET'])] + public function getFolderAction( + GetDataObjectFolderHandler $handler, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { - foreach ($changes as $key => $value) { - $fd = $object->getClass()->getFieldDefinition($key); - if ($fd) { - if ($fd instanceof DataObject\ClassDefinition\Data\Localizedfields) { - $user = Tool\Admin::getCurrentUser(); - if (!$user->getAdmin()) { - $allowedLanguages = DataObject\Service::getLanguagePermissions($object, $user, 'lEdit'); - if (!is_null($allowedLanguages)) { - $allowedLanguages = array_keys($allowedLanguages); - $submittedLanguages = array_keys($changes[$key]); - foreach ($submittedLanguages as $submittedLanguage) { - if (!in_array($submittedLanguage, $allowedLanguages)) { - unset($value[$submittedLanguage]); - } - } - } - } - } + $result = $handler($id); - if ($fd instanceof ReverseObjectRelation) { - $remoteClass = DataObject\ClassDefinition::getByName($fd->getOwnerClassName()); - $relations = $object->getRelationData($fd->getOwnerFieldName(), false, $remoteClass->getId()); - $toAdd = $this->detectAddedRemoteOwnerRelations($relations, $value); - $toDelete = $this->detectDeletedRemoteOwnerRelations($relations, $value); - if (count($toAdd) > 0 || count($toDelete) > 0) { - $this->processRemoteOwnerRelations($object, $toDelete, $toAdd, $fd->getOwnerFieldName()); - } - } else { - $object->setValue($key, $fd->getDataFromEditmode($value, $object)); - } - } - } + return $this->adminJson($result->data); } - private function getDataForObject(DataObject\Concrete $object, bool $objectFromVersion = false): void - { - foreach ($object->getClass()->getFieldDefinitions(['object' => $object]) as $key => $def) { - $this->getDataForField($object, $key, $def, $objectFromVersion); - } + #[Route('/add', name: 'add', methods: ['POST'])] + public function addAction(Request $request, AddObjectHandler $handler): JsonResponse + { + $result = $handler( + className: $request->request->get('className'), + classId: $request->request->get('classId'), + parentId: $request->request->getInt('parentId'), + key: $request->request->get('key'), + objectType: $request->request->get('objecttype') ?? '', + variantViaTree: (bool) $request->request->get('variantViaTree'), + ); + + return $this->adminJson(ApiResponse::ok([ + 'id' => $result->id, + 'type' => $result->type, + ])); } - /** - * Gets recursively attribute data from parent and fills objectData and metaData - */ - private function getDataForField(DataObject\Concrete $object, string $key, DataObject\ClassDefinition\Data $fielddefinition, bool $objectFromVersion, int $level = 0): void + #[Route('/add-folder', name: 'addfolder', methods: ['POST'])] + public function addFolderAction(Request $request, AddObjectFolderHandler $handler): JsonResponse { - $parent = DataObject\Service::hasInheritableParentObject($object); - $getter = 'get' . ucfirst($key); - - // Editmode optimization for lazy loaded relations (note that this is just for AbstractRelations, not for all - // LazyLoadingSupportInterface types. It tries to optimize fetching the data needed for the editmode without - // loading the entire target element. - // ReverseObjectRelation should go in there anyway (regardless if it a version or not), - // so that the values can be loaded. - if ( - (!$objectFromVersion && $fielddefinition instanceof AbstractRelations) - || $fielddefinition instanceof ReverseObjectRelation - ) { - $refId = null; - - if ($fielddefinition instanceof ReverseObjectRelation) { - $refKey = $fielddefinition->getOwnerFieldName(); - $refClass = DataObject\ClassDefinition::getByName($fielddefinition->getOwnerClassName()); - if ($refClass) { - $refId = $refClass->getId(); - } - } else { - $refKey = $key; - } - - $relations = $object->getRelationData($refKey, !$fielddefinition instanceof ReverseObjectRelation, $refId); - - if ($fielddefinition->supportsInheritance() && $relations === [] && !empty($parent)) { - $this->getDataForField($parent, $key, $fielddefinition, $objectFromVersion, $level + 1); - } else { - $data = []; - - if ($fielddefinition instanceof DataObject\ClassDefinition\Data\ManyToOneRelation) { - if (isset($relations[0])) { - $data = $relations[0]; - $data['published'] = (bool)$data['published']; - } else { - $data = null; - } - } elseif ( - ($fielddefinition instanceof DataObject\ClassDefinition\Data\OptimizedAdminLoadingInterface && $fielddefinition->isOptimizedAdminLoading()) - || ($fielddefinition instanceof ManyToManyObjectRelation && !$fielddefinition->getVisibleFields() && !$fielddefinition instanceof DataObject\ClassDefinition\Data\AdvancedManyToManyObjectRelation) - ) { - foreach ($relations as $rkey => $rel) { - $index = $rkey + 1; - $rel['fullpath'] = $rel['path']; - $rel['classname'] = $rel['subtype']; - $rel['rowId'] = $rel['id'] . AbstractRelations::RELATION_ID_SEPARATOR . $index . AbstractRelations::RELATION_ID_SEPARATOR . $rel['type']; - $rel['published'] = (bool)$rel['published']; - $data[] = $rel; - } - } else { - $fieldData = $object->$getter(); - $data = $fielddefinition->getDataForEditmode($fieldData, $object, ['objectFromVersion' => $objectFromVersion]); - } - $this->objectData[$key] = $data; - $this->metaData[$key]['objectid'] = $object->getId(); - $this->metaData[$key]['inherited'] = $level !== 0; - } - } else { - $fieldData = $object->$getter(); - $isInheritedValue = false; - - if ($fielddefinition instanceof DataObject\ClassDefinition\Data\CalculatedValue) { - $fieldData = new DataObject\Data\CalculatedValue($fielddefinition->getName()); - $fieldData->setContextualData('object', null, null, null, null, null, $fielddefinition); - $value = $fielddefinition->getDataForEditmode($fieldData, $object, ['objectFromVersion' => $objectFromVersion]); - } else { - $value = $fielddefinition->getDataForEditmode($fieldData, $object, ['objectFromVersion' => $objectFromVersion]); - } - - // following some exceptions for special data types (localizedfields, objectbricks) - if ($value && ($fieldData instanceof DataObject\Localizedfield || $fieldData instanceof DataObject\Classificationstore)) { - // make sure that the localized field participates in the inheritance detection process - $isInheritedValue = $value['inherited']; - } - if ($fielddefinition instanceof DataObject\ClassDefinition\Data\Objectbricks && is_array($value)) { - // make sure that the objectbricks participate in the inheritance detection process - foreach ($value as $singleBrickData) { - if (!empty($singleBrickData['inherited'])) { - $isInheritedValue = true; - } - } - } - - if ($fielddefinition->isEmpty($fieldData) && !empty($parent)) { - $this->getDataForField($parent, $key, $fielddefinition, $objectFromVersion, $level + 1); - // exception for classification store. if there are no items then it is empty by definition. - // consequence is that we have to preserve the metadata information - if ($fielddefinition instanceof DataObject\ClassDefinition\Data\Classificationstore && $level === 0) { - $this->objectData[$key]['metaData'] = $value['metaData'] ?? []; - $this->objectData[$key]['inherited'] = true; - } - } else { - $isInheritedValue = $isInheritedValue || ($level !== 0); - $this->metaData[$key]['objectid'] = $object->getId(); - - $this->objectData[$key] = $value; - $this->metaData[$key]['inherited'] = $isInheritedValue; + $handler( + parentId: $request->request->getInt('parentId'), + key: $request->request->get('key'), + ); - if ($isInheritedValue && !$fielddefinition->isEmpty($fieldData) && !$fielddefinition->supportsInheritance()) { - $this->objectData[$key] = null; - $this->metaData[$key]['inherited'] = false; - $this->metaData[$key]['hasParentValue'] = true; - } - } - } + return $this->adminJson(ApiResponse::ok()); } - #[Route('/get-folder', name: 'getfolder', methods: ['GET'])] - public function getFolderAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse + #[Route('/delete', name: 'delete', methods: ['DELETE'])] + public function deleteAction(DeleteDataObjectHandler $handler, Request $request): JsonResponse { - $objectId = (int)$request->query->get('id'); - $object = DataObject::getById($objectId); + $type = $request->request->get('type'); + $id = (int) $request->request->get('id'); - if (!$object) { - throw $this->createNotFoundException(); + if ($type !== 'children' && !$id) { + throw new NotFoundHttpException(); } - if ($object->isAllowed('view')) { - $objectData = []; - - $objectData['general'] = []; - $objectData['idPath'] = Element\Service::getIdPath($object); - $objectData['type'] = $object->getType(); - $allowedKeys = ['published', 'key', 'id', 'type', 'path', 'modificationDate', 'creationDate', 'userOwner', 'userModification']; - foreach ($object->getObjectVars() as $key => $value) { - if (in_array($key, $allowedKeys)) { - $objectData['general'][$key] = $value; - } - } - $objectData['general']['fullpath'] = $object->getRealFullPath(); - - $objectData['general']['locked'] = $object->isLocked(); - - $objectData['properties'] = Element\Service::minimizePropertiesForEditmode($object->getProperties()); - $objectData['userPermissions'] = $object->getUserPermissions($this->getAdminUser()); - $objectData['classes'] = $this->prepareChildClasses($object->getDao()->getClasses()); - - $userOwnerName = $this->getUserName($objectData['general']['userOwner']); - $userModificationName = ($objectData['general']['userOwner'] == $objectData['general']['userModification']) ? $userOwnerName : $this->getUserName($objectData['general']['userModification']); - $objectData['general']['userOwnerUsername'] = $userOwnerName['userName']; - $objectData['general']['userOwnerFullname'] = $userOwnerName['fullName']; - $objectData['general']['userModificationUsername'] = $userModificationName['userName']; - $objectData['general']['userModificationFullname'] = $userModificationName['fullName']; - - //Hook for modifying return value - e.g. for changing permissions based on object data - //data need to wrapped into a container in order to pass parameter to event listeners by reference so that they can change the values - $event = new GenericEvent($this, [ - 'data' => $objectData, - 'object' => $object, - ]); - $eventDispatcher->dispatch($event, AdminEvents::OBJECT_GET_PRE_SEND_DATA); - $objectData = $event->getArgument('data'); + $result = $handler( + type: $type ?? '', + id: $id, + amount: $request->request->getInt('amount'), + ); - return $this->adminJson($objectData); + if ($type === 'children') { + return $this->adminJson(ApiResponse::ok(['deleted' => $result->deleted])); } - throw $this->createAccessDeniedHttpException(); + // return ok even when the object doesn't exist — valid for batch delete incl. children + return $this->adminJson(ApiResponse::ok()); } - /** - * @param DataObject\ClassDefinition[] $classes - */ - protected function prepareChildClasses(array $classes): array + #[Route('/change-children-sort-by', name: 'changechildrensortby', methods: ['PUT'])] + public function changeChildrenSortByAction(ChangeChildrenSortByHandler $handler, Request $request): JsonResponse { - $reduced = []; - foreach ($classes as $class) { - $reduced[] = [ - 'id' => $class->getId(), - 'name' => $class->getName(), - 'inheritance' => $class->getAllowInherit(), - ]; - } + $handler( + id: $request->request->getInt('id'), + sortBy: $request->request->get('sortBy') ?? '', + sortOrder: $request->request->get('childrenSortOrder') ?? '', + ); - return $reduced; + return $this->adminJson(ApiResponse::ok()); } - #[Route('/add', name: 'add', methods: ['POST'])] - public function addAction(Request $request, Model\FactoryInterface $modelFactory): JsonResponse + #[Route('/update', name: 'update', methods: ['PUT'])] + public function updateAction(Request $request, UpdateDataObjectHandler $handler): JsonResponse { - $message = ''; - $parent = DataObject::getById((int) $request->request->get('parentId')); - - if (!$parent->isAllowed('create')) { - $message = 'prevented adding object because of missing permissions'; - Logger::debug($message); - } - - $intendedPath = $parent->getRealFullPath() . '/' . $request->request->get('key'); - if (DataObject\Service::pathExists($intendedPath)) { - $message = 'prevented creating object because object with same path+key already exists'; - Logger::debug($message); - } - - //return false if missing permissions or path+key already exists - if (!empty($message)) { - return $this->adminJson([ - 'success' => false, - 'message' => $message, - ]); - } - - $className = 'OpenDxp\\Model\\DataObject\\' . ucfirst($request->request->get('className')); - /** @var DataObject\Concrete $object */ - $object = $modelFactory->build($className); - $object->setOmitMandatoryCheck(true); // allow to save the object although there are mandatory fields - $classId = $request->request->get('classId'); - if ($request->request->get('variantViaTree')) { - $parentId = $request->request->getInt('parentId'); - $parent = DataObject\Concrete::getById($parentId); - $classId = $parent->getClass()->getId(); - } + $values = $this->decodeJson($request->request->get('values')); + $ids = $this->decodeJson($request->request->get('id')); - $object->setClassId($classId); - $object->setClassName($request->request->get('className')); - $object->setParentId($request->request->getInt('parentId')); - $object->setKey($request->request->get('key')); - $object->setCreationDate(time()); - $object->setUserOwner($this->getAdminUser()->getId()); - $object->setUserModification($this->getAdminUser()->getId()); - $object->setPublished(false); + if (is_array($ids)) { + $result = null; + foreach ($ids as $id) { + try { + $result = $handler((int) $id, $values); + } catch (\Throwable $e) { + return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); + } + } - $objectType = $request->request->get('objecttype'); - if (in_array($objectType, [DataObject::OBJECT_TYPE_OBJECT, DataObject::OBJECT_TYPE_VARIANT])) { - $object->setType($objectType); + return $this->adminJson(ApiResponse::ok(['treeData' => $result?->treeData])); } - try { - $object->save(); - $return = [ - 'success' => true, - 'id' => $object->getId(), - 'type' => $object->getType(), - 'message' => $message, - ]; - } catch (Exception $e) { - $return = [ - 'success' => false, - 'message' => $e->getMessage(), - ]; - } + $result = $handler((int) $ids, $values); - return $this->adminJson($return); + return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } - #[Route('/add-folder', name: 'addfolder', methods: ['POST'])] - public function addFolderAction(Request $request): JsonResponse + #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] + public function saveAction(Request $request, SaveDataObjectHandler $handler): JsonResponse { - $success = false; + $payload = DataObjectPayload::fromRequest($request); + $result = $handler($request->request->getInt('id'), $payload); - $parent = DataObject::getById((int) $request->request->get('parentId')); - if ($parent->isAllowed('create')) { - if (!DataObject\Service::pathExists($parent->getRealFullPath() . '/' . $request->request->get('key'))) { - $folder = DataObject\Folder::create([ - 'parentId' => $request->request->get('parentId'), - 'creationDate' => time(), - 'userOwner' => $this->getAdminUser()->getId(), - 'userModification' => $this->getAdminUser()->getId(), - 'key' => $request->request->get('key'), - 'published' => true, - ]); + if ($payload->task === 'session' || $payload->task === 'scheduler') { + return $this->adminJson(ApiResponse::ok()); + } - try { - $folder->save(); - $success = true; - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - } else { - Logger::debug('prevented creating object id because of missing permissions'); + if ($payload->task === 'publish' || $payload->task === 'unpublish') { + return $this->adminJson(ApiResponse::ok([ + 'general' => [ + 'modificationDate' => $result->modificationDate, + 'versionDate' => $result->versionDate, + 'versionCount' => $result->versionCount, + ], + 'treeData' => $result->treeData, + ])); } - return $this->adminJson(['success' => $success]); + return $this->adminJson(ApiResponse::ok([ + 'general' => [ + 'modificationDate' => $result->modificationDate, + 'versionDate' => $result->versionDate, + 'versionCount' => $result->versionCount, + ], + 'draft' => $result->draftData, + 'treeData' => $result->treeData, + ])); } - /** - * @throws Exception - */ - #[Route('/delete', name: 'delete', methods: ['DELETE'])] - public function deleteAction(Request $request): JsonResponse + #[Route('/save-folder', name: 'savefolder', methods: ['PUT'])] + public function saveFolderAction(SaveDataObjectFolderHandler $handler, Request $request): JsonResponse { - $type = $request->request->get('type'); + $propertiesData = $request->request->has('properties') + ? $this->decodeJson($request->request->get('properties')) + : null; - if ($type === 'children') { - $parentObject = DataObject::getById((int) $request->request->get('id')); + $handler( + id: $request->request->getInt('id'), + general: $this->decodeJson($request->request->get('general')), + propertiesData: is_array($propertiesData) ? $propertiesData : null, + ); - $list = new DataObject\Listing(); - $list->setCondition('`path` LIKE ' . $list->quote($list->escapeLike($parentObject->getRealFullPath()) . '/%')); - $list->setLimit((int)$request->request->get('amount')); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('DESC'); + return $this->adminJson(ApiResponse::ok()); + } - $deletedItems = []; - foreach ($list as $object) { - $deletedItems[$object->getId()] = $object->getRealFullPath(); - if ($object->isAllowed('delete') && !$object->isLocked()) { - $object->delete(); - } - } + #[Route('/grid-proxy', name: 'gridproxy', methods: ['GET', 'POST', 'PUT'])] + public function gridProxyAction( + Request $request, + DataObjectGridProxyHandler $handler, + CsrfProtectionHandler $csrfProtection, + ): JsonResponse { + $csrfProtection->checkCsrfToken($request); - return $this->adminJson(['success' => true, 'deleted' => $deletedItems]); + $allParams = [...$request->request->all(), ...$request->query->all()]; + if (isset($allParams['context']) && $allParams['context']) { + $allParams['context'] = json_decode($allParams['context'], true); + } else { + $allParams['context'] = []; } - if ($id = $request->request->get('id')) { - $object = DataObject::getById((int) $id); - if ($object) { - if (!$object->isAllowed('delete')) { - throw $this->createAccessDeniedHttpException(); - } - if ($object->isLocked()) { - return $this->adminJson(['success' => false, 'message' => 'prevented deleting object, because it is locked: ID: ' . $object->getId()]); - } - $object->delete(); - } - // return true, even when the object doesn't exist, this can be the case when using batch delete incl. children - return $this->adminJson(['success' => true]); + $result = $handler($allParams, $request->getLocale()); + if ($result->requestedLanguage && $result->requestedLanguage !== 'default') { + $request->setLocale($result->requestedLanguage); } - return $this->adminJson(['success' => false]); + return $this->adminJson($result->data); } - /** - * @throws Exception - */ - #[Route('/change-children-sort-by', name: 'changechildrensortby', methods: ['PUT'])] - public function changeChildrenSortByAction(Request $request): JsonResponse + #[Route('/preview', name: 'preview', methods: ['GET'])] + public function previewAction( + Request $request, + GetDataObjectPreviewUrlHandler $handler, + #[MapQueryParameter] int $id = 0, + ): RedirectResponse|Response { - $object = DataObject::getById((int) $request->request->get('id')); - if ($object) { - $sortBy = $request->request->get('sortBy'); - $sortOrder = $request->request->get('childrenSortOrder'); - if (!\in_array($sortOrder, ['ASC', 'DESC'])) { - $sortOrder = 'ASC'; - } + $object = $this->sessionService->getObject('object', $id); - $currentSortBy = $object->getChildrenSortBy(); + if ($object instanceof DataObject\Concrete) { + $redirectUrl = $handler($object, ['context' => $this, ...$request->query->all()]); - $object->setChildrenSortBy($sortBy); - $object->setChildrenSortOrder($sortOrder); + return $this->redirect($redirectUrl); + } - if ($currentSortBy != $sortBy) { - $user = Tool\Admin::getCurrentUser(); + throw new NotFoundHttpException(sprintf('Expected an object of type "%s", got "%s"', DataObject\Concrete::class, get_debug_type($object))); + } - if (!$user->isAdmin() && !$user->isAllowed('objects_sort_method')) { - return $this->json(['success' => false, 'message' => 'Changing the sort method is only allowed for admin users']); - } - - if ($sortBy === 'index') { - $this->reindexBasedOnSortOrder($object, $sortOrder); - } - } - - $object->save(); - - return $this->json(['success' => true]); - } - - return $this->json(['success' => false, 'message' => 'Unable to change a sorting way of children items.']); - } - - /** - * @throws Exception - */ - #[Route('/update', name: 'update', methods: ['PUT'])] - public function updateAction(Request $request): JsonResponse - { - $values = $this->decodeJson($request->request->get('values')); - $ids = $this->decodeJson($request->request->get('id')); - - if (is_array($ids)) { - $return = ['success' => true]; - foreach ($ids as $id) { - $object = DataObject::getById((int)$id); - $return = $this->executeUpdateAction($object, $values); - if (!$return['success']) { - return $this->adminJson($return); - } - } - } else { - $object = DataObject::getById((int)$ids); - $return = $this->executeUpdateAction($object, $values); - } - - return $this->adminJson($return); - } - - /** - * @return array{success: bool, message?: string} - * - * @throws Exception - */ - private function executeUpdateAction(DataObject $object, mixed $values): array - { - $data = ['success' => false]; - - if ($object instanceof DataObject\Concrete) { - $object->setOmitMandatoryCheck(true); - } - - // this prevents the user from renaming, relocating (actions in the tree) if the newest version isn't the published one - // the reason is that otherwise the content of the newer not published version will be overwritten - if ($object instanceof DataObject\Concrete) { - $latestVersion = $object->getLatestVersion(); - if ($latestVersion && $latestVersion->getData()->getModificationDate() != $object->getModificationDate()) { - return ['success' => false, 'message' => "You can't rename or relocate if there's a newer not published version"]; - } - } - - $key = $values['key'] ?? null; - if ($key) { - $key = Service::getValidKey($key, 'object'); - } - - if ($object->isAllowed('settings')) { - if ($key) { - if ($object->isAllowed('rename')) { - $object->setKey($key); - } elseif ($key !== $object->getKey()) { - Logger::debug('prevented renaming object because of missing permissions '); - } - } - - if (!empty($values['parentId'])) { - $parent = DataObject::getById((int) $values['parentId']); - - //check if parent is changed - if ($object->getParentId() !== $parent->getId()) { - if (!$parent->isAllowed('create')) { - throw new Exception('Prevented moving object - no create permission on new parent '); - } - - $objectWithSamePath = DataObject::getByPath($parent->getRealFullPath() . '/' . $object->getKey()); - - if ($objectWithSamePath != null) { - return ['success' => false, 'message' => 'prevented creating object because object with same path+key already exists']; - } - - if ($object->isLocked()) { - return ['success' => false, 'message' => 'prevented moving object, because it is locked: ID: ' . $object->getId()]; - } - - $object->setParentId($values['parentId']); - } - } - - if (array_key_exists('locked', $values)) { - $object->setLocked($values['locked']); - } - - $object->setModificationDate(time()); - $object->setUserModification($this->getAdminUser()->getId()); - - try { - $isIndexUpdate = isset($values['indices']); - - if ($isIndexUpdate) { - // Ensure the update sort index is already available in the postUpdate eventListener - $indexUpdate = is_int($values['indices']) ? $values['indices'] : $values['indices'][$object->getId()]; - $object->setIndex($indexUpdate); - } - - $object->save(); - - if ($isIndexUpdate) { - $this->updateIndexesOfObjectSiblings($object, $indexUpdate); - } - - $data = [ - 'success' => true, - 'treeData' => $this->getTreeNodeConfig($object), - ]; - } catch (Exception $e) { - Logger::error((string) $e); - - return ['success' => false, 'message' => $e->getMessage()]; - } - } elseif ($key && $object->isAllowed('rename')) { - return $this->renameObject($object, $key); - } else { - Logger::debug('prevented update object because of missing permissions.'); - } - - return $data; - } - - private function executeInsideTransaction(callable $fn): void - { - $maxRetries = 5; - for ($retries = 0; $retries < $maxRetries; $retries++) { - try { - Db::get()->beginTransaction(); - - $fn(); - - Db::get()->commit(); - - break; - } catch (Exception $e) { - Db::get()->rollBack(); - - // we try to start the transaction $maxRetries times again (deadlocks, ...) - if ($retries < ($maxRetries - 1)) { - $run = $retries + 1; - $waitTime = random_int(1, 5) * 100000; // microseconds - Logger::warn('Unable to finish transaction (' . $run . ". run) because of the following reason '" . $e->getMessage() . "'. --> Retrying in " . $waitTime . ' microseconds ... (' . ($run + 1) . ' of ' . $maxRetries . ')'); - - usleep($waitTime); // wait specified time until we restart the transaction - } else { - // if the transaction still fail after $maxRetries retries, we throw out the exception - Logger::error('Finally giving up restarting the same transaction again and again, last message: ' . $e->getMessage()); - - throw $e; - } - } - } - } - - protected function reindexBasedOnSortOrder(DataObject\AbstractObject $parentObject, string $currentSortOrder): void - { - $fn = function () use ($parentObject, $currentSortOrder): void { - $list = new DataObject\Listing(); - - $db = Db::get(); - $result = $db->executeStatement( - 'UPDATE '.$list->getDao()->getTableName().' o, - ( - SELECT newIndex, id FROM ( - SELECT @n := @n +1 AS newIndex, id - FROM '.$list->getDao()->getTableName().', - (SELECT @n := -1) variable - WHERE parentId = ? ORDER BY `key` ' . $currentSortOrder - .') tmp - ) order_table - SET o.index = order_table.newIndex - WHERE o.id=order_table.id', - [ - $parentObject->getId(), - ] - ); - - $db = Db::get(); - $children = $db->fetchAllAssociative( - 'SELECT id, modificationDate, versionCount FROM objects WHERE parentId = ? ORDER BY `index` ASC', - [$parentObject->getId()] - ); - $index = 0; - - foreach ($children as $child) { - $this->updateLatestVersionIndex($child['id'], $child['modificationDate']); - $index++; - - DataObject::clearDependentCacheByObjectId($child['id']); - } - }; - - $this->executeInsideTransaction($fn); - } - - private function updateLatestVersionIndex(int $objectId, int $newIndex): void - { - $object = DataObject\Concrete::getById($objectId); - - if ( - $object && - $object->getType() !== DataObject::OBJECT_TYPE_FOLDER && - $latestVersion = $object->getLatestVersion() - ) { - // don't renew references (which means loading the target elements) - // Not needed as we just save a new version with the updated index - $object = $latestVersion->loadData(false); - if ($newIndex !== $object->getIndex()) { - $object->setIndex($newIndex); - } - $latestVersion->save(); - } - } - - protected function updateIndexesOfObjectSiblings(DataObject\AbstractObject $updatedObject, int $newIndex): void - { - $fn = function () use ($updatedObject, $newIndex): void { - $list = new DataObject\Listing(); - $updatedObject->saveIndex($newIndex); - - // The cte and the limit are needed to order the data before the newIndex is set - $db = Db::get(); - $db->executeStatement( - 'UPDATE '.$list->getDao()->getTableName().' o, - ( - SELECT newIndex, id - FROM ( - With cte As (SELECT `index`, id FROM ' . $list->getDao()->getTableName() . ' WHERE parentId = ? AND id != ? AND `type` IN (\''.implode( - "','", [ - DataObject::OBJECT_TYPE_OBJECT, - DataObject::OBJECT_TYPE_VARIANT, - DataObject::OBJECT_TYPE_FOLDER, - ] - ).'\') ORDER BY `index` LIMIT '. $updatedObject->getParent()->getChildAmount([ - DataObject::OBJECT_TYPE_OBJECT, - DataObject::OBJECT_TYPE_VARIANT, - DataObject::OBJECT_TYPE_FOLDER, - ]) .') - SELECT @n := IF(@n = ? - 1,@n + 2,@n + 1) AS newIndex, id - FROM cte, - (SELECT @n := -1) variable - ) tmp - ) order_table - SET o.index = order_table.newIndex - WHERE o.id=order_table.id', - [ - $updatedObject->getParentId(), - $updatedObject->getId(), - $newIndex, - ] - ); - - $siblings = $db->fetchAllAssociative( - 'SELECT id, modificationDate, versionCount, `key`, `index` FROM objects - WHERE parentId = ? AND id != ? AND `type` IN ("object", "variant", "folder") ORDER BY `index` ASC', - [$updatedObject->getParentId(), $updatedObject->getId()] - ); - $index = 0; - - foreach ($siblings as $sibling) { - if ($index === $newIndex) { - $index++; - } - - $this->updateLatestVersionIndex($sibling['id'], $index); - $index++; - - DataObject::clearDependentCacheByObjectId($sibling['id']); - } - }; - - $this->executeInsideTransaction($fn); - } - - /** - * @throws Exception - */ - #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] - public function saveAction(Request $request): JsonResponse - { - $objectFromDatabase = DataObject\Concrete::getById((int) $request->request->get('id')); - - if (!$objectFromDatabase instanceof DataObject\Concrete) { - return $this->adminJson(['success' => false, 'message' => 'Could not find object']); - } - - // set the latest available version for editmode - $object = $this->getLatestVersion($objectFromDatabase); - $object->setUserModification($this->getAdminUser()->getId()); - - $objectFromVersion = $object !== $objectFromDatabase; - if ($objectFromVersion) { - if (method_exists($object, 'getLocalizedFields')) { - /** @var DataObject\Localizedfield $localizedFields */ - $localizedFields = $object->getLocalizedFields(); - $localizedFields->setLoadedAllLazyData(); - } - - // Mark fields that have changed as dirty - if ($request->query->get('task') !== 'autoSave' && $request->query->get('task') !== 'unpublish') { - foreach ($object->getClass()->getFieldDefinitions() as $fieldName => $fieldDefinition) { - $getter = 'get' . ucfirst($fieldName); - $oldValue = $objectFromDatabase->$getter(); - $newValue = $object->$getter(); - $isEqual = $fieldDefinition instanceof DataObject\ClassDefinition\Data\EqualComparisonInterface - ? $fieldDefinition->isEqual($oldValue, $newValue) - : $oldValue === $newValue; - - if (!$isEqual) { - $object->markFieldDirty($fieldName); - } - } - } - } - - if ($request->request->has('data')) { - try { - $this->applyChanges($object, $this->decodeJson($request->request->get('data'))); - } catch (Throwable) { - $this->applyChanges($objectFromDatabase, $this->decodeJson($request->request->get('data'))); - } - } - - $this->assignPropertiesFromEditmode($request, $object); - $this->applySchedulerDataToElement($request, $object, $this->getAdminUser()); - - if (($request->query->get('task') === 'unpublish' && !$object->isAllowed('unpublish')) || ($request->query->get('task') === 'publish' && !$object->isAllowed('publish'))) { - throw $this->createAccessDeniedHttpException(); - } - - if ($request->query->get('task') === 'unpublish') { - $object->setPublished(false); - } - - if ($request->query->get('task') === 'publish') { - $object->setPublished(true); - } - - // unpublish and save version is possible without checking mandatory fields - if (in_array($request->query->get('task'), ['unpublish', 'version', 'autoSave'])) { - $object->setOmitMandatoryCheck(true); - } - - if (($request->query->get('task') === 'publish') || ($request->query->get('task') === 'unpublish')) { - - $object->save(); - $treeData = $this->getTreeNodeConfig($object); - - $newObject = DataObject::getById($object->getId(), ['force' => true]); - - if ($request->query->get('task') === 'publish') { - $object->deleteAutoSaveVersions($this->getAdminUser()->getId()); - } - - return $this->adminJson([ - 'success' => true, - 'general' => ['modificationDate' => $object->getModificationDate(), - 'versionDate' => $newObject->getModificationDate(), - 'versionCount' => $newObject->getVersionCount(), - ], - 'treeData' => $treeData, - ]); - } - - if ($request->query->get('task') === 'session') { - DataObject\Service::saveElementToSession($object, $request->getSession()->getId(), ''); - - return $this->adminJson(['success' => true]); - } - - if ($request->query->get('task') === 'scheduler' && $object->isAllowed('settings')) { - $object->saveScheduledTasks(); - - return $this->adminJson(['success' => true]); - } - - if ($object->isAllowed('save') || $object->isAllowed('publish')) { - $isAutoSave = $request->query->get('task') === 'autoSave'; - $draftData = []; - - if ($object->isPublished() || $isAutoSave) { - $version = $object->saveVersion(true, true, null, $isAutoSave); - $draftData = [ - 'id' => $version->getId(), - 'modificationDate' => $version->getDate(), - 'isAutoSave' => $version->isAutoSave(), - ]; - } else { - $object->save(); - } - - if ($request->query->get('task') === 'version') { - $object->deleteAutoSaveVersions($this->getAdminUser()->getId()); - } - - $treeData = $this->getTreeNodeConfig($object); - - $newObject = DataObject::getById($object->getId(), ['force' => true]); - - return $this->adminJson([ - 'success' => true, - 'general' => ['modificationDate' => $object->getModificationDate(), - 'versionDate' => $newObject->getModificationDate(), - 'versionCount' => $newObject->getVersionCount(), - ], - 'draft' => $draftData, - 'treeData' => $treeData, - ]); - } - - throw $this->createAccessDeniedHttpException(); - } - - #[Route('/save-folder', name: 'savefolder', methods: ['PUT'])] - public function saveFolderAction(Request $request): JsonResponse - { - $object = DataObject::getById((int) $request->request->get('id')); - - if (!$object) { - throw $this->createNotFoundException('Object not found'); - } - - if ($object->isAllowed('publish')) { - try { - // general settings - $general = $this->decodeJson($request->request->get('general')); - $object->setValues($general); - $object->setUserModification($this->getAdminUser()->getId()); - - $this->assignPropertiesFromEditmode($request, $object); - - $object->save(); - - return $this->adminJson(['success' => true]); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - throw $this->createAccessDeniedHttpException(); - } - - protected function assignPropertiesFromEditmode(Request $request, DataObject\AbstractObject $object): void - { - if ($request->request->has('properties')) { - $properties = []; - // assign inherited properties - foreach ($object->getProperties() as $p) { - if ($p->isInherited()) { - $properties[$p->getName()] = $p; - } - } - - $propertiesData = $this->decodeJson($request->request->get('properties')); - - if (is_array($propertiesData)) { - foreach ($propertiesData as $propertyName => $propertyData) { - $value = $propertyData['data']; - - try { - $property = new Model\Property(); - $property->setType($propertyData['type']); - $property->setName($propertyName); - $property->setCtype('object'); - $property->setDataFromEditmode($value); - $property->setInheritable($propertyData['inheritable']); - - $properties[$propertyName] = $property; - } catch (Exception) { - Logger::err("Can't add " . $propertyName . ' to object ' . $object->getRealFullPath()); - } - } - } - $object->setProperties($properties); - } - } - - #[Route('/publish-version', name: 'publishversion', methods: ['POST'])] - public function publishVersionAction(Request $request): JsonResponse - { - $id = $request->request->getInt('id'); - $version = Model\Version::getById($id); - $object = $version?->loadData(); - - if (!$object) { - throw $this->createNotFoundException('Version with id [' . $id . "] doesn't exist"); - } - - $object = $version->loadData(); - - $currentObject = DataObject::getById($object->getId()); - if ($currentObject->isAllowed('publish')) { - $object->setPublished(true); - $object->setUserModification($this->getAdminUser()->getId()); - - try { - $object->save(); - - $treeData = []; - $this->addAdminStyle($object, ElementAdminStyleEvent::CONTEXT_TREE, $treeData); - - return $this->adminJson( - [ - 'success' => true, - 'general' => ['modificationDate' => $object->getModificationDate() ], - 'treeData' => $treeData, ] - ); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - throw $this->createAccessDeniedHttpException(); - } - - /** - * @throws Exception - */ - #[Route('/preview-version', name: 'previewversion', methods: ['GET'])] - public function previewVersionAction(Request $request, Environment $twig): Response - { - DataObject::setDoNotRestoreKeyAndPath(true); - - $id = $request->query->getInt('id'); - $version = Model\Version::getById($id); - $object = $version?->loadData(); - - if ($object) { - - Tool\UserTimezone::setUserTimezone($request->query->get('userTimezone')); - - if ($timezone = Tool\UserTimezone::getUserTimezone()) { - $twig->getExtension(CoreExtension::class)->setTimezone($timezone); - } - - if (method_exists($object, 'getLocalizedFields')) { - /** @var DataObject\Localizedfield $localizedFields */ - $localizedFields = $object->getLocalizedFields(); - $localizedFields->setLoadedAllLazyData(); - } - - DataObject::setDoNotRestoreKeyAndPath(false); - - if ($object->isAllowed('versions')) { - return $this->render('@OpenDxpAdmin/admin/data_object/data_object/preview_version.html.twig', - [ - 'object' => $object, - 'versionNote' => $version->getNote(), - 'validLanguages' => Tool::getValidLanguages(), - ]); - } - - throw $this->createAccessDeniedException('Permission denied, version id [' . $id . ']'); - } - - throw $this->createNotFoundException('Version with id [' . $id . "] doesn't exist"); - } - - /** - * @throws Exception - */ - #[Route('/diff-versions/from/{from}/to/{to}', name: 'diffversions', methods: ['GET'])] - public function diffVersionsAction(Request $request, Environment $twig, int $from, int $to): Response - { - DataObject::setDoNotRestoreKeyAndPath(true); - - $id1 = $from; - $id2 = $to; - - $version1 = Model\Version::getById($id1); - $object1 = $version1?->loadData(); - - if (!$object1) { - throw $this->createNotFoundException('Version with id [' . $id1 . "] doesn't exist"); - } - - if (method_exists($object1, 'getLocalizedFields')) { - /** @var DataObject\Localizedfield $localizedFields1 */ - $localizedFields1 = $object1->getLocalizedFields(); - $localizedFields1->setLoadedAllLazyData(); - } - - $version2 = Model\Version::getById($id2); - $object2 = $version2?->loadData(); - - if (!$object2) { - throw $this->createNotFoundException('Version with id [' . $id2 . "] doesn't exist"); - } - - Tool\UserTimezone::setUserTimezone($request->query->get('userTimezone')); - - if ($timezone = Tool\UserTimezone::getUserTimezone()) { - $twig->getExtension(CoreExtension::class)->setTimezone($timezone); - } - - if (method_exists($object2, 'getLocalizedFields')) { - /** @var DataObject\Localizedfield $localizedFields2 */ - $localizedFields2 = $object2->getLocalizedFields(); - $localizedFields2->setLoadedAllLazyData(); - } - - DataObject::setDoNotRestoreKeyAndPath(false); - - if ($object1->isAllowed('versions') && $object2->isAllowed('versions')) { - return $this->render('@OpenDxpAdmin/admin/data_object/data_object/diff_versions.html.twig', - [ - 'object1' => $object1, - 'versionNote1' => $version1->getNote(), - 'object2' => $object2, - 'versionNote2' => $version2->getNote(), - 'validLanguages' => Tool::getValidLanguages(), - ]); - } - - throw $this->createAccessDeniedException('Permission denied, version ids [' . $id1 . ', ' . $id2 . ']'); - } - - #[Route('/grid-proxy', name: 'gridproxy', methods: ['GET', 'POST', 'PUT'])] - public function gridProxyAction( - Request $request, - EventDispatcherInterface $eventDispatcher, - GridHelperService $gridHelperService, - LocaleServiceInterface $localeService, - CsrfProtectionHandler $csrfProtection - ): JsonResponse { - $allParams = [...$request->request->all(), ...$request->query->all()]; - if (isset($allParams['context']) && $allParams['context']) { - $allParams['context'] = json_decode($allParams['context'], true); - } else { - $allParams['context'] = []; - } - - $filterPrepareEvent = new GenericEvent($this, [ - 'requestParams' => $allParams, - ]); - $eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::OBJECT_LIST_BEFORE_FILTER_PREPARE); - - $allParams = $filterPrepareEvent->getArgument('requestParams'); - - $csrfProtection->checkCsrfToken($request); - - $result = $this->gridProxy( - $allParams, - DataObject::OBJECT_TYPE_OBJECT, - $request, - $eventDispatcher, - $gridHelperService, - $localeService - ); - - return $this->adminJson($result); - } - - #[Route('/copy-info', name: 'copyinfo', methods: ['GET'])] - public function copyInfoAction(Request $request): JsonResponse - { - $transactionId = time(); - $pasteJobs = []; - - Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { - $session->set((string) $transactionId, ['idMapping' => []]); - }, 'opendxp_copy'); - - if ($request->query->get('type') === 'recursive' || $request->query->get('type') === 'recursive-update-references') { - $object = DataObject::getById((int) $request->query->get('sourceId')); - - // first of all the new parent - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $request->query->get('sourceId'), - 'targetId' => $request->query->get('targetId'), - 'type' => 'child', - 'transactionId' => $transactionId, - 'saveParentId' => true, - ], - ]]; - - if ($object->hasChildren(DataObject::$types)) { - // get amount of children - $list = new DataObject\Listing(); - $list->setCondition('`path` LIKE ' . $list->quote($list->escapeLike($object->getRealFullPath()) . '/%')); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('ASC'); - $list->setObjectTypes(DataObject::$types); - $childIds = $list->loadIdList(); - - if (count($childIds) > 0) { - foreach ($childIds as $id) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $id, - 'targetParentId' => $request->query->get('targetId'), - 'sourceParentId' => $request->query->get('sourceId'), - 'type' => 'child', - 'transactionId' => $transactionId, - ], - ]]; - } - } - - // add id-rewrite steps - if ($request->query->get('type') === 'recursive-update-references') { - for ($i = 0; $i < (count($childIds) + 1); $i++) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copyrewriteids'), - 'method' => 'PUT', - 'params' => [ - 'transactionId' => $transactionId, - '_dc' => uniqid('', false), - ], - ]]; - } - } - } - } elseif ($request->query->get('type') === 'child' || $request->query->get('type') === 'replace') { - // the object itself is the last one - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $request->query->get('sourceId'), - 'targetId' => $request->query->get('targetId'), - 'type' => $request->query->get('type'), - 'transactionId' => $transactionId, - ], - ]]; - } - - return $this->adminJson([ - 'pastejobs' => $pasteJobs, - ]); - } - - /** - * @throws Exception - */ - #[Route('/copy-rewrite-ids', name: 'copyrewriteids', methods: ['PUT'])] - public function copyRewriteIdsAction(Request $request): JsonResponse - { - $transactionId = $request->request->get('transactionId'); - - $idStore = Tool\Session::useBag($request->getSession(), static fn (AttributeBagInterface $session) => $session->get($transactionId), 'opendxp_copy'); - - if (!array_key_exists('rewrite-stack', $idStore)) { - $idStore['rewrite-stack'] = array_values($idStore['idMapping']); - } - - $id = array_shift($idStore['rewrite-stack']); - $object = DataObject::getById($id); - - // create rewriteIds() config parameter - $rewriteConfig = ['object' => $idStore['idMapping']]; - - $object = DataObject\Service::rewriteIds($object, $rewriteConfig); - - $object->setUserModification($this->getAdminUser()->getId()); - $object->save(); - - // write the store back to the session - Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId, $idStore): void { - $session->set($transactionId, $idStore); - }, 'opendxp_copy'); - - return $this->adminJson([ - 'success' => true, - 'id' => $id, - ]); - } - - #[Route('/copy', name: 'copy', methods: ['POST'])] - public function copyAction(Request $request): JsonResponse - { - $message = ''; - $sourceId = $request->request->getInt('sourceId'); - $source = DataObject::getById($sourceId); - - $session = Tool\Session::getSessionBag($request->getSession(), 'opendxp_copy'); - $sessionBag = $session->get($request->request->get('transactionId')); - - $targetId = $request->request->getInt('targetId'); - if ($request->request->has('targetParentId')) { - $sourceParent = DataObject::getById($request->request->getInt('sourceParentId')); - - // this is because the key can get the prefix "_copy" if the target does already exists - if ($sessionBag['parentId']) { - $targetParent = DataObject::getById((int) $sessionBag['parentId']); - } else { - $targetParent = DataObject::getById($request->request->getInt('targetParentId')); - } - - $targetPath = preg_replace('@^' . preg_quote($sourceParent->getRealFullPath(), '@') . '@', $targetParent . '/', $source->getRealPath()); - $target = DataObject::getByPath($targetPath); - } else { - $target = DataObject::getById($targetId); - } - - $user = Tool\Admin::getCurrentUser(); - - if ( - $target->isAllowed('create') && - ($source instanceof DataObject\Concrete ? $user->isAllowed($source->getClassId(), 'class') : true) - ) { - $source = DataObject::getById($sourceId); - if ($source instanceof \OpenDxp\Model\DataObject) { - if ($source instanceof DataObject\Concrete && $latestVersion = $source->getLatestVersion()) { - $source = $latestVersion->loadData(); - $source->setPublished(false); //as latest version is used which is not published - } - - if ($request->request->get('type') === 'child') { - $newObject = $this->_objectService->copyAsChild($target, $source); - - $sessionBag['idMapping'][(int)$source->getId()] = (int)$newObject->getId(); - - // this is because the key can get the prefix "_copy" if the target does already exists - if ($request->request->get('saveParentId')) { - $sessionBag['parentId'] = $newObject->getId(); - } - } elseif ($request->request->get('type') === 'replace') { - $concreteTarget = DataObject\Concrete::getById($target->getId()); - $concreteSource = DataObject\Concrete::getById($source->getId()); - $this->_objectService->copyContents($concreteTarget, $concreteSource); - } - - $session->set($request->request->get('transactionId'), $sessionBag); - - return $this->adminJson(['success' => true, 'message' => $message]); - } - - Logger::error("could not execute copy/paste, source object with id [ $sourceId ] not found"); - - return $this->adminJson(['success' => false, 'message' => 'source object not found']); - } - - throw $this->createAccessDeniedHttpException(); - } - - #[Route('/preview', name: 'preview', methods: ['GET'])] - public function previewAction(Request $request, PreviewGeneratorInterface $defaultPreviewGenerator): RedirectResponse|Response - { - $id = $request->query->getInt('id'); - $object = DataObject\Service::getElementFromSession('object', $id, $request->getSession()->getId()); - - if ($object instanceof DataObject\Concrete) { - $url = null; - if ($previewService = $object->getClass()->getPreviewGenerator()) { - $url = $previewService->generatePreviewUrl($object, ['preview' => true, 'context' => $this, ...$request->query->all()]); - } elseif ($object->getClass()->getLinkGenerator()) { - $parameters = [ - 'preview' => true, - 'context' => $this, - ]; - - $url = $defaultPreviewGenerator->generatePreviewUrl($object, [...$parameters, ...$request->query->all()]); - } - - if (!$url) { - throw new NotFoundHttpException('Cannot render preview due to empty URL'); - } - - // replace all remaining % signs - $url = str_replace('%', '%25', $url); - - $urlParts = parse_url($url); - - $redirectParameters = array_filter([ - 'opendxp_object_preview' => $id, - 'site' => $request->query->getInt(PreviewGeneratorInterface::PARAMETER_SITE), - 'dc' => time(), - ]); - - $redirectUrl = $urlParts['path'] . '?' . http_build_query($redirectParameters) . (isset($urlParts['query']) ? '&' . $urlParts['query'] : ''); - - return $this->redirect($redirectUrl); - } - - throw new NotFoundHttpException(sprintf('Expected an object of type "%s", got "%s"', DataObject\Concrete::class, get_debug_type($object))); - } - - protected function processRemoteOwnerRelations(DataObject\Concrete $object, array $toDelete, array $toAdd, string $ownerFieldName): void - { - $getter = 'get' . ucfirst($ownerFieldName); - $setter = 'set' . ucfirst($ownerFieldName); - - foreach ($toDelete as $id) { - $owner = DataObject::getById($id); - //TODO: lock ?! - if (method_exists($owner, $getter)) { - $currentData = $owner->$getter(); - if (is_array($currentData)) { - $counter = count($currentData); - for ($i = 0; $i < $counter; $i++) { - if ($currentData[$i]->getId() == $object->getId()) { - unset($currentData[$i]); - $owner->$setter($currentData); - - break; - } - } - } elseif ($currentData->getId() == $object->getId()) { - $owner->$setter(null); - } - } - $owner->setUserModification($this->getAdminUser()->getId()); - $owner->save(); - Logger::debug('Saved object id [ ' . $owner->getId() . ' ] by remote modification through [' . $object->getId() . '], Action: deleted [ ' . $object->getId() . " ] from [ $ownerFieldName]"); - } - - foreach ($toAdd as $id) { - $owner = DataObject::getById($id); - //TODO: lock ?! - if (method_exists($owner, $getter)) { - $currentData = $owner->$getter(); - if (is_array($currentData)) { - $currentData[] = $object; - } else { - $currentData = $object; - } - $owner->$setter($currentData); - $owner->setUserModification($this->getAdminUser()->getId()); - $owner->save(); - Logger::debug('Saved object id [ ' . $owner->getId() . ' ] by remote modification through [' . $object->getId() . '], Action: added [ ' . $object->getId() . " ] to [ $ownerFieldName ]"); - } - } - } - - protected function detectDeletedRemoteOwnerRelations(array $relations, array $value): array - { - $originals = []; - $changed = []; - foreach ($relations as $r) { - $originals[] = $r['dest_id']; - } - - foreach ($value as $row) { - $changed[] = $row['id']; - } - - return array_diff($originals, $changed); - } - - protected function detectAddedRemoteOwnerRelations(array $relations, array $value): array - { - $originals = []; - $changed = []; - - foreach ($relations as $r) { - $originals[] = $r['dest_id']; - } - - foreach ($value as $row) { - $changed[] = $row['id']; - } - - return array_diff($changed, $originals); - } - - protected function getLatestVersion(DataObject\Concrete $object, ?Model\Version &$draftVersion = null): DataObject\Concrete - { - $latestVersion = $object->getLatestVersion($this->getAdminUser()?->getId()); - if ($latestVersion) { - $latestObj = $latestVersion->loadData(); - if ($latestObj instanceof DataObject\Concrete) { - $draftVersion = $latestVersion; - - return $latestObj; - } - } - - return $object; - } - - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - // check permissions - $this->checkPermission('objects'); - - $this->_objectService = new DataObject\Service($this->getAdminUser()); - } } diff --git a/src/Controller/Admin/DataObject/DataObjectCopyController.php b/src/Controller/Admin/DataObject/DataObjectCopyController.php new file mode 100644 index 00000000..d846b0f8 --- /dev/null +++ b/src/Controller/Admin/DataObject/DataObjectCopyController.php @@ -0,0 +1,165 @@ +value)] +class DataObjectCopyController extends AdminAbstractController +{ + #[Route('/copy-info', name: 'copyinfo', methods: ['GET'])] + public function copyInfoAction( + GetDataObjectChildIdsHandler $getChildIds, + Request $request, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] int $sourceId = 0, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $targetId = null, + ): JsonResponse { + $transactionId = time(); + $pasteJobs = []; + + Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { + $session->set((string) $transactionId, ['idMapping' => []]); + }, 'opendxp_copy'); + + if ($type === 'recursive' || $type === 'recursive-update-references') { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $sourceId, + 'targetId' => $targetId, + 'type' => 'child', + 'transactionId' => $transactionId, + 'saveParentId' => true, + ], + ]]; + + $childIds = $getChildIds($sourceId)->ids; + + foreach ($childIds as $id) { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $id, + 'targetParentId' => $targetId, + 'sourceParentId' => $sourceId, + 'type' => 'child', + 'transactionId' => $transactionId, + ], + ]]; + } + + if ($type === 'recursive-update-references' && count($childIds) > 0) { + for ($i = 0; $i < (count($childIds) + 1); $i++) { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copyrewriteids'), + 'method' => 'PUT', + 'params' => [ + 'transactionId' => $transactionId, + '_dc' => uniqid('', false), + ], + ]]; + } + } + } elseif ($type === 'child' || $type === 'replace') { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $sourceId, + 'targetId' => $targetId, + 'type' => $type, + 'transactionId' => $transactionId, + ], + ]]; + } + + return $this->adminJson(['pastejobs' => $pasteJobs]); + } + + #[Route('/copy-rewrite-ids', name: 'copyrewriteids', methods: ['PUT'])] + public function copyRewriteIdsAction(RewriteDataObjectIdsHandler $rewriteIds, Request $request): JsonResponse + { + $transactionId = $request->request->get('transactionId'); + + $idStore = Tool\Session::useBag($request->getSession(), static fn (AttributeBagInterface $session) => $session->get($transactionId), 'opendxp_copy'); + + if (!array_key_exists('rewrite-stack', $idStore)) { + $idStore['rewrite-stack'] = array_values($idStore['idMapping']); + } + + $id = array_shift($idStore['rewrite-stack']); + + $rewriteIds((int) $id, $idStore['idMapping']); + + Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId, $idStore): void { + $session->set($transactionId, $idStore); + }, 'opendxp_copy'); + + return $this->adminJson(ApiResponse::ok(['id' => $id])); + } + + #[Route('/copy', name: 'copy', methods: ['POST'])] + public function copyAction(CopyDataObjectHandler $copyObject, Request $request): JsonResponse + { + $sourceId = $request->request->getInt('sourceId'); + $targetId = $request->request->getInt('targetId'); + $type = (string) $request->request->get('type'); + + $session = Tool\Session::getSessionBag($request->getSession(), 'opendxp_copy'); + $sessionBag = $session->get($request->request->get('transactionId')); + + $sourceParentId = $request->request->has('targetParentId') ? $request->request->getInt('sourceParentId') : null; + $targetParentId = $request->request->has('targetParentId') ? $request->request->getInt('targetParentId') : null; + $sessionParentId = !empty($sessionBag['parentId']) ? (int) $sessionBag['parentId'] : null; + + $result = $copyObject($sourceId, $targetId, $type, $sourceParentId, $targetParentId, $sessionParentId); + + if ($result->newObject !== null) { + $sessionBag['idMapping'][$result->sourceId] = $result->newObject->getId(); + + if ($request->request->get('saveParentId')) { + $sessionBag['parentId'] = $result->newObject->getId(); + } + + $session->set($request->request->get('transactionId'), $sessionBag); + } + + return $this->adminJson(ApiResponse::ok([ + 'message' => $result->newObject?->getRealFullPath() ?? '', + ])); + } +} diff --git a/src/Controller/Admin/DataObject/DataObjectHelperController.php b/src/Controller/Admin/DataObject/DataObjectHelperController.php index bb10862c..1e8f0916 100644 --- a/src/Controller/Admin/DataObject/DataObjectHelperController.php +++ b/src/Controller/Admin/DataObject/DataObjectHelperController.php @@ -16,43 +16,35 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; -use Doctrine\DBAL\ArrayParameterType; -use Doctrine\DBAL\ParameterType; -use Exception; -use InvalidArgumentException; -use League\Flysystem\FilesystemException; -use League\Flysystem\UnableToReadFile; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Helper\GridHelperService; -use OpenDxp\Bundle\AdminBundle\Model\GridConfig; -use OpenDxp\Bundle\AdminBundle\Model\GridConfigFavourite; -use OpenDxp\Bundle\AdminBundle\Model\GridConfigShare; -use OpenDxp\Bundle\AdminBundle\Service\GridData; -use OpenDxp\Config; -use OpenDxp\Db; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAllHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatchHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteDataObjectGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUploadHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFieldsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavouriteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\File; -use OpenDxp\Localization\LocaleServiceInterface; -use OpenDxp\Logger; -use OpenDxp\Model\DataObject; -use OpenDxp\Model\DataObject\Listing; -use OpenDxp\Model\User; -use OpenDxp\Security\SecurityHelper; use OpenDxp\Tool; -use OpenDxp\Tool\Storage; -use OpenDxp\Version; use stdClass; -use Symfony\Component\EventDispatcher\GenericEvent; -use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\File\UploadedFile; -use Symfony\Component\HttpFoundation\HeaderUtils; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal @@ -60,112 +52,33 @@ #[Route('/object-helper', name: 'opendxp_admin_dataobject_dataobjecthelper_')] class DataObjectHelperController extends AdminAbstractController { - public const array SYSTEM_COLUMNS = ['id', 'fullpath', 'key', 'published', 'creationDate', 'modificationDate', 'filename', 'classname']; + public function __construct(private readonly GridExportService $gridExportService) {} #[Route('/load-object-data', name: 'loadobjectdata', methods: ['GET'])] - public function loadObjectDataAction(Request $request): JsonResponse - { - $object = DataObject::getById((int) $request->query->get('id')); - $result = []; - if ($object) { - $result['success'] = true; - $fields = $request->query->all('fields'); - $result['fields'] = GridData\DataObject::getData($object, $fields); - } else { - $result['success'] = false; - } - - return $this->adminJson($result); - } - - public function getMyOwnGridColumnConfigs(int $userId, string $classId, ?string $searchType = null): array - { - $db = Db::get(); - $configListingConditionParts = []; - $configListingConditionParts[] = 'ownerId = ' . $userId; - $configListingConditionParts[] = 'classId = ' . $db->quote($classId); - - if ($searchType) { - $configListingConditionParts[] = 'searchType = ' . $db->quote($searchType); - } - - $configCondition = implode(' AND ', $configListingConditionParts); - $configListing = new GridConfig\Listing(); - $configListing->setOrderKey('name'); - $configListing->setOrder('ASC'); - $configListing->setCondition($configCondition); - $configListing = $configListing->load(); - - $configData = []; - foreach ($configListing as $config) { - $configData[] = $config->getObjectVars(); - } - - return $configData; - } - - public function getSharedGridColumnConfigs(User $user, string $classId, ?string $searchType = null): array - { - $configListing = []; - - $userIds = [$user->getId()]; - // collect all roles - $userIds = [...$userIds, ...$user->getRoles()]; - $db = Db::get(); - - $ids = $db->fetchFirstColumn( - 'SELECT DISTINCT c1.id FROM gridconfigs c1, gridconfig_shares s - WHERE (c1.searchType = ? AND c1.id = s.gridConfigId AND s.sharedWithUserId IN (?) AND c1.classId = ?) - UNION DISTINCT SELECT c2.id FROM gridconfigs c2 WHERE shareGlobally = 1 AND c2.classId = ? AND c2.ownerId != ?', - [$searchType, $userIds, $classId, $classId, $user->getId()], - [ParameterType::STRING, ArrayParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER] - ); - - if ($ids) { - $ids = implode(',', $ids); - $configListing = new GridConfig\Listing(); - $configListing->setOrderKey('name'); - $configListing->setOrder('ASC'); - $configListing->setCondition('id in (' . $ids . ')'); - $configListing = $configListing->load(); - } - - $configData = []; - foreach ($configListing as $config) { - $configData[] = $config->getObjectVars(); - } - - return $configData; + public function loadObjectDataAction( + LoadObjectDataHandler $handler, + Request $request, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['fields' => $handler($id, $request->query->all('fields'))])); } #[Route('/get-export-configs', name: 'getexportconfigs', methods: ['GET'])] - public function getExportConfigsAction(Request $request): JsonResponse - { - $result = []; - $classId = $request->query->get('classId'); - - $list = $this->getMyOwnGridColumnConfigs($this->getAdminUser()->getId(), $classId); - $list = [...$list, ...$this->getSharedGridColumnConfigs($this->getAdminUser(), $classId)]; - - $result[] = [ - 'id' => -1, - 'name' => '--default--', - ]; - - /** @var GridConfig $config */ - foreach ($list as $config) { - $result[] = [ - 'id' => $config['id'], - 'name' => $config['name'], - ]; - } - - return $this->adminJson(['success' => true, 'data' => $result]); + public function getExportConfigsAction( + GetExportConfigsHandler $getExportConfigs, + #[MapQueryParameter] ?string $classId = null, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $getExportConfigs($classId)])); } #[Route('/grid-delete-column-config', name: 'griddeletecolumnconfig', methods: ['DELETE'])] - public function gridDeleteColumnConfigAction(Request $request, EventDispatcherInterface $eventDispatcher, Config $config): JsonResponse - { + public function gridDeleteColumnConfigAction( + DeleteDataObjectGridColumnConfigHandler $deleteGridColumnConfig, + DeleteGridColumnConfigHandler $handler, + Request $request, + #[MapQueryParameter(name: 'no_system_columns')] bool $noSystemColumns = false, + #[MapQueryParameter(name: 'no_brick_columns')] bool $noBrickColumns = false, + ): JsonResponse { $params = [ 'id' => $request->request->get('id'), 'objectId' => $request->request->get('objectId'), @@ -175,962 +88,137 @@ public function gridDeleteColumnConfigAction(Request $request, EventDispatcherIn 'gridtype' => $request->request->get('gridtype'), 'gridConfigId' => $request->request->get('gridConfigId'), 'searchType' => $request->request->get('searchType'), - 'noSystemColumns' => $request->query->getBoolean('no_system_columns'), - 'noBrickColumns' => $request->query->getBoolean('no_brick_columns'), - 'locale' => $request->getLocale(), + 'noSystemColumns' => $noSystemColumns, + 'noBrickColumns' => $noBrickColumns, + 'locale' => $request->getLocale(), ]; - $gridConfigId = (int)$request->request->get('gridConfigId'); - $gridConfig = GridConfig::getById($gridConfigId); - $success = false; - if ($gridConfig) { - if ($gridConfig->getOwnerId() !== $this->getAdminUser()->getId() && !$this->getAdminUser()->isAdmin()) { - throw new Exception("don't mess with someone elses grid config"); - } - - $gridConfig->delete(); - $success = true; - } - - $newGridConfig = $this->doGetGridColumnConfig($request, $params, $config, true); - $newGridConfig['deleteSuccess'] = $success; + $deleteGridColumnConfig((int) $request->request->get('gridConfigId')); - $event = new GenericEvent($this, [ - 'data' => $newGridConfig, - 'request' => $request, - 'config' => $config, - 'context' => 'delete', - ]); - - $eventDispatcher->dispatch($event, AdminEvents::OBJECT_GRID_GET_COLUMN_CONFIG_PRE_SEND_DATA); - $newGridConfig = $event->getArgument('data'); - - return $this->adminJson($newGridConfig); + return $this->adminJson($handler($request, $params)); } #[Route('/grid-get-column-config', name: 'gridgetcolumnconfig', methods: ['GET'])] - public function gridGetColumnConfigAction(Request $request, EventDispatcherInterface $eventDispatcher, Config $config): JsonResponse - { + public function gridGetColumnConfigAction( + GetGridColumnConfigHandler $handler, + Request $request, + #[MapQueryParameter] ?string $id = null, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $objectId = null, + #[MapQueryParameter] ?string $name = null, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] ?string $types = null, + #[MapQueryParameter] ?string $gridtype = null, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $gridConfigId = null, + #[MapQueryParameter] ?string $searchType = null, + #[MapQueryParameter(name: 'no_system_columns')] bool $noSystemColumns = false, + #[MapQueryParameter(name: 'no_brick_columns')] bool $noBrickColumns = false, + ): JsonResponse { $params = [ - 'id' => $request->query->get('id'), - 'objectId' => $request->query->get('objectId'), - 'name' => $request->query->get('name'), - 'type' => $request->query->get('type'), - 'types' => $request->query->get('types'), - 'gridtype' => $request->query->get('gridtype'), - 'gridConfigId' => $request->query->get('gridConfigId'), - 'searchType' => $request->query->get('searchType'), - 'noSystemColumns' => $request->query->getBoolean('no_system_columns'), - 'noBrickColumns' => $request->query->getBoolean('no_brick_columns'), + 'id' => $id, + 'objectId' => $objectId, + 'name' => $name, + 'type' => $type, + 'types' => $types, + 'gridtype' => $gridtype, + 'gridConfigId' => $gridConfigId, + 'searchType' => $searchType, + 'noSystemColumns' => $noSystemColumns, + 'noBrickColumns' => $noBrickColumns, ]; - $result = $this->doGetGridColumnConfig($request, $params, $config); - - $event = new GenericEvent($this, [ - 'data' => $result, - 'request' => $request, - 'config' => $config, - 'context' => 'get', - ]); - - $eventDispatcher->dispatch($event, AdminEvents::OBJECT_GRID_GET_COLUMN_CONFIG_PRE_SEND_DATA); - $result = $event->getArgument('data'); - - return $this->adminJson($result); - } - - private function doGetGridColumnConfig(Request $request, array $params, Config $config, bool $isDelete = false): array - { - $class = null; - $fields = null; - - if ($params['id'] !== null) { - $class = DataObject\ClassDefinition::getById($params['id']); - } elseif ($params['name'] !== null) { - $class = DataObject\ClassDefinition::getByName($params['name']); - } - - $gridConfigId = null; - $gridType = 'search'; - if ($params['gridtype'] !== null) { - $gridType = $params['gridtype']; - } - - $objectId = $params['objectId'] !== null ? (int) $params['objectId'] : 0; - - if ($objectId) { - $fields = DataObject\Service::getCustomGridFieldDefinitions($class->getId(), $objectId); - } - - $context = ['purpose' => 'gridconfig']; - if ($class) { - $context['class'] = $class; - } - - if ($objectId) { - $object = DataObject::getById($objectId); - $context['object'] = $object; - } - - if (!$fields && $class) { - $fields = $class->getFieldDefinitions(); - } - - $types = []; - if ($params['types'] !== null) { - $types = explode(',', $params['types']); - } - - $userId = $this->getAdminUser()->getId(); - - $requestedGridConfigId = $isDelete ? null : $params['gridConfigId']; - - // grid config - $gridConfig = []; - $searchType = $params['searchType']; - - if ((string) ($requestedGridConfigId ?? '') === '' && $class) { - // check if there is a favourite view - $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId($userId, $class->getId(), $objectId ?: 0, $searchType); - if (!$favourite && $objectId) { - $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId($userId, $class->getId(), 0, $searchType); - } - - if ($favourite) { - $requestedGridConfigId = $favourite->getGridConfigId(); - } - } - - if (is_numeric($requestedGridConfigId) && $requestedGridConfigId > 0) { - $db = Db::get(); - $savedGridConfig = GridConfig::getById((int) $requestedGridConfigId); - - if ($savedGridConfig) { - $shared = false; - if (!$this->getAdminUser()->isAdmin()) { - $userIds = [$this->getAdminUser()->getId()]; - $userIds = [...$userIds, ...$this->getAdminUser()->getRoles()]; - $isSharedGlobally = $savedGridConfig->getOwnerId() !== $userId && $savedGridConfig->isShareGlobally(); - - $isSharedWithUser = (bool) $db->fetchOne( - 'SELECT 1 FROM gridconfig_shares WHERE sharedWithUserId IN (?) AND gridConfigId = ?', - [$userIds, $savedGridConfig->getId()], - [ArrayParameterType::INTEGER, ParameterType::INTEGER] - ); - - $shared = $isSharedGlobally || $isSharedWithUser; - - if (!$shared && $savedGridConfig->getOwnerId() !== $this->getAdminUser()->getId()) { - throw new Exception('You are neither the owner of this config nor it is shared with you'); - } - } - - $gridConfigId = $savedGridConfig->getId(); - $gridConfig = $savedGridConfig->getConfig(); - $gridConfig = json_decode($gridConfig, true); - $gridConfigName = SecurityHelper::convertHtmlSpecialChars($savedGridConfig->getName()); - $owner = $savedGridConfig->getOwnerId(); - $ownerObject = User::getById($owner); - if ($ownerObject instanceof User) { - $owner = $ownerObject->getName(); - } - $modificationDate = $savedGridConfig->getModificationDate(); - $gridConfigDescription = SecurityHelper::convertHtmlSpecialChars($savedGridConfig->getDescription()); - $sharedGlobally = $savedGridConfig->isShareGlobally(); - $setAsFavourite = $savedGridConfig->isSetAsFavourite(); - $saveFilters = $savedGridConfig->isSaveFilters(); - - foreach ($gridConfig['columns'] as &$column) { - if (array_key_exists('isOperator', $column) && $column['isOperator']) { - $colAttributes = &$column['fieldConfig']['attributes']; - SecurityHelper::convertHtmlSpecialCharsArrayKeys($colAttributes, ['label', 'attribute', 'param1']); - } - } - } - } - - $localizedFields = []; - if (is_array($fields)) { - foreach ($fields as $field) { - if ($field instanceof DataObject\ClassDefinition\Data\Localizedfields) { - $localizedFields[] = $field; - } - } - } - - $availableFields = []; - - if (empty($gridConfig)) { - $availableFields = $this->getDefaultGridFields( - $params['noSystemColumns'], - $class, - $gridType, - $params['noBrickColumns'], - $fields, - $context, - $objectId, - $types - ); - } else { - $savedColumns = $gridConfig['columns']; - foreach ($savedColumns as $key => $sc) { - if (!$sc['hidden']) { - if (in_array($key, self::SYSTEM_COLUMNS)) { - $colConfig = [ - 'key' => $key, - 'type' => 'system', - 'label' => $key, - 'position' => $sc['position'], - ]; - $this->injectCustomLayoutValues($colConfig, $sc); - $availableFields[] = $colConfig; - } else { - $keyParts = explode('~', $key); - - if (str_starts_with($key, '~')) { - // not needed for now - $type = $keyParts[1]; - $groupAndKeyId = explode('-', $keyParts[3]); - $keyId = (int) $groupAndKeyId[1]; - - if ($type === 'classificationstore') { - $keyDef = DataObject\Classificationstore\KeyConfig::getById($keyId); - if ($keyDef) { - $keyFieldDef = json_decode($keyDef->getDefinition(), true); - if ($keyFieldDef) { - $keyFieldDef = \OpenDxp\Model\DataObject\Classificationstore\Service::getFieldDefinitionFromJson($keyFieldDef, $keyDef->getType()); - $fieldConfig = $this->getFieldGridConfig($keyFieldDef, $gridType, (string)$sc['position'], true, null, $class, $objectId); - if ($fieldConfig) { - $fieldConfig['key'] = $key; - $fieldConfig['label'] = '#' . $keyFieldDef->getTitle(); - $fieldConfig = $this->injectCustomLayoutValues($fieldConfig, $sc); - $availableFields[] = $fieldConfig; - } - } - } - } - } elseif (count($keyParts) > 1) { - $brick = $keyParts[0]; - $brickDescriptor = null; - - if (str_contains($brick, '?')) { - $brickDescriptor = substr($brick, 1); - $brickDescriptor = json_decode($brickDescriptor, true); - $keyPrefix = $brick . '~'; - $brick = $brickDescriptor['containerKey']; - } else { - $keyPrefix = $brick . '~'; - } - - $fieldname = $keyParts[1]; - - $brickClass = DataObject\Objectbrick\Definition::getByKey($brick); - - $fd = null; - if ($brickClass instanceof DataObject\Objectbrick\Definition) { - if ($brickDescriptor) { - $innerContainer = $brickDescriptor['innerContainer'] ?? 'localizedfields'; - /** @var DataObject\ClassDefinition\Data\Localizedfields $localizedFields */ - $localizedFields = $brickClass->getFieldDefinition($innerContainer); - $fd = $localizedFields->getFieldDefinition($brickDescriptor['brickfield']); - } else { - $fd = $brickClass->getFieldDefinition($fieldname); - } - } - - if ($fd !== null) { - $fieldConfig = $this->getFieldGridConfig($fd, $gridType, (string)$sc['position'], true, $keyPrefix, $class, $objectId); - if (!empty($fieldConfig)) { - $fieldConfig = $this->injectCustomLayoutValues($fieldConfig, $sc); - $availableFields[] = $fieldConfig; - } - } - } elseif (DataObject\Service::isHelperGridColumnConfig($key)) { - $calculatedColumnConfig = $this->getCalculatedColumnConfig($request, $savedColumns[$key]); - if ($calculatedColumnConfig) { - $availableFields[] = $calculatedColumnConfig; - } - } else { - $fd = $class->getFieldDefinition($key); - //if not found, look for localized fields - if (empty($fd)) { - foreach ($localizedFields as $lf) { - $fd = $lf->getFieldDefinition($key); - if (!empty($fd)) { - break; - } - } - } - - if (!empty($fd)) { - $fieldConfig = $this->getFieldGridConfig($fd, $gridType, (string)$sc['position'], true, null, $class, $objectId); - if (!empty($fieldConfig)) { - $fieldConfig = $this->injectCustomLayoutValues($fieldConfig, $sc); - $availableFields[] = $fieldConfig; - } - } - } - } - } - } - } - - usort($availableFields, static fn ($a, $b) => $a['position'] <=> $b['position']); - - $frontendLanguages = Tool\Admin::reorderWebsiteLanguages(\OpenDxp\Tool\Admin::getCurrentUser(), $config['general']['valid_languages']); - $language = $frontendLanguages ? $frontendLanguages[0] : $request->getLocale(); - - if (!Tool::isValidLanguage($language)) { - $validLanguages = Tool::getValidLanguages(); - $language = $validLanguages[0]; - } - - if (!empty($gridConfig) && !empty($gridConfig['language'])) { - $language = $gridConfig['language']; - } - - $availableConfigs = $class ? $this->getMyOwnGridColumnConfigs($userId, $class->getId(), $searchType) : []; - $sharedConfigs = $class ? $this->getSharedGridColumnConfigs($this->getAdminUser(), $class->getId(), $searchType) : []; - $settings = $this->getShareSettings((int)$gridConfigId); - $settings['gridConfigId'] = (int)$gridConfigId; - $settings['gridConfigName'] = $gridConfigName ?? null; - $settings['gridConfigDescription'] = $gridConfigDescription ?? null; - $settings['owner'] = $owner ?? null; - $settings['modificationDate'] = $modificationDate ?? null; - $settings['shareGlobally'] = $sharedGlobally ?? null; - $settings['setAsFavourite'] = $setAsFavourite ?? null; - $settings['saveFilters'] = $saveFilters ?? null; - $settings['isShared'] = !$gridConfigId || ($shared ?? null); - $settings['allowVariants'] = $class && $class->getAllowVariants(); - - $context = $gridConfig['context'] ?? null; - if ($context) { - $context = json_decode($context, true); - } - - return [ - 'sortinfo' => $gridConfig['sortinfo'] ?? false, - 'language' => $language, - 'availableFields' => $availableFields, - 'settings' => $settings, - 'onlyDirectChildren' => $gridConfig['onlyDirectChildren'] ?? false, - 'pageSize' => $gridConfig['pageSize'] ?? false, - 'availableConfigs' => $availableConfigs, - 'sharedConfigs' => $sharedConfigs, - 'context' => $context, - 'searchFilter' => $gridConfig['searchFilter'] ?? '', - 'filter' => $gridConfig['filter'] ?? [], - ]; - } - - private function injectCustomLayoutValues(array $fieldConfig, array $savedColumn): array - { - $keys = ['width', 'locked']; - foreach ($keys as $key) { - if (isset($savedColumn[$key])) { - $fieldConfig[$key] = $savedColumn[$key]; - } - } - - $fieldConfigKeys = ['noteditable']; - foreach ($fieldConfigKeys as $fieldConfigKey) { - if (isset($savedColumn['fieldConfig']['layout'][$fieldConfigKey])) { - $setter = 'set' . ucfirst($fieldConfigKey); - $fieldConfig['layout']->$setter($savedColumn['fieldConfig']['layout'][$fieldConfigKey]); - } - } - - return $fieldConfig; - } - - /** - * @param DataObject\ClassDefinition\Data[]|null $fields - */ - public function getDefaultGridFields(bool $noSystemColumns, ?DataObject\ClassDefinition $class, string $gridType, bool $noBrickColumns, ?array $fields, array $context, int $objectId, array $types = []): array - { - $count = 0; - $availableFields = []; - - if (!$noSystemColumns && $class) { - $vis = $class->getPropertyVisibility(); - foreach (self::SYSTEM_COLUMNS as $sc) { - $key = $sc; - if ($key === 'fullpath') { - $key = 'path'; - } - - if ($types === [] && (!empty($vis[$gridType][$key]) || $gridType === 'all')) { - $availableFields[] = [ - 'key' => $sc, - 'type' => 'system', - 'label' => $sc, - 'position' => $count, ]; - $count++; - } - } - } - - $includeBricks = !$noBrickColumns; - - if (is_array($fields)) { - foreach ($fields as $field) { - if ($field instanceof DataObject\ClassDefinition\Data\Localizedfields) { - foreach ($field->getFieldDefinitions($context) as $fd) { - if ($types === [] || in_array($fd->getFieldType(), $types)) { - $fieldConfig = $this->getFieldGridConfig($fd, $gridType, (string)$count, false, null, $class, $objectId); - if (!empty($fieldConfig)) { - $availableFields[] = $fieldConfig; - $count++; - } - } - } - } elseif ($field instanceof DataObject\ClassDefinition\Data\Objectbricks && $includeBricks) { - if (in_array($field->getFieldType(), $types)) { - $fieldConfig = $this->getFieldGridConfig($field, $gridType, (string)$count, false, null, $class, $objectId); - if (!empty($fieldConfig)) { - $availableFields[] = $fieldConfig; - $count++; - } - } else { - $allowedTypes = $field->getAllowedTypes(); - foreach ($allowedTypes as $t) { - $brickClass = DataObject\Objectbrick\Definition::getByKey($t); - $brickFields = $brickClass->getFieldDefinitions($context); - - $this->appendBrickFields($field, $brickFields, $availableFields, $gridType, $count, $t, $class, $objectId); - } - } - } elseif ($types === [] || in_array($field->getFieldType(), $types)) { - $fieldConfig = $this->getFieldGridConfig($field, $gridType, (string)$count, $types !== [], null, $class, $objectId); - if (!empty($fieldConfig)) { - $availableFields[] = $fieldConfig; - $count++; - } - } - } - } - - return $availableFields; - } - - /** - * @param DataObject\ClassDefinition\Data[] $brickFields - */ - protected function appendBrickFields(DataObject\ClassDefinition\Data $field, array $brickFields, array &$availableFields, string $gridType, int &$count, string $brickType, DataObject\ClassDefinition $class, int $objectId, ?array $context = null): void - { - foreach ($brickFields as $bf) { - if ($bf instanceof DataObject\ClassDefinition\Data\Localizedfields) { - $localizedFieldDefinitions = $bf->getFieldDefinitions(); - - $localizedContext = [ - 'containerKey' => $brickType, - 'fieldname' => $field->getName(), - ]; - - $this->appendBrickFields($bf, $localizedFieldDefinitions, $availableFields, $gridType, $count, $brickType, $class, $objectId, $localizedContext); - } else { - if ($context) { - $context['brickfield'] = $bf->getName(); - $keyPrefix = '?' . json_encode($context) . '~'; - } else { - $keyPrefix = $brickType . '~'; - } - $fieldConfig = $this->getFieldGridConfig($bf, $gridType, (string)$count, false, $keyPrefix, $class, $objectId); - if (!empty($fieldConfig)) { - $availableFields[] = $fieldConfig; - $count++; - } - } - } - } - - protected function getCalculatedColumnConfig(Request $request, array $config): mixed - { - try { - return Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($config) { - - $existingKey = $config['fieldConfig']['key']; - $calculatedColumnConfig['key'] = $existingKey; - $calculatedColumnConfig['position'] = $config['position']; - $calculatedColumnConfig['isOperator'] = true; - $calculatedColumnConfig['attributes'] = $config['fieldConfig']['attributes']; - $calculatedColumnConfig['width'] = $config['width']; - $calculatedColumnConfig['locked'] = $config['locked']; - - $existingColumns = $session->get('helpercolumns', []); - - if (isset($existingColumns[$existingKey])) { - // if the configuration is still in the session, then reuse it - return $calculatedColumnConfig; - } - - $newKey = '#' . uniqid('', false); - $calculatedColumnConfig['key'] = $newKey; - - // prepare a column config on the fly - $phpConfig = json_encode($config['fieldConfig']); - $phpConfig = json_decode($phpConfig); - $helperColumns = []; - $helperColumns[$newKey] = $phpConfig; - - $helperColumns = [...$helperColumns, ...$existingColumns]; - $session->set('helpercolumns', $helperColumns); - - return $calculatedColumnConfig; - }, 'opendxp_gridconfig'); - } catch (Exception $e) { - Logger::error((string) $e); - } - - return null; + return $this->adminJson($handler($request, $params)); } #[Route('/prepare-helper-column-configs', name: 'preparehelpercolumnconfigs', methods: ['POST'])] - public function prepareHelperColumnConfigs(Request $request): JsonResponse + public function prepareHelperColumnConfigs(Request $request, PrepareHelperColumnConfigsHandler $prepareHelperColumns): JsonResponse { - $helperColumns = []; - $newData = []; - /** @var stdClass[] $data */ - $data = json_decode($request->request->get('columns')); - foreach ($data as $item) { - if (!empty($item->isOperator)) { - $itemKey = '#' . uniqid('', false); + /** @var stdClass[] $columns */ + $columns = json_decode($request->request->get('columns')); - $item->key = $itemKey; - $newData[] = $item; - $helperColumns[$itemKey] = $item; - } else { - $newData[] = $item; - } - } + $existingHelperColumns = Tool\Session::useBag( + $request->getSession(), + static fn(AttributeBagInterface $bag) => $bag->get('helpercolumns', []), + 'opendxp_gridconfig', + ); - Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($helperColumns): void { - $existingColumns = $session->get('helpercolumns', []); - $helperColumns = [...$helperColumns, ...$existingColumns]; - $session->set('helpercolumns', $helperColumns); - }, 'opendxp_gridconfig'); + $result = $prepareHelperColumns($columns, $existingHelperColumns); - return $this->adminJson(['success' => true, 'columns' => $newData]); + Tool\Session::useBag( + $request->getSession(), + static function (AttributeBagInterface $bag) use ($result): void { + $bag->set('helpercolumns', $result['helperColumns']); + }, + 'opendxp_gridconfig', + ); + + return $this->adminJson(ApiResponse::ok(['columns' => $result['newData']])); } #[Route('/grid-config-apply-to-all', name: 'gridconfigapplytoall', methods: ['POST'])] - public function gridConfigApplyToAllAction(Request $request): JsonResponse + public function gridConfigApplyToAllAction(ApplyGridConfigToAllHandler $applyToAll, Request $request): JsonResponse { - $objectId = $request->request->getInt('objectId'); - $object = DataObject::getById($objectId); - - if ($object->isAllowed('list')) { - $classId = $request->request->get('classId'); - $searchType = $request->request->get('searchType'); - $user = $this->getAdminUser(); - $db = Db::get(); - $db->executeStatement( - 'DELETE FROM gridconfig_favourites WHERE ownerId = ? AND classId = ? AND searchType = ? AND objectId != ? AND objectId != 0', - [$user->getId(), $classId, $searchType, $objectId] + $applyToAll( + $request->request->getInt('objectId'), + (string) $request->request->get('classId'), + (string) $request->request->get('searchType'), ); - return $this->adminJson(['success' => true]); - } - - throw $this->createAccessDeniedHttpException(); + return $this->adminJson(ApiResponse::ok()); } #[Route('/grid-mark-favourite-column-config', name: 'gridmarkfavouritecolumnconfig', methods: ['POST'])] - public function gridMarkFavouriteColumnConfigAction(Request $request): JsonResponse - { - $objectId = $request->request->getInt('objectId'); - $object = DataObject::getById($objectId); - - if ($object->isAllowed('list')) { - $classId = $request->request->get('classId'); - $gridConfigId = $request->request->get('gridConfigId'); - $searchType = $request->request->get('searchType'); - $global = $request->request->get('global'); - $user = $this->getAdminUser(); - $type = $request->request->get('type'); - - $favourite = new GridConfigFavourite(); - $favourite->setOwnerId($user->getId()); - $class = DataObject\ClassDefinition::getById($classId); - if (!$class) { - throw new Exception('class ' . $classId . ' does not exist anymore'); - } - $favourite->setClassId($classId); - $favourite->setSearchType($searchType); - $favourite->setType($type); - $specializedConfigs = false; - - try { - if ($gridConfigId != 0) { - $gridConfig = GridConfig::getById((int)$gridConfigId); - $favourite->setGridConfigId($gridConfig->getId()); - } - $favourite->setObjectId($objectId); - $favourite->save(); - - if ($global) { - $favourite->setObjectId(0); - $favourite->save(); - } - $db = Db::get(); - $count = $db->fetchOne( - 'SELECT * FROM gridconfig_favourites WHERE ownerId = ? AND classId = ? AND searchType = ? AND objectId != ? AND objectId != 0 AND `type` != ?', - [$user->getId(), $classId, $searchType, $objectId, $type] - ); - $specializedConfigs = $count > 0; - } catch (Exception) { - $favourite->delete(); - } - - return $this->adminJson(['success' => true, 'specializedConfigs' => $specializedConfigs]); - } - - throw $this->createAccessDeniedHttpException(); - } - - protected function getShareSettings(int $gridConfigId): array - { - $result = [ - 'sharedUserIds' => [], - 'sharedRoleIds' => [], - ]; - - $db = Db::get(); - $allShares = $db->fetchAllAssociative( - 'SELECT s.sharedWithUserId, u.type FROM gridconfig_shares s, users u - WHERE s.sharedWithUserId = u.id AND s.gridConfigId = ?', - [$gridConfigId] - ); - - foreach ($allShares as $share) { - $type = $share['type']; - $key = 'shared' . ucfirst($type) . 'Ids'; - $result[$key][] = $share['sharedWithUserId']; - } - - foreach ($result as $idx => $value) { - $value = $value ? implode(',', $value) : ''; - $result[$idx] = $value; - } + public function gridMarkFavouriteColumnConfigAction( + MarkDataObjectGridConfigFavouriteHandler $markFavourite, + Request $request, + ): JsonResponse { + $result = $markFavourite( + $request->request->getInt('objectId'), + $request->request->get('classId'), + (int) $request->request->get('gridConfigId'), + $request->request->get('searchType'), + (bool) $request->request->get('global'), + $request->request->get('type'), + ); - return $result; + return $this->adminJson(ApiResponse::ok(['specializedConfigs' => $result->specializedConfigs])); } #[Route('/grid-save-column-config', name: 'gridsavecolumnconfig', methods: ['POST'])] - public function gridSaveColumnConfigAction(Request $request): JsonResponse - { - $objectId = $request->request->getInt('id'); - $object = DataObject::getById($objectId); - - if ($object->isAllowed('list')) { - try { - $classId = $request->request->get('class_id'); - $context = $request->request->get('context'); - - $searchType = $request->request->get('searchType'); - - // grid config - $gridConfigData = $this->decodeJson($request->request->get('gridconfig')); - $gridConfigData['opendxp_version'] = Version::getVersion(); - $gridConfigData['opendxp_revision'] = Version::getRevision(); - - $gridConfigData['context'] = $context; - - unset($gridConfigData['settings']['isShared']); - - $metadata = $request->request->get('settings'); - $metadata = json_decode($metadata, true); - - $gridConfigId = $metadata['gridConfigId']; - $gridConfig = null; - if ($gridConfigId) { - $gridConfig = GridConfig::getById($gridConfigId); - } - - if ($gridConfig && $gridConfig->getOwnerId() !== $this->getAdminUser()->getId() && !$this->getAdminUser()->isAdmin()) { - throw new Exception("don't mess around with somebody elses configuration"); - } - - $this->updateGridConfigShares($gridConfig, $metadata); - - if ($metadata['setAsFavourite'] && $this->getAdminUser()->isAdmin()) { - $this->updateGridConfigFavourites($gridConfig, $metadata, $objectId); - } - - if (!$gridConfig) { - $gridConfig = new GridConfig(); - $gridConfig->setName(date('c')); - $gridConfig->setClassId($classId); - $gridConfig->setSearchType($searchType); - - $gridConfig->setOwnerId($this->getAdminUser()->getId()); - } - - if ($metadata) { - $gridConfig->setName(SecurityHelper::convertHtmlSpecialChars($metadata['gridConfigName'])); - $gridConfig->setDescription(SecurityHelper::convertHtmlSpecialChars($metadata['gridConfigDescription'])); - $gridConfig->setShareGlobally($metadata['shareGlobally'] && $this->getAdminUser()->isAdmin()); - $gridConfig->setSetAsFavourite($metadata['setAsFavourite'] && $this->getAdminUser()->isAdmin()); - $gridConfig->setSaveFilters($metadata['saveFilters'] ?? false); - } - - $gridConfigData = json_encode($gridConfigData); - $gridConfig->setConfig($gridConfigData); - $gridConfig->save(); - - $userId = $this->getAdminUser()->getId(); - - $availableConfigs = $this->getMyOwnGridColumnConfigs($userId, $classId, $searchType); - $sharedConfigs = $this->getSharedGridColumnConfigs($this->getAdminUser(), $classId, $searchType); - - $settings = $this->getShareSettings($gridConfig->getId()); - $settings['gridConfigId'] = (int)$gridConfig->getId(); - $settings['gridConfigName'] = SecurityHelper::convertHtmlSpecialChars($gridConfig->getName()); - $settings['gridConfigDescription'] = SecurityHelper::convertHtmlSpecialChars($gridConfig->getDescription()); - $settings['shareGlobally'] = $gridConfig->isShareGlobally(); - $settings['setAsFavourite'] = $gridConfig->isSetAsFavourite(); - $settings['saveFilters'] = $gridConfig->isSaveFilters(); - $settings['isShared'] = $gridConfig->getOwnerId() !== $this->getAdminUser()->getId() && !$this->getAdminUser()->isAdmin(); - - return $this->adminJson([ - 'success' => true, - 'settings' => $settings, - 'availableConfigs' => $availableConfigs, - 'sharedConfigs' => $sharedConfigs, - ]); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - throw $this->createAccessDeniedHttpException(); - } - - /** - * @throws Exception - */ - protected function updateGridConfigShares(?GridConfig $gridConfig, array $metadata): void - { - $user = $this->getAdminUser(); - if (!$gridConfig || !$user->isAllowed('share_configurations')) { - // nothing to do - return; - } - - if ($gridConfig->getOwnerId() !== $user->getId() && !$user->isAdmin()) { - throw new Exception("don't mess with someone elses grid config"); - } - $combinedShares = []; - $sharedUserIds = $metadata['sharedUserIds']; - $sharedRoleIds = $metadata['sharedRoleIds']; - - if ($sharedUserIds) { - $combinedShares = explode(',', $sharedUserIds); - } - - if ($sharedRoleIds) { - $sharedRoleIds = explode(',', $sharedRoleIds); - $combinedShares = [...$combinedShares, ...$sharedRoleIds]; - } - - $db = Db::get(); - $db->delete('gridconfig_shares', ['gridConfigId' => $gridConfig->getId()]); - - foreach ($combinedShares as $id) { - $share = new GridConfigShare(); - $share->setGridConfigId($gridConfig->getId()); - $share->setSharedWithUserId((int) $id); - $share->save(); - } - } - - /** - * @throws Exception - */ - protected function updateGridConfigFavourites(?GridConfig $gridConfig, array $metadata, int $objectId): void - { - $currentUser = $this->getAdminUser(); - - if (!$gridConfig || $currentUser === null || !$currentUser->isAllowed('share_configurations')) { - // nothing to do - return; - } - - if (!$currentUser->isAdmin() && (int) $gridConfig->getOwnerId() !== $currentUser->getId()) { - throw new Exception("don't mess with someone elses grid config"); - } - - $sharedUsers = []; - - if ($metadata['shareGlobally'] === false) { - $sharedUserIds = $metadata['sharedUserIds']; - - if ($sharedUserIds) { - $sharedUsers = array_map(intval(...), explode(',', $sharedUserIds)); - } - } - - if ($metadata['shareGlobally'] === true) { - $users = new User\Listing(); - $users->setCondition('id = ?', $currentUser->getId()); - - foreach ($users as $user) { - $sharedUsers[] = $user->getId(); - } - } - - foreach ($sharedUsers as $id) { - $global = true; - $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId( - $id, - $gridConfig->getClassId(), - $objectId, - $gridConfig->getSearchType() - ); - - // If the user has already a favourite for that object we check the current favourite and decide if we update - if ($favourite instanceof GridConfigFavourite) { - $favouriteGridConfig = GridConfig::getById($favourite->getGridConfigId()); - - if ($favouriteGridConfig instanceof GridConfig) { - // Check if the grid config was shared globally if that is *not* the case we also not update - if ($favouriteGridConfig->isShareGlobally() === false) { - continue; - } - - // Check if the user is the owner. If that is the case we do not update the favourite - if ($favouriteGridConfig->getOwnerId() === $id) { - continue; - } - } - } - - // Check if the user has already a global favourite then we do not save the favourite as global - $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId( - $id, - $gridConfig->getClassId(), - 0, - $gridConfig->getSearchType() + public function gridSaveColumnConfigAction( + SaveDataObjectGridColumnConfigHandler $saveGridColumnConfig, + Request $request, + ): JsonResponse { + $gridConfigData = $this->decodeJson($request->request->get('gridconfig')); + $metadata = json_decode($request->request->get('settings'), true); + + $result = $saveGridColumnConfig( + $request->request->getInt('id'), + $request->request->get('class_id'), + $request->request->get('context'), + $request->request->get('searchType'), + $gridConfigData, + $metadata, ); - if ($favourite instanceof GridConfigFavourite) { - $favouriteGridConfig = GridConfig::getById($favourite->getGridConfigId()); - - if ($favouriteGridConfig instanceof GridConfig) { - // Check if the grid config was shared globally if that is *not* the case we also not update - if ($favouriteGridConfig->isShareGlobally() === false) { - $global = false; - } - - // Check if the user is the owner. If that is the case we do not update the global favourite - if ($favouriteGridConfig->getOwnerId() === $id) { - $global = false; - } - } - } - - $favourite = new GridConfigFavourite(); - $favourite->setGridConfigId($gridConfig->getId()); - $favourite->setClassId($gridConfig->getClassId()); - $favourite->setObjectId($objectId); - $favourite->setOwnerId($id); - $favourite->setType($gridConfig->getType()); - $favourite->setSearchType($gridConfig->getSearchType()); - $favourite->save(); - - if ($global) { - $favourite->setObjectId(0); - $favourite->save(); - } - } - } - - protected function getFieldGridConfig(DataObject\ClassDefinition\Data $field, string $gridType, string $position, bool $force = false, ?string $keyPrefix = null, ?DataObject\ClassDefinition $class = null, ?int $objectId = null): ?array - { - $key = $keyPrefix . $field->getName(); - $config = null; - $title = $field->getName(); - - if (!empty($field->getTitle())) { - $title = $field->getTitle(); - } - - if ($field instanceof DataObject\ClassDefinition\Data\Slider) { - $config['minValue'] = $field->getMinValue(); - $config['maxValue'] = $field->getMaxValue(); - $config['increment'] = $field->getIncrement(); - } - - if (method_exists($field, 'getWidth')) { - $config['width'] = $field->getWidth(); - } - if (method_exists($field, 'getHeight')) { - $config['height'] = $field->getHeight(); - } - - $visible = false; - if ($gridType === 'search') { - $visible = $field->getVisibleSearch(); - } elseif ($gridType === 'grid') { - $visible = $field->getVisibleGridView(); - } elseif ($gridType === 'all') { - $visible = true; - } - - if (!$field->getInvisible() && ($force || $visible)) { - $context = ['purpose' => 'gridconfig']; - if ($class) { - $context['class'] = $class; - } - - if ($objectId) { - $object = DataObject::getById($objectId); - $context['object'] = $object; - } - DataObject\Service::enrichLayoutDefinition($field, null, $context); - - $result = [ - 'key' => $key, - 'type' => $field->getFieldType(), - 'label' => $title, - 'config' => $config, - 'layout' => $field, - 'position' => $position, - ]; - - if ($field instanceof DataObject\ClassDefinition\Data\EncryptedField) { - $result['delegateDatatype'] = $field->getDelegateDatatype(); - } - - return $result; - } - - return null; + return $this->adminJson(ApiResponse::ok([ + 'settings' => $result->settings, + 'availableConfigs' => $result->availableConfigs, + 'sharedConfigs' => $result->sharedConfigs, + ])); } /** * IMPORTER */ #[Route('/import-upload', name: 'importupload', methods: ['POST'])] - public function importUploadAction(Request $request, Filesystem $filesystem): JsonResponse + public function importUploadAction(Request $request, ImportUploadHandler $importUpload): JsonResponse { /** @var UploadedFile $file */ $file = $request->files->get('Filedata'); - $data = file_get_contents($file->getPathname()); - $data = Tool\Text::convertToUTF8($data); - - $importId = $request->request->get('importId'); - $importId = str_replace('..', '', $importId); - $importFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/import_' . $importId; - $filesystem->dumpFile($importFile, $data); + $importUpload(file_get_contents($file->getPathname()), (string) $request->request->get('importId')); - $importFileOriginal = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/import_' . $importId . '_original'; - $filesystem->dumpFile($importFileOriginal, $data); - - $response = $this->adminJson([ - 'success' => true, - ]); + $response = $this->adminJson(ApiResponse::ok()); // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in // Ext.form.Action.Submit and mark the submission as failed @@ -1139,572 +227,119 @@ public function importUploadAction(Request $request, Filesystem $filesystem): Js return $response; } - protected function extractLanguage(Request $request): string - { - $requestedLanguage = $request->request->get('language'); - if ($requestedLanguage) { - if ($requestedLanguage !== 'default') { - $request->setLocale($requestedLanguage); - } - } else { - $requestedLanguage = $request->getLocale(); - } - - return $requestedLanguage; - } - - protected function getCsvFile(string $fileHandle): string - { - return $fileHandle . '.csv'; - } - #[Route('/get-export-jobs', name: 'getexportjobs', methods: ['POST'])] - public function getExportJobsAction(Request $request, GridHelperService $gridHelperService, EventDispatcherInterface $eventDispatcher): JsonResponse + public function getExportJobsAction(Request $request, GetExportJobsHandler $handler): JsonResponse { $requestedLanguage = $this->extractLanguage($request); $allParams = [...$request->request->all(), ...$request->query->all()]; - //prepare fields - $fieldnames = []; - $fields = json_decode($allParams['fields'][0], true); - foreach ($fields as $field) { - $fieldnames[] = $field['key']; - } - $allParams['fields'] = $fieldnames; - - $list = $gridHelperService->prepareListingForGrid($allParams, $requestedLanguage, $this->getAdminUser()); - - $beforeListPrepareEvent = new GenericEvent($this, [ - 'list' => $list, - 'context' => $allParams, - ]); - $eventDispatcher->dispatch($beforeListPrepareEvent, AdminEvents::OBJECT_LIST_BEFORE_EXPORT_PREPARE); + $result = $handler($allParams, $requestedLanguage); - $list = $beforeListPrepareEvent->getArgument('list'); - - $ids = $list->loadIdList(); - - $jobs = array_chunk($ids, 20); - - $fileHandle = uniqid('export-'); - - $storage = Storage::get('temp'); - $storage->write($this->getCsvFile($fileHandle), ''); - - return $this->adminJson(['success' => true, 'jobs' => $jobs, 'fileHandle' => $fileHandle]); + return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs, 'fileHandle' => $result->fileHandle])); } - /** - * @throws Exception|FilesystemException - */ #[Route('/do-export', name: 'doexport', methods: ['POST'])] - public function doExportAction( - Request $request, - LocaleServiceInterface $localeService, - EventDispatcherInterface $eventDispatcher - ): JsonResponse { + public function doExportAction(DoDataObjectExportHandler $doExport, Request $request): JsonResponse + { $fileHandle = File::getValidFilename($request->request->get('fileHandle')); - $ids = $request->request->all('ids'); $settings = json_decode($request->request->get('settings'), true); - $delimiter = $settings['delimiter'] ?? ';'; - $header = $settings['header'] ?? 'title'; - Tool\UserTimezone::setUserTimezone($request->request->get('userTimezone')); - - $allParams = [...$request->request->all(), ...$request->query->all()]; - - $enableInheritance = $settings['enableInheritance'] ?? false; - DataObject\Concrete::setGetInheritedValues($enableInheritance); - - $class = DataObject\ClassDefinition::getById($request->request->get('classId')); - - if (!$class) { - throw new InvalidArgumentException('No class definition found'); - } - - $className = $class->getName(); - $listClass = '\\OpenDxp\\Model\\DataObject\\' . ucfirst($className) . '\\Listing'; - - /** @var Listing $list */ - $list = new $listClass(); - - $quotedIds = []; - foreach ($ids as $id) { - $quotedIds[] = $list->quote($id); - } - - $list->setObjectTypes(DataObject::$types); - $list->setCondition('id IN (' . implode(',', $quotedIds) . ')'); - $list->setOrderKey(' FIELD(id, ' . implode(',', $quotedIds) . ')', false); - - $beforeListExportEvent = new GenericEvent($this, [ - 'list' => $list, - 'context' => $allParams, - ]); - $eventDispatcher->dispatch($beforeListExportEvent, AdminEvents::OBJECT_LIST_BEFORE_EXPORT); - - $list = $beforeListExportEvent->getArgument('list'); - $fields = json_decode($request->request->all('fields')[0], true); - $addTitles = (bool) $request->request->get('initial'); - - $requestedLanguage = $this->extractLanguage($request); - - $context = [ - 'source' => 'opendxp-export', - ]; + $allParams = [...$request->request->all(), ...$request->query->all()]; + $context = ['source' => 'opendxp-export']; $contextFromRequest = $request->request->get('context'); if ($contextFromRequest) { - $contextFromRequest = json_decode($contextFromRequest, true); - $context = [...$context, ...$contextFromRequest]; - } - - $csv = DataObject\Service::getCsvData( - $requestedLanguage, - $localeService, - $list, + $context = [...$context, ...json_decode($contextFromRequest, true)]; + } + + $doExport( + $fileHandle, + $request->request->all('ids'), + (string) $request->request->get('classId'), + $settings['delimiter'] ?? ';', + $settings['header'] ?? 'title', + $request->request->get('userTimezone'), + $allParams, + $this->extractLanguage($request), $fields, - $header, - $addTitles, - $context + (bool) $request->request->get('initial'), + (bool) ($settings['enableInheritance'] ?? false), + $context, ); - $temp = tmpfile(); - - try { - $storage = Storage::get('temp'); - $csvFile = $this->getCsvFile($fileHandle); - - $fileStream = $storage->readStream($csvFile); - - stream_copy_to_stream($fileStream, $temp, null, 0); - - $firstLine = true; - - if ($request->request->get('initial') && $header === 'no_header') { - array_shift($csv); - $firstLine = false; - } - - $lineCount = count($csv); - - if (!$addTitles && $lineCount > 0) { - fwrite($temp, "\r\n"); - } - - for ($i = 0; $i < $lineCount; $i++) { - $line = $csv[$i]; - if ($addTitles && $firstLine) { - $firstLine = false; - $line = implode($delimiter, $line); - fwrite($temp, $line); - } else { - fwrite($temp, implode($delimiter, array_map($this->encodeFunc(...), $line))); - } - if ($i < $lineCount - 1) { - fwrite($temp, "\r\n"); - } - } - $storage->writeStream($csvFile, $temp); - } catch (UnableToReadFile $exception) { - Logger::err($exception->getMessage()); - - return $this->adminJson( - [ - 'success' => false, - 'message' => sprintf('export file not found: %s', $fileHandle), - ] - ); - } finally { - if (is_resource($temp)) { - fclose($temp); - } - } - - return $this->adminJson(['success' => true]); - } - - public function encodeFunc(string $value): string - { - $value = str_replace('"', '""', $value); - - //force wrap value in quotes and return - return '"' . $value . '"'; + return $this->adminJson(ApiResponse::ok()); } #[Route('/download-csv-file', name: 'downloadcsvfile', methods: ['GET'])] - public function downloadCsvFileAction(Request $request): Response - { - $storage = Storage::get('temp'); - $fileHandle = File::getValidFilename($request->query->get('fileHandle')); - $csvFile = $this->getCsvFile($fileHandle); - + public function downloadCsvFileAction( + #[MapQueryParameter] ?string $fileHandle = null, + ): Response { try { - $csvData = $storage->read($csvFile); - $response = new Response($csvData); - $response->headers->set('Content-Type', 'application/csv'); - $disposition = HeaderUtils::makeDisposition( - HeaderUtils::DISPOSITION_ATTACHMENT, - 'export.csv' - ); - - $response->headers->set('Content-Disposition', $disposition); - $storage->delete($csvFile); - - return $response; - } catch (FilesystemException | UnableToReadFile) { - // handle the error + return $this->gridExportService->downloadCsvFile($fileHandle); + } catch (\RuntimeException) { throw $this->createNotFoundException('CSV file not found'); } } #[Route('/download-xlsx-file', name: 'downloadxlsxfile', methods: ['GET'])] - public function downloadXlsxFileAction(Request $request, GridHelperService $gridHelperService): BinaryFileResponse - { - $storage = Storage::get('temp'); - $fileHandle = File::getValidFilename($request->query->get('fileHandle')); - $csvFile = $this->getCsvFile($fileHandle); - + public function downloadXlsxFileAction( + #[MapQueryParameter] ?string $fileHandle = null, + ): BinaryFileResponse { try { - return $gridHelperService->createXlsxExportFile($storage, $fileHandle, $csvFile); - } catch (Exception | FilesystemException | UnableToReadFile) { - // handle the error + return $this->gridExportService->downloadXlsxFile($fileHandle); + } catch (\RuntimeException) { throw $this->createNotFoundException('XLSX file not found'); } } - /** - * Flattens object data to an array with key=>value where - * value is simply a string representation of the value (for objects, hrefs and assets the full path is used) - */ - protected function csvObjectData(DataObject\Concrete $object): array - { - $o = []; - foreach ($object->getClass()->getFieldDefinitions() as $key => $value) { - $o[$key] = $value->getForCsvExport($object); - } - - $o['id (system)'] = $object->getId(); - $o['key (system)'] = $object->getKey(); - $o['fullpath (system)'] = $object->getRealFullPath(); - $o['published (system)'] = $object->isPublished(); - $o['type (system)'] = $object->getType(); - - return $o; - } - #[Route('/get-batch-jobs', name: 'getbatchjobs', methods: ['POST'])] - public function getBatchJobsAction(Request $request, GridHelperService $gridHelperService): JsonResponse + public function getBatchJobsAction(Request $request, GetBatchJobsHandler $handler): JsonResponse { if ($request->request->get('language')) { $request->setLocale($request->request->get('language')); } $allParams = [...$request->request->all(), ...$request->query->all()]; - $list = $gridHelperService->prepareListingForGrid($allParams, $request->getLocale(), $this->getAdminUser()); - - $jobs = $list->loadIdList(); + $result = $handler($allParams, $request->getLocale()); - return $this->adminJson(['success' => true, 'jobs' => $jobs]); + return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs])); } #[Route('/batch', name: 'batch', methods: ['PUT'])] - public function batchAction(Request $request): JsonResponse + public function batchAction(Request $request, ExecuteBatchHandler $handler): JsonResponse { - $success = true; - - try { - if ($request->request->has('data')) { - $params = $this->decodeJson($request->request->get('data'), true); - $object = DataObject\Concrete::getById($params['job']); - - if ($object) { - $requestedLanguage = $params['language']; - if ($requestedLanguage) { - if ($requestedLanguage !== 'default') { - $request->setLocale($requestedLanguage); - } - } else { - $requestedLanguage = $request->getLocale(); - } - - $name = $params['name']; - - if (!$object->isAllowed('save') || ($name === 'published' && !$object->isAllowed('publish'))) { - throw new Exception("Permission denied. You don't have the rights to save this object."); - } - - $append = $params['append'] ?? false; - $remove = $params['remove'] ?? false; + if ($request->request->has('data')) { + $params = $this->decodeJson($request->request->get('data'), true); + $saved = $handler($params, $request->getLocale()); - $className = $object->getClassName(); - $class = DataObject\ClassDefinition::getByName($className); - $value = $params['value']; - if ($params['valueType'] === 'object') { - $value = $this->decodeJson($value); - } - - $parts = explode('~', $name); - - if (str_starts_with($name, '~')) { - $type = $parts[1]; - $field = $parts[2]; - $keyId = $parts[3]; - - if ($type === 'classificationstore') { - $groupKeyId = explode('-', $keyId); - $groupId = (int) $groupKeyId[0]; - $keyId = (int) $groupKeyId[1]; - - $getter = 'get' . ucfirst($field); - if (method_exists($object, $getter)) { - /** @var DataObject\ClassDefinition\Data\Classificationstore $csFieldDefinition */ - $csFieldDefinition = $object->getClass()->getFieldDefinition($field); - $csLanguage = $requestedLanguage; - if (!$csFieldDefinition->isLocalized()) { - $csLanguage = 'default'; - } - - /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ - $fd = $class->getFieldDefinition($field); - $keyConfig = $fd->getKeyConfiguration($keyId); - $dataDefinition = DataObject\Classificationstore\Service::getFieldDefinitionFromKeyConfig($keyConfig); - - /** @var DataObject\Classificationstore $classificationStoreData */ - $classificationStoreData = $object->$getter(); - if ($append) { - $oldValue = $classificationStoreData->getLocalizedKeyValue($groupId, $keyId); - $value = $dataDefinition->appendData($oldValue, $value); - } - if ($remove) { - $oldValue = $classificationStoreData->getLocalizedKeyValue($groupId, $keyId); - $value = $dataDefinition->removeData($oldValue, $value); - } - $classificationStoreData->setLocalizedKeyValue( - $groupId, - $keyId, - $dataDefinition->getDataFromEditmode($value), - $csLanguage - ); - $object->markFieldDirty($field); - } - } - } elseif (count($parts) > 1) { - // check for bricks - $brickType = $parts[0]; - - if (str_contains($brickType, '?')) { - $brickDescriptor = substr($brickType, 1); - $brickDescriptor = json_decode($brickDescriptor, true); - $brickType = $brickDescriptor['containerKey']; - } - $brickKey = $parts[1]; - $brickField = DataObject\Service::getFieldForBrickType($object->getClass(), $brickType); - - $fieldGetter = 'get' . ucfirst($brickField); - $brickGetter = 'get' . ucfirst($brickType); - $valueSetter = 'set' . ucfirst($brickKey); - - $brick = $object->$fieldGetter()->$brickGetter(); - if (empty($brick)) { - $classname = '\\OpenDxp\\Model\\DataObject\\Objectbrick\\Data\\' . ucfirst($brickType); - $brickSetter = 'set' . ucfirst($brickType); - $brick = new $classname($object); - $object->$fieldGetter()->$brickSetter($brick); - } - - $brickClass = DataObject\Objectbrick\Definition::getByKey($brickType); - $field = $brickClass->getFieldDefinition($brickKey); - - $newData = $field->getDataFromEditmode($value, $object); - - if ($append) { - $valueGetter = 'get' . ucfirst($brickKey); - $existingData = $brick->$valueGetter(); - $newData = $field->appendData($existingData, $newData); - } - if ($remove) { - $valueGetter = 'get' . ucfirst($brickKey); - $existingData = $brick->$valueGetter(); - $newData = $field->removeData($existingData, $newData); - } - - $localizedFields = $brickClass->getFieldDefinition('localizedfields'); - $isLocalizedField = false; - if ($localizedFields instanceof DataObject\ClassDefinition\Data\Localizedfields && $localizedFields->getFieldDefinition($brickKey)) { - $isLocalizedField = true; - } - - if ($isLocalizedField) { - $brick->$valueSetter($newData, $params['language']); - } else { - $brick->$valueSetter($newData); - } - } else { - // everything else - $field = $class->getFieldDefinition($name); - if ($field) { - $newData = $field->getDataFromEditmode($value, $object); - - if ($append) { - $existingData = $object->{'get' . $name}(); - $newData = $field->appendData($existingData, $newData); - } - if ($remove) { - $existingData = $object->{'get' . $name}(); - $newData = $field->removeData($existingData, $newData); - } - $object->setValue($name, $newData); - } else { - // check if it is a localized field - if ($params['language']) { - $localizedField = $class->getFieldDefinition('localizedfields'); - if ($localizedField instanceof DataObject\ClassDefinition\Data\Localizedfields) { - $field = $localizedField->getFieldDefinition($name); - if ($field) { - $getter = 'get' . $name; - $setter = 'set' . $name; - $newData = $field->getDataFromEditmode($value, $object); - if ($append) { - $existingData = $object->$getter($params['language']); - $newData = $field->appendData($existingData, $newData); - } - if ($remove) { - $existingData = $object->$getter($params['language']); - $newData = $field->removeData($existingData, $newData); - } - - $object->$setter($newData, $params['language']); - } - } - } - - // seems to be a system field, this is actually only possible for the "published" field yet - if ($name === 'published') { - if ($value === 'false' || empty($value)) { - $object->setPublished(false); - } else { - $object->setPublished(true); - } - } - } - } - - try { - // don't check for mandatory fields here - $object->setOmitMandatoryCheck(!$object->isPublished()); - $object->setUserModification($this->getAdminUser()->getId()); - $object->save(); - $success = true; - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } else { - Logger::debug('DataObjectController::batchAction => There is no object left to update.'); - - return $this->adminJson(['success' => false, 'message' => 'DataObjectController::batchAction => There is no object left to update.']); - } - } - } catch (Exception $e) { - Logger::err((string) $e); - - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); + return $this->adminJson(ApiResponse::fromBool($saved)); } - return $this->adminJson(['success' => $success]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/get-available-visible-vields', name: 'getavailablevisiblefields', methods: ['GET'])] - public function getAvailableVisibleFieldsAction(Request $request): JsonResponse - { - $classList = []; - $classNameList = []; - - if ($request->query->has('classes')) { - $classNameList = $request->query->get('classes'); - $classNameList = explode(',', $classNameList); - foreach ($classNameList as $className) { - $class = DataObject\ClassDefinition::getByName($className); - if ($class) { - $classList[] = $class; - } - } - } - - if (!$classList) { - return $this->adminJson(['availableFields' => []]); - } - - $availableFields = []; - foreach (self::SYSTEM_COLUMNS as $field) { - $availableFields[] = [ - 'key' => $field, - 'value' => $field, - ]; - } - - /** @var DataObject\ClassDefinition\Data[] $commonFields */ - $commonFields = []; - - $firstOne = true; - foreach ($classNameList as $className) { - $class = DataObject\ClassDefinition::getByName($className); - if ($class) { - $fds = $class->getFieldDefinitions(); - - $additionalFieldNames = array_keys($fds); - $localizedFields = $class->getFieldDefinition('localizedfields'); - if ($localizedFields instanceof DataObject\ClassDefinition\Data\Localizedfields) { - $lfNames = array_keys($localizedFields->getFieldDefinitions()); - $additionalFieldNames = [...$additionalFieldNames, ...$lfNames]; - } - - foreach ($commonFields as $commonFieldKey => $commonFieldDefinition) { - if (!in_array($commonFieldKey, $additionalFieldNames)) { - unset($commonFields[$commonFieldKey]); - } - } - - $this->processAvailableFieldDefinitions($fds, $firstOne, $commonFields); - - $firstOne = false; - } - } - - $commonFieldKeys = array_keys($commonFields); - foreach ($commonFieldKeys as $field) { - $availableFields[] = [ - 'key' => $field, - 'value' => $field, - ]; - } + public function getAvailableVisibleFieldsAction( + GetAvailableVisibleFieldsHandler $getAvailableFields, + #[MapQueryParameter] ?string $classes = null, + ): JsonResponse { + $result = $getAvailableFields($classes); - return $this->adminJson(['availableFields' => $availableFields]); + return $this->adminJson(['availableFields' => $result->availableFields]); } - /** - * @param DataObject\ClassDefinition\Data[] $fds - * @param DataObject\ClassDefinition\Data[] $commonFields - */ - protected function processAvailableFieldDefinitions(array $fds, bool &$firstOne, array &$commonFields): void + private function extractLanguage(Request $request): string { - foreach ($fds as $fd) { - if ($fd instanceof DataObject\ClassDefinition\Data\Fieldcollections) { - continue; - } - if ($fd instanceof DataObject\ClassDefinition\Data\Objectbricks) { - continue; - } - if ($fd instanceof DataObject\ClassDefinition\Data\Block) { - continue; - } - if ($fd instanceof DataObject\ClassDefinition\Data\Localizedfields) { - $lfDefs = $fd->getFieldDefinitions(); - $this->processAvailableFieldDefinitions($lfDefs, $firstOne, $commonFields); - } elseif ($firstOne || (isset($commonFields[$fd->getName()]) && $commonFields[$fd->getName()]->getFieldtype() == $fd->getFieldtype())) { - $commonFields[$fd->getName()] = $fd; + $requestedLanguage = $request->request->get('language'); + if ($requestedLanguage) { + if ($requestedLanguage !== 'default') { + $request->setLocale($requestedLanguage); } + } else { + $requestedLanguage = $request->getLocale(); } + + return $requestedLanguage; } } diff --git a/src/Controller/Admin/DataObject/DataObjectVersionController.php b/src/Controller/Admin/DataObject/DataObjectVersionController.php new file mode 100644 index 00000000..2ba49e56 --- /dev/null +++ b/src/Controller/Admin/DataObject/DataObjectVersionController.php @@ -0,0 +1,100 @@ +value)] +class DataObjectVersionController extends AdminAbstractController +{ + #[Route('/publish-version', name: 'publishversion', methods: ['POST'])] + public function publishVersionAction(Request $request, PublishVersionHandler $publishVersion): JsonResponse + { + $result = $publishVersion($request->request->getInt('id')); + + return $this->adminJson(ApiResponse::ok([ + 'general' => ['modificationDate' => $result->modificationDate], + 'treeData' => $result->treeData, + ])); + } + + #[Route('/preview-version', name: 'previewversion', methods: ['GET'])] + public function previewVersionAction( + Environment $twig, + PreviewVersionHandler $previewVersion, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $userTimezone = null, + ): Response + { + $result = $previewVersion($id); + + Tool\UserTimezone::setUserTimezone($userTimezone); + if ($timezone = Tool\UserTimezone::getUserTimezone()) { + $twig->getExtension(CoreExtension::class)->setTimezone($timezone); + } + + return $this->render('@OpenDxpAdmin/admin/data_object/data_object/preview_version.html.twig', [ + 'object' => $result->object, + 'versionNote' => $result->version->getNote(), + 'validLanguages' => Tool::getValidLanguages(), + ]); + } + + #[Route('/diff-versions/from/{from}/to/{to}', name: 'diffversions', methods: ['GET'])] + public function diffVersionsAction( + Environment $twig, + DiffVersionsHandler $diffVersions, + int $from, + int $to, + #[MapQueryParameter] ?string $userTimezone = null, + ): Response + { + $result = $diffVersions($from, $to); + + Tool\UserTimezone::setUserTimezone($userTimezone); + if ($timezone = Tool\UserTimezone::getUserTimezone()) { + $twig->getExtension(CoreExtension::class)->setTimezone($timezone); + } + + return $this->render('@OpenDxpAdmin/admin/data_object/data_object/diff_versions.html.twig', [ + 'object1' => $result->object1, + 'versionNote1' => $result->version1->getNote(), + 'object2' => $result->object2, + 'versionNote2' => $result->version2->getNote(), + 'validLanguages' => Tool::getValidLanguages(), + ]); + } +} diff --git a/src/Controller/Admin/DataObject/FieldCollectionController.php b/src/Controller/Admin/DataObject/FieldCollectionController.php new file mode 100644 index 00000000..16046aca --- /dev/null +++ b/src/Controller/Admin/DataObject/FieldCollectionController.php @@ -0,0 +1,151 @@ +value)] +class FieldCollectionController extends AdminAbstractController +{ + #[Route('/fieldcollection-get', name: 'fieldcollectionget', methods: ['GET'])] + public function fieldcollectionGetAction(GetFieldCollectionHandler $getFieldCollection, #[MapQueryParameter] string $id): JsonResponse + { + $result = $getFieldCollection($id); + $data = $result->data; + $data['isWriteable'] = $result->isWriteable; + + return $this->adminJson($data); + } + + #[Route('/fieldcollection-update', name: 'fieldcollectionupdate', methods: ['PUT', 'POST'])] + public function fieldcollectionUpdateAction(UpdateFieldCollectionHandler $updateFieldCollection, Request $request): JsonResponse + { + $fcDef = $updateFieldCollection( + (string) $request->request->get('key'), + (string) $request->request->get('title'), + (string) $request->request->get('group'), + $request->request->get('task') === 'add', + $request->request->has('values') ? $this->decodeJson($request->request->get('values')) : null, + $request->request->has('configuration') ? $this->decodeJson($request->request->get('configuration')) : null, + ); + + return $this->adminJson(ApiResponse::ok(['id' => $fcDef->getKey()])); + } + + #[Route('/fieldcollection-delete', name: 'fieldcollectiondelete', methods: ['DELETE'])] + public function fieldcollectionDeleteAction(DeleteFieldCollectionHandler $deleteFieldCollection, Request $request): JsonResponse + { + $deleteFieldCollection((string) $request->request->get('id')); + + return $this->adminJson(ApiResponse::ok()); + } + + #[Route('/fieldcollection-tree', name: 'fieldcollectiontree', methods: ['GET', 'POST'])] + public function fieldcollectionTreeAction( + GetFieldCollectionTreeHandler $getTree, + #[MapQueryParameter] ?string $forObjectEditor = null, + #[MapQueryParameter] ?string $allowedTypes = null, + #[MapQueryParameter(name: 'object_id')] int $objectId = 0, + #[MapQueryParameter] ?string $layoutId = null, + ): JsonResponse { + $result = $getTree( + $forObjectEditor !== null, + $allowedTypes !== null ? explode(',', $allowedTypes) : null, + $objectId, + $layoutId, + ); + + if ($forObjectEditor) { + return $this->adminJson(['fieldcollections' => $result->definitions, 'layoutDefinitions' => $result->layoutDefinitions]); + } + + return $this->adminJson($result->definitions); + } + + #[Route('/fieldcollection-list', name: 'fieldcollectionlist', methods: ['GET'])] + public function fieldcollectionListAction( + GetFieldCollectionListHandler $getList, + #[MapQueryParameter] ?string $layoutId = null, + #[MapQueryParameter] ?string $allowedTypes = null, + #[MapQueryParameter(name: 'field_name')] ?string $fieldName = null, + #[MapQueryParameter(name: 'object_id')] int $objectId = 0, + ): JsonResponse { + $result = $getList( + $allowedTypes !== null ? explode(',', $allowedTypes) : null, + $fieldName, + $objectId, + $layoutId, + ); + + return $this->adminJson(['fieldcollections' => $result->fieldcollections]); + } + + #[Route('/import-fieldcollection', name: 'importfieldcollection', methods: ['POST'])] + public function importFieldcollectionAction(ImportFieldCollectionHandler $importFieldCollection, Request $request, #[MapQueryParameter] string $id): Response + { + /** @var UploadedFile $file */ + $file = $request->files->get('Filedata'); + $importFieldCollection($id, file_get_contents($file->getPathname())); + + $response = $this->adminJson(ApiResponse::ok()); + $response->headers->set('Content-Type', 'text/html'); + + return $response; + } + + #[Route('/export-fieldcollection', name: 'exportfieldcollection', methods: ['GET'])] + public function exportFieldcollectionAction(ExportFieldCollectionHandler $exportFieldCollection, #[MapQueryParameter] string $id): Response + { + $result = $exportFieldCollection($id); + + $response = new Response($result->json); + $response->headers->set('Content-type', 'application/json'); + $response->headers->set('Content-Disposition', 'attachment; filename="fieldcollection_' . $result->key . '_export.json"'); + + return $response; + } + + #[Route('/get-fieldcollection-usages', name: 'getfieldcollectionusages', methods: ['GET'])] + public function getFieldcollectionUsagesAction( + GetFieldCollectionUsagesHandler $getUsages, + #[MapQueryParameter] string $key, + ): Response { + return $this->adminJson($getUsages($key)); + } +} diff --git a/src/Controller/Admin/DataObject/ObjectBrickController.php b/src/Controller/Admin/DataObject/ObjectBrickController.php new file mode 100644 index 00000000..18454411 --- /dev/null +++ b/src/Controller/Admin/DataObject/ObjectBrickController.php @@ -0,0 +1,145 @@ +value)] +class ObjectBrickController extends AdminAbstractController +{ + #[Route('/objectbrick-get', name: 'objectbrickget', methods: ['GET'])] + public function objectbrickGetAction(GetObjectBrickHandler $getObjectBrick, #[MapQueryParameter] string $id): JsonResponse + { + $result = $getObjectBrick($id); + $data = $result->data; + $data['isWriteable'] = $result->isWriteable; + + return $this->adminJson($data); + } + + #[Route('/objectbrick-update', name: 'objectbrickupdate', methods: ['PUT', 'POST'])] + public function objectbrickUpdateAction(UpdateObjectBrickHandler $updateObjectBrick, Request $request): JsonResponse + { + $brickDef = $updateObjectBrick( + (string) $request->request->get('key'), + (string) $request->request->get('title'), + (string) $request->request->get('group'), + $request->request->get('task') === 'add', + $request->request->has('values') ? $this->decodeJson($request->request->get('values')) : null, + $request->request->has('configuration') ? $this->decodeJson($request->request->get('configuration')) : null, + ); + + return $this->adminJson(ApiResponse::ok(['id' => $brickDef->getKey()])); + } + + #[Route('/objectbrick-delete', name: 'objectbrickdelete', methods: ['DELETE'])] + public function objectbrickDeleteAction(DeleteObjectBrickHandler $deleteObjectBrick, Request $request): JsonResponse + { + $deleteObjectBrick((string) $request->request->get('id')); + + return $this->adminJson(ApiResponse::ok()); + } + + #[Route('/objectbrick-tree', name: 'objectbricktree', methods: ['GET', 'POST'])] + public function objectbrickTreeAction( + GetObjectBrickTreeHandler $getTree, + #[MapQueryParameter] ?string $forObjectEditor = null, + #[MapQueryParameter(name: 'object_id')] int $objectId = 0, + #[MapQueryParameter(name: 'class_id')] ?string $classId = null, + #[MapQueryParameter(name: 'field_name')] ?string $fieldName = null, + #[MapQueryParameter] ?string $layoutId = null, + ): JsonResponse { + $result = $getTree( + $forObjectEditor !== null, + $objectId, + $classId, + $fieldName, + $layoutId, + ); + + if ($forObjectEditor) { + return $this->adminJson(['objectbricks' => $result->definitions, 'layoutDefinitions' => $result->layoutDefinitions]); + } + + return $this->adminJson($result->definitions); + } + + #[Route('/objectbrick-list', name: 'objectbricklist', methods: ['GET'])] + public function objectbrickListAction( + GetObjectBrickListHandler $getList, + #[MapQueryParameter(name: 'class_id')] ?string $classId = null, + #[MapQueryParameter(name: 'field_name')] ?string $fieldName = null, + #[MapQueryParameter] ?string $layoutId = null, + #[MapQueryParameter(name: 'object_id')] int $objectId = 0, + ): JsonResponse { + $result = $getList($classId, $fieldName, $layoutId, $objectId); + + return $this->adminJson(['objectbricks' => $result->objectbricks]); + } + + #[Route('/import-objectbrick', name: 'importobjectbrick', methods: ['POST'])] + public function importObjectbrickAction(ImportObjectBrickHandler $importObjectBrick, Request $request, #[MapQueryParameter] string $id): JsonResponse + { + /** @var UploadedFile $file */ + $file = $request->files->get('Filedata'); + $importObjectBrick($id, file_get_contents($file->getPathname())); + $response = $this->adminJson(ApiResponse::ok()); + $response->headers->set('Content-Type', 'text/html'); + + return $response; + } + + #[Route('/export-objectbrick', name: 'exportobjectbrick', methods: ['GET'])] + public function exportObjectbrickAction(ExportObjectBrickHandler $exportObjectBrick, #[MapQueryParameter] string $id): Response + { + $result = $exportObjectBrick($id); + + $response = new Response($result->json); + $response->headers->set('Content-type', 'application/json'); + $response->headers->set('Content-Disposition', 'attachment; filename="objectbrick_' . $result->key . '_export.json"'); + + return $response; + } + + #[Route('/get-bricks-usages', name: 'getbrickusages', methods: ['GET'])] + public function getBrickUsagesAction(GetBrickUsagesHandler $getBrickUsages, #[MapQueryParameter] string $classId): Response + { + return $this->adminJson($getBrickUsages($classId)->usages); + } +} diff --git a/src/Controller/Admin/DataObject/QuantityValueController.php b/src/Controller/Admin/DataObject/QuantityValueController.php index c1bab143..0307e8ca 100644 --- a/src/Controller/Admin/DataObject/QuantityValueController.php +++ b/src/Controller/Admin/DataObject/QuantityValueController.php @@ -16,18 +16,24 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; -use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Model\DataObject\Data\QuantityValue; -use OpenDxp\Model\DataObject\QuantityValue\Service as QuantityValueService; -use OpenDxp\Model\DataObject\QuantityValue\Unit; -use OpenDxp\Model\DataObject\QuantityValue\UnitConversionService; -use OpenDxp\Model\Translation; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValuesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValueHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ExportQuantityValueUnitsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnitsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ManageQuantityValueUnitHandler; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal @@ -35,250 +41,96 @@ #[Route('/quantity-value', name: 'opendxp_admin_dataobject_quantityvalue_')] class QuantityValueController extends AdminAbstractController { - public function __construct(protected QuantityValueService $service) - { - } - #[Route('/unit-import', name: 'unitimport', methods: ['POST', 'PUT'])] - public function unitImportAction(Request $request): JsonResponse + public function unitImportAction(Request $request, ImportQuantityValueUnitsHandler $importUnits): JsonResponse { /** @var UploadedFile $uploadFile */ $uploadFile = $request->files->get('Filedata'); - $json = file_get_contents($uploadFile->getPathname()); - $success = $this->service->importDefinitionFromJson($json); - $response = $this->adminJson(['success' => $success]); + $success = $importUnits(file_get_contents($uploadFile->getPathname())); + $response = $this->adminJson(ApiResponse::fromBool($success)); $response->headers->set('Content-Type', 'text/html'); return $response; } #[Route('/unit-export', name: 'unitexport', methods: ['GET'])] - public function unitExportAction(Request $request): Response + public function unitExportAction(ExportQuantityValueUnitsHandler $exportUnits): Response { - $result = $this->service->generateDefinitionJson(); - $response = new Response($result); + $response = new Response($exportUnits()); $response->headers->set('Content-Type', 'application/json'); $response->headers->set('Content-Disposition', 'attachment;filename: "quantityvalue_unit_export.json"'); return $response; } - /** - * @throws Exception - */ #[Route('/unit-proxy', name: 'unitproxyget', methods: ['GET'])] - public function unitProxyGetAction(Request $request): JsonResponse - { - $this->checkPermission('quantityValueUnits'); - - $list = new Unit\Listing(); - - $order = ['ASC', 'ASC', 'ASC']; - $orderKey = ['baseunit', 'factor', 'abbreviation']; - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->query->all()); - - // Prepend user-requested sorting settings but keep the others to keep secondary order of quantity values in respective order - if ($sortingSettings['orderKey']) { - array_unshift($orderKey, $sortingSettings['orderKey']); - } - if ($sortingSettings['order']) { - array_unshift($order, $sortingSettings['order']); - } - - $list->setOrder($order); - $list->setOrderKey($orderKey); - - $list->setLimit((int)$request->query->get('limit', '25')); - $list->setOffset((int)$request->query->get('start', '0')); - - $condition = '1 = 1'; - if ($request->query->get('filter')) { - $filterString = $request->query->get('filter'); - $filters = json_decode($filterString); - $db = \OpenDxp\Db::get(); - foreach ($filters as $f) { - if ($f->type === 'string') { - $condition .= ' AND ' . $db->quoteIdentifier($f->property) . ' LIKE ' . $db->quote('%' . $f->value . '%'); - } elseif ($f->type === 'numeric') { - $operator = $this->getOperator($f->comparison); - $condition .= ' AND ' . $db->quoteIdentifier($f->property) . ' ' . $operator . ' ' . $db->quote($f->value); - } - } - $list->setCondition($condition); - } - - $units = []; - foreach ($list->getUnits() as $u) { - $units[] = $u->getObjectVars(); - } - - return $this->adminJson(['data' => $units, 'success' => true, 'total' => $list->getTotalCount()]); + #[IsGranted(CorePermission::QuantityValueUnits->value)] + public function unitProxyGetAction( + GetQuantityValueUnitsHandler $getUnits, + Request $request, + #[MapQueryParameter] int $limit = 25, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] ?string $filter = null, + ): JsonResponse { + $result = $getUnits($request->query->all(), $limit, $start, $filter); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - /** - * @throws Exception - */ #[Route('/unit-proxy', name: 'unitproxy', methods: ['POST', 'PUT'])] - public function unitProxyAction(Request $request): JsonResponse - { - $this->checkPermission('quantityValueUnits'); - - if ($request->request->has('data')) { - if ($request->query->get('xaction') === 'destroy') { - $data = json_decode($request->request->get('data'), true); - $id = $data['id']; - $unit = \OpenDxp\Model\DataObject\QuantityValue\Unit::getById($id); - if (!empty($unit)) { - $unit->delete(); - - return $this->adminJson(['data' => [], 'success' => true]); - } - - throw new Exception('Unit with id ' . $id . ' not found.'); - } - if ($request->query->get('xaction') === 'update') { - $data = json_decode($request->request->get('data'), true); - $unit = Unit::getById($data['id']); - if (!empty($unit)) { - if (($data['baseunit'] ?? null) == -1) { - $data['baseunit'] = null; - } - $unit->setValues($data); - $unit->save(); - - return $this->adminJson(['data' => $unit->getObjectVars(), 'success' => true]); - } - - throw new Exception('Unit with id ' . $data['id'] . ' not found.'); - } - if ($request->query->get('xaction') === 'create') { - $data = json_decode($request->request->get('data'), true); - if (isset($data['baseunit']) && $data['baseunit'] === -1) { - $data['baseunit'] = null; - } - $id = $data['id']; - if (Unit::getById($id)) { - throw new Exception('unit with ID [' . $id . '] already exists'); - } - if (mb_strlen($id) > 50) { - throw new Exception('The maximal character length for the unit ID is 50 characters, the provided ID has ' . mb_strlen($id) . ' characters.'); - } - $unit = new Unit(); - $unit->setValues($data); - $unit->save(); - - return $this->adminJson(['data' => $unit->getObjectVars(), 'success' => true]); - } + #[IsGranted(CorePermission::QuantityValueUnits->value)] + public function unitProxyAction( + ManageQuantityValueUnitHandler $manageUnit, + Request $request, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { + if (!$request->request->has('data')) { + throw new BadRequestHttpException(); } - return $this->adminJson(['success' => false]); - } - - private function getOperator(string $comparison): string - { - $mapper = [ - 'lt' => '<', - 'gt' => '>', - 'eq' => '=', - ]; + $data = json_decode($request->request->get('data'), true); + $result = $manageUnit((string) $xaction, $data); - return $mapper[$comparison]; + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } #[Route('/unit-list', name: 'unitlist', methods: ['GET'])] - public function unitListAction(Request $request): JsonResponse - { - $list = new Unit\Listing(); - $list->setOrderKey(['baseunit', 'factor', 'abbreviation']); - $list->setOrder(['ASC', 'ASC', 'ASC']); - if ($request->query->get('filter')) { - $array = explode(',', $request->query->get('filter')); - $quotedArray = []; - $db = \OpenDxp\Db::get(); - foreach ($array as $a) { - $quotedArray[] = $db->quote($a); - } - $string = implode(',', $quotedArray); - $list->setCondition('id IN (' . $string . ')'); - } - - $result = []; - $units = $list->getUnits(); - foreach ($units as &$unit) { - try { - if ($unit->getAbbreviation()) { - $unit->setAbbreviation(Translation::getByKeyLocalized($unit->getAbbreviation(), Translation::DOMAIN_ADMIN, - true, true)); - } - if ($unit->getLongname()) { - $unit->setLongname(Translation::getByKeyLocalized($unit->getLongname(), Translation::DOMAIN_ADMIN, true, - true)); - } - $result[] = $unit->getObjectVars(); - } catch (Exception) { - // nothing to do ... - } - } + public function unitListAction( + GetQuantityValueUnitListHandler $getUnitList, + #[MapQueryParameter] ?string $filter = null, + ): JsonResponse { + $result = $getUnitList($filter); - return $this->adminJson(['data' => $result, 'success' => true, 'total' => $list->getTotalCount()]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[Route('/convert', name: 'convert', methods: ['GET'])] - public function convertAction(Request $request, UnitConversionService $conversionService): JsonResponse - { - $this->checkPermission('objects'); - - $fromUnitId = $request->query->get('fromUnit'); - $toUnitId = $request->query->get('toUnit'); - - $fromUnit = Unit::getById($fromUnitId); - $toUnit = Unit::getById($toUnitId); - if (!$fromUnit instanceof Unit || !$toUnit instanceof Unit) { - return $this->adminJson(['success' => false]); - } - - try { - $convertedValue = $conversionService->convert(new QuantityValue($request->query->get('value'), $fromUnit), $toUnit); - } catch (Exception) { - return $this->adminJson(['success' => false]); - } - - return $this->adminJson(['value' => $convertedValue->getValue(), 'success' => true]); + #[IsGranted(CorePermission::Objects->value)] + public function convertAction( + ConvertQuantityValueHandler $convert, + #[MapQueryParameter] ?string $fromUnit = null, + #[MapQueryParameter] ?string $toUnit = null, + #[MapQueryParameter] ?string $value = null, + ): JsonResponse { + $result = $convert($fromUnit, $toUnit, $value); + + return $this->adminJson(ApiResponse::ok(['value' => $result->value])); } #[Route('/convert-all', name: 'convertall', methods: ['GET'])] - public function convertAllAction(Request $request, UnitConversionService $conversionService): JsonResponse - { - $this->checkPermission('objects'); - - $unitId = $request->query->get('unit'); - - $fromUnit = Unit::getById($unitId); - if (!$fromUnit instanceof Unit) { - return $this->adminJson(['success' => false]); - } - $baseUnit = $fromUnit->getBaseunit() ?? $fromUnit; - - $units = new Unit\Listing(); - $units->setCondition('baseunit = '.$units->quote($baseUnit->getId()).' AND id != '.$units->quote($fromUnit->getId())); - - $convertedValues = []; - foreach ($units->getUnits() as $targetUnit) { - try { - $convertedValue = $conversionService->convert(new QuantityValue($request->query->get('value'), $fromUnit), $targetUnit); - - $convertedValues[] = ['unit' => $targetUnit->getAbbreviation(), 'unitName' => $targetUnit->getLongname(), 'value' => round($convertedValue->getValue(), 4)]; - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - return $this->adminJson([ - 'value' => $request->query->get('value'), - 'fromUnit' => $fromUnit->getAbbreviation(), - 'values' => $convertedValues, 'success' => true] - ); + #[IsGranted(CorePermission::Objects->value)] + public function convertAllAction( + ConvertAllQuantityValuesHandler $convertAll, + #[MapQueryParameter] ?string $unit = null, + #[MapQueryParameter] ?string $value = null, + ): JsonResponse { + $result = $convertAll($unit, $value); + + return $this->adminJson(ApiResponse::ok([ + 'value' => $result->value, + 'fromUnit' => $result->fromUnit, + 'values' => $result->values, + ])); } } diff --git a/src/Controller/Admin/DataObject/VariantsController.php b/src/Controller/Admin/DataObject/VariantsController.php index 3942b2d3..4b468ddb 100644 --- a/src/Controller/Admin/DataObject/VariantsController.php +++ b/src/Controller/Admin/DataObject/VariantsController.php @@ -1,5 +1,4 @@ request->getInt('id'); - $key = $request->request->get('key'); - $object = DataObject\Concrete::getById($id); + $result = $updateObjectKey( + $request->request->getInt('id'), + $request->request->get('key'), + ); - return $this->adminJson($this->renameObject($object, $key)); + return $this->adminJson($result->data); } - /** - * @throws Exception - */ #[Route('/get-variants', name: 'getvariants', methods: ['POST'])] public function getVariantsAction( + GetVariantsHandler $getVariants, Request $request, - EventDispatcherInterface $eventDispatcher, - GridHelperService $gridHelperService, - LocaleServiceInterface $localeService, - CsrfProtectionHandler $csrfProtection + CsrfProtectionHandler $csrfProtection, ): JsonResponse { - - $parentObject = DataObject\Concrete::getById((int) $request->request->get('objectId')); - - if ($parentObject === null) { - throw new Exception('No Object found with id ' . $request->request->get('objectId')); - } - - if (!$parentObject->isAllowed('view')) { - throw new Exception('Permission denied'); - } + $csrfProtection->checkCsrfToken($request); $allParams = [...$request->request->all(), ...$request->query->all()]; + $requestedLanguage = $allParams['language'] ?? null; + if ($requestedLanguage && $requestedLanguage !== 'default') { + $request->setLocale($requestedLanguage); + } else { + $requestedLanguage = $request->getLocale(); + } - $allParams['folderId'] = $parentObject->getId(); - $allParams['classId'] = $parentObject->getClassId(); - - $csrfProtection->checkCsrfToken($request); - - $result = $this->gridProxy( + $result = $getVariants( + (int) $request->request->get('objectId'), $allParams, - DataObject::OBJECT_TYPE_VARIANT, - $request, - $eventDispatcher, - $gridHelperService, - $localeService + $requestedLanguage, ); - return $this->adminJson($result); + return $this->adminJson($result->data); } } diff --git a/src/Controller/Admin/Document/DocumentController.php b/src/Controller/Admin/Document/DocumentController.php index f5c53d6a..f1bde27b 100644 --- a/src/Controller/Admin/Document/DocumentController.php +++ b/src/Controller/Admin/Document/DocumentController.php @@ -15,1355 +15,353 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\Document; -use Exception; -use Imagick; -use OpenDxp; use OpenDxp\Bundle\AdminBundle\Controller\Admin\ElementControllerBase; -use OpenDxp\Bundle\AdminBundle\Controller\Traits\AdminStyleTrait; -use OpenDxp\Bundle\AdminBundle\Controller\Traits\UserNameTrait; -use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Event\ElementAdminStyleEvent; -use OpenDxp\Bundle\AdminBundle\Event\SiteCustomSettingsEvent; -use OpenDxp\Cache\RuntimeCache; -use OpenDxp\Config; -use OpenDxp\Controller\KernelControllerEventInterface; -use OpenDxp\Db; -use OpenDxp\Document\Renderer\DocumentRendererInterface; -use OpenDxp\Event\Traits\RecursionBlockingEventDispatchHelperTrait; -use OpenDxp\Image\HtmlToImage; -use OpenDxp\Logger; -use OpenDxp\Model\Document; -use OpenDxp\Model\Document\DocType; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; +use OpenDxp\Controller\Traits\ElementEditLockHelperTrait; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; +use OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\ConvertDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\DeleteDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentIdForPathHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocTypesByTypeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocTypesListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\ManageDocTypesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\GetSiteCustomSettingsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\RemoveSiteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\AddDocumentTranslationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\CheckTranslationLanguageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\DetermineTranslationParentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTreeRootHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\RemoveDocumentTranslationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateSiteHandler; +use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Model\Element\ElementInterface; -use OpenDxp\Model\Element\Service; -use OpenDxp\Model\Exception\ConfigWriteException; -use OpenDxp\Model\Site; -use OpenDxp\Model\Version; use OpenDxp\Tool; -use OpenDxp\Tool\Session; use Override; -use RuntimeException; -use Symfony\Component\EventDispatcher\GenericEvent; -use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -use Symfony\Component\HttpKernel\Event\ControllerEvent; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Component\Routing\RouterInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use function base64_encode; -use function basename; -use function date; -use function file_exists; -use function file_get_contents; -use function file_put_contents; -use function sprintf; -use function uniqid; -use function unlink; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal */ #[Route('/document')] -class DocumentController extends ElementControllerBase implements KernelControllerEventInterface +class DocumentController extends ElementControllerBase { - use AdminStyleTrait; - use UserNameTrait; - use RecursionBlockingEventDispatchHelperTrait; + use ElementEditLockHelperTrait; - protected Document\Service $_documentService; + public function __construct( + ElementServiceInterface $elementService, + ) { + parent::__construct($elementService); + } + #[IsGranted(CorePermission::Documents->value)] #[Override] #[Route('/tree-get-root', name: 'opendxp_admin_document_document_treegetroot', methods: ['GET'])] - public function treeGetRootAction(Request $request): JsonResponse - { - return parent::treeGetRootAction($request); + public function treeGetRootAction( + #[MapQueryParameter] ?string $elementType = null, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $id = null, + ): JsonResponse { + return parent::treeGetRootAction($elementType, $id); } + #[IsGranted(CorePermission::Documents->value)] #[Override] #[Route('/delete-info', name: 'opendxp_admin_document_document_deleteinfo', methods: ['GET'])] - public function deleteInfoAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - return parent::deleteInfoAction($request, $eventDispatcher); + public function deleteInfoAction( + GetDeleteInfoHandler $handler, + Request $request, + #[MapQueryParameter] ?string $id = null, + #[MapQueryParameter] ?string $type = null, + ): JsonResponse { + return parent::deleteInfoAction($handler, $request, $id, $type); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/get-data-by-id', name: 'opendxp_admin_document_document_getdatabyid', methods: ['GET'])] - public function getDataByIdAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse + public function getDataByIdAction( + GetDocumentDataHandler $handler, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { - $document = Document::getById((int) $request->query->get('id')); - - if (!$document) { - throw $this->createNotFoundException('Document not found'); - } - - $document = clone $document; - $data = $document->getObjectVars(); - $data['versionDate'] = $document->getModificationDate(); - - $userOwnerName = $this->getUserName($document->getUserOwner()); - $userModificationName = ($document->getUserOwner() === $document->getUserModification()) ? $userOwnerName : $this->getUserName($document->getUserModification()); - $data['userOwnerUsername'] = $userOwnerName['userName']; - $data['userOwnerFullname'] = $userOwnerName['fullName']; - $data['userModificationUsername'] = $userModificationName['userName']; - $data['userModificationFullname'] = $userModificationName['fullName']; - - $data['php'] = [ - 'classes' => [$document::class, ...array_values(class_parents($document))], - 'interfaces' => array_values(class_implements($document)), - ]; - - $this->addAdminStyle($document, ElementAdminStyleEvent::CONTEXT_EDITOR, $data); - - $event = new GenericEvent($this, [ - 'data' => $data, - 'document' => $document, - ]); - $eventDispatcher->dispatch($event, AdminEvents::DOCUMENT_GET_PRE_SEND_DATA); - $data = $event->getArgument('data'); - - if ($document->isAllowed('view')) { - return $this->adminJson($data); + try { + $result = $handler($id); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - throw $this->createAccessDeniedHttpException(); + return $this->adminJson($result->data); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/tree-get-children-by-id', name: 'opendxp_admin_document_document_treegetchildrenbyid', methods: ['GET'])] - public function treeGetChildrenByIdAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse + public function treeGetChildrenByIdAction( + TreeGetDocumentChildrenHandler $handler, + Request $request, + #[MapQueryParameter] int $inSearch = 0, + ): JsonResponse { - $allParams = $request->query->all(); - - $filter = $request->query->get('filter'); - $limit = (int)($allParams['limit'] ?? 100000000); - $offset = (int)($allParams['start'] ?? 0); - - if (!is_null($filter)) { - if (!str_ends_with($filter, '*')) { - $filter .= '*'; - } - $filter = str_replace('*', '%', $filter); - $limit = 100; - $offset = 0; - } - - $document = Document::getById((int) $allParams['node']); - if (!$document) { - throw $this->createNotFoundException('Document was not found'); - } - - $documents = []; - $cv = []; - if ($document->hasChildren()) { - if ($allParams['view']) { - $cv = $this->elementService->getCustomViewById($allParams['view']); - } - - $db = Db::get(); - - $list = new Document\Listing(); - - $condition = 'parentId = ' . $db->quote($document->getId()); - - if (!$this->getAdminUser()->isAdmin()) { - $userIds = $this->getAdminUser()->getRoles(); - $currentUserId = $this->getAdminUser()->getId(); - $userIds[] = $currentUserId; - - $inheritedPermission = $document->getDao()->isInheritingPermission('list', $userIds); - - $anyAllowedRowOrChildren = 'EXISTS(SELECT list FROM users_workspaces_document uwd WHERE userId IN (' . implode(',', $userIds) . ') AND list=1 AND LOCATE(CONCAT(`path`,`key`),cpath)=1 AND - NOT EXISTS(SELECT list FROM users_workspaces_document WHERE userId =' . $currentUserId . ' AND list=0 AND cpath = uwd.cpath))'; - $isDisallowedCurrentRow = 'EXISTS(SELECT list FROM users_workspaces_document WHERE userId IN (' . implode(',', $userIds) . ') AND cid = id AND list=0)'; - - $condition .= ' AND IF(' . $anyAllowedRowOrChildren . ',1,IF(' . $inheritedPermission . ', ' . $isDisallowedCurrentRow . ' = 0, 0)) = 1'; - } - - if ($filter) { - $condition = '(' . $condition . ')' . ' AND CAST(documents.key AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ' . $db->quote($filter); - } - - $list->setCondition($condition); - - $list->setOrderKey(['index', 'id']); - $list->setOrder(['asc', 'asc']); - - $list->setLimit($limit); - $list->setOffset($offset); - - Service::addTreeFilterJoins($cv, $list); - - $beforeListLoadEvent = new GenericEvent($this, [ - 'list' => $list, - 'context' => $allParams, - ]); - - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::DOCUMENT_LIST_BEFORE_LIST_LOAD); - /** @var Document\Listing $list */ - $list = $beforeListLoadEvent->getArgument('list'); - - $childrenList = $list->load(); - - foreach ($childrenList as $childDocument) { - $documentTreeNode = $this->elementService->getElementTreeNodeConfig($childDocument); - // the !isset is for printContainer case, there are no permissions sets there - if (!isset($documentTreeNode['permissions']['list']) || $documentTreeNode['permissions']['list'] == 1) { - $documents[] = $documentTreeNode; - } - } - } - - //Hook for modifying return value - e.g. for changing permissions based on document data - $event = new GenericEvent($this, [ - 'documents' => $documents, - ]); - - $eventDispatcher->dispatch($event, AdminEvents::DOCUMENT_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA); - $documents = $event->getArgument('documents'); + $result = $handler($request->query->all()); - if ($allParams['limit']) { + if ($result->paginated) { return $this->adminJson([ - 'offset' => $offset, - 'limit' => $limit, - 'total' => $document->getChildAmount($this->getAdminUser()), - 'nodes' => $documents, - 'filter' => $request->query->get('filter') ?: '', - 'inSearch' => (int)$request->query->get('inSearch'), + 'offset' => $result->offset, + 'limit' => $result->limit, + 'total' => $result->total, + 'nodes' => $result->documents, + 'filter' => $result->filter ?: '', + 'inSearch' => $inSearch, ]); } - return $this->adminJson($documents); + return $this->adminJson($result->documents); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/add', name: 'opendxp_admin_document_document_add', methods: ['POST'])] - public function addAction(Request $request): JsonResponse - { - $success = false; - $errorMessage = ''; - - // check for permission - $parentDocument = Document::getById($request->request->getInt('parentId')); - $document = null; - if ($parentDocument->isAllowed('create')) { - $intendedPath = $parentDocument->getRealFullPath() . '/' . $request->request->get('key'); - - if (!Document\Service::pathExists($intendedPath)) { - $createValues = [ - 'userOwner' => $this->getAdminUser()->getId(), - 'userModification' => $this->getAdminUser()->getId(), - 'published' => false, - ]; - - $createValues['key'] = Service::getValidKey($request->request->get('key'), 'document'); - - // check for a docType - $docType = Document\DocType::getById($request->request->get('docTypeId', '')); - - if ($docType) { - $createValues['template'] = $docType->getTemplate(); - $createValues['controller'] = $docType->getController(); - $createValues['staticGeneratorEnabled'] = $docType->getStaticGeneratorEnabled(); - } elseif ($translationsBaseDocumentId = $request->request->get('translationsBaseDocument')) { - $translationsBaseDocument = Document::getById((int) $translationsBaseDocumentId); - if ($translationsBaseDocument instanceof Document\PageSnippet) { - $createValues['template'] = $translationsBaseDocument->getTemplate(); - $createValues['controller'] = $translationsBaseDocument->getController(); - } - } elseif (in_array($request->request->get('type'), ['page', 'snippet', 'email'])) { - $createValues['controller'] = $this->getParameter('opendxp.documents.default_controller'); - } - - if ($request->request->has('inheritanceSource')) { - $createValues['contentMainDocumentId'] = $request->request->get('inheritanceSource'); - } - - switch ($request->request->get('type')) { - case 'page': - $document = Document\Page::create($parentDocument->getId(), $createValues, false); - $document->setTitle($request->request->get('title')); - $document->setProperty('navigation_name', 'text', $request->request->get('name'), false, false); - $document->save(); - $success = true; - - break; - case 'snippet': - $document = Document\Snippet::create($parentDocument->getId(), $createValues); - $success = true; - - break; - case 'email': //ckogler - $document = Document\Email::create($parentDocument->getId(), $createValues); - $success = true; - - break; - case 'link': - $document = Document\Link::create($parentDocument->getId(), $createValues); - $success = true; - - break; - case 'hardlink': - $document = Document\Hardlink::create($parentDocument->getId(), $createValues); - $success = true; - - break; - case 'folder': - $document = Document\Folder::create($parentDocument->getId(), $createValues); - $document->setPublished(true); - - try { - $document->save(); - $success = true; - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - - break; - default: - $classname = OpenDxp::getContainer()->get('opendxp.class.resolver.document')->resolve($request->request->get('type')); - - if (Tool::classExists($classname)) { - $document = $classname::create($parentDocument->getId(), $createValues); - - try { - $document->save(); - $success = true; - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - - break; - } - - Logger::debug("Unknown document type, can't add [ " . $request->request->get('type') . ' ] '); - - break; - } - } else { - $errorMessage = "prevented adding a document because document with same path+key [ $intendedPath ] already exists"; - Logger::debug($errorMessage); - } - } else { - $errorMessage = 'prevented adding a document because of missing permissions'; - Logger::debug($errorMessage); - } - - if ($success && $document instanceof Document) { - if ($translationsBaseDocumentId = $request->request->get('translationsBaseDocument')) { - $translationsBaseDocument = Document::getById((int) $translationsBaseDocumentId); - - $properties = $translationsBaseDocument->getProperties(); - $properties = [...$properties, ...$document->getProperties()]; - $document->setProperties($properties); - $document->setProperty('language', 'text', $request->request->get('language'), false, true); - $document->save(); - - $service = new Document\Service(); - $service->addTranslation($translationsBaseDocument, $document); - } - - return $this->adminJson([ - 'success' => $success, - 'id' => $document->getId(), - 'type' => $document->getType(), - ]); - } - - return $this->adminJson([ - 'success' => $success, - 'message' => $errorMessage, - ]); - } - + public function addAction(Request $request, AddDocumentHandler $handler): JsonResponse + { + $result = $handler( + parentId: $request->request->getInt('parentId'), + type: $request->request->getString('type'), + key: $request->request->getString('key'), + docTypeId: $request->request->get('docTypeId'), + translationsBaseDocumentId: $request->request->get('translationsBaseDocument'), + language: $request->request->get('language'), + inheritanceSource: $request->request->has('inheritanceSource') ? $request->request->get('inheritanceSource') : null, + title: $request->request->get('title'), + name: $request->request->get('name'), + ); + + return $this->adminJson(ApiResponse::ok([ + 'id' => $result->document->getId(), + 'type' => $result->document->getType(), + ])); + } + + #[IsGranted(CorePermission::Documents->value)] #[Route('/delete', name: 'opendxp_admin_document_document_delete', methods: ['DELETE'])] - public function deleteAction(Request $request): JsonResponse + public function deleteAction(Request $request, DeleteDocumentHandler $handler): JsonResponse { - $type = $request->request->get('type'); - - if ($type === 'children') { - $parentDocument = Document::getById((int) $request->request->get('id')); - - $list = new Document\Listing(); - $list->setCondition('`path` LIKE ?', [$list->escapeLike($parentDocument->getRealFullPath()) . '/%']); - $list->setLimit((int)$request->request->get('amount')); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('DESC'); - - $documents = $list->load(); - - $deletedItems = []; - foreach ($documents as $document) { - $deletedItems[$document->getId()] = $document->getRealFullPath(); - if ($document->isAllowed('delete') && !$document->isLocked()) { - $document->delete(); - } - } - - return $this->adminJson(['success' => true, 'deleted' => $deletedItems]); - } - - if ($id = $request->request->get('id')) { - $document = Document::getById((int) $id); - if ($document && $document->isAllowed('delete')) { - try { - if ($document->isLocked()) { - throw new Exception('prevented deleting document, because it is locked: ID: ' . $document->getId()); - } - $document->delete(); + $type = $request->request->getString('type'); + $id = $request->request->getInt('id'); + $amount = $request->request->getInt('amount'); - return $this->adminJson(['success' => true]); - } catch (Exception $e) { - Logger::err((string) $e); + $result = $handler($type, $id, $amount); - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } + if ($type === 'children') { + return $this->adminJson(ApiResponse::ok(['deleted' => $result->deleted])); } - throw $this->createAccessDeniedHttpException(); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - * @throws RuntimeException - */ + #[IsGranted(CorePermission::Documents->value)] #[Route('/update', name: 'opendxp_admin_document_document_update', methods: ['PUT'])] - public function updateAction(Request $request): JsonResponse - { - $data = ['success' => false]; - $allowUpdate = true; - - $document = Document::getById((int) $request->request->get('id')); - - $oldPath = $document->getDao()->getCurrentFullPath(); - $oldDocument = Document::getById($document->getId(), ['force' => true]); - - // this prevents the user from renaming, relocating (actions in the tree) if the newest version isn't published - // the reason is that otherwise the content of the newer not published version will be overwritten - if ($document instanceof Document\PageSnippet) { - $latestVersion = $document->getLatestVersion(); - if ($latestVersion && - $latestVersion->getData()->getModificationDate() != $document->getModificationDate() - ) { - return $this->adminJson( - [ - 'success' => false, - 'message' => "You can't rename or relocate if there's a newer not published version", - ]); - } - } - - if ($document->isAllowed('settings')) { - // if the position is changed the path must be changed || also from the children - if ($parentId = $request->request->get('parentId')) { - $parentDocument = Document::getById((int) $parentId); - - //check if parent is changed - if ($document->getParentId() !== $parentDocument->getId()) { - if (!$parentDocument->isAllowed('create')) { - throw new RuntimeException('Prevented moving document - no create permission on new parent.'); - } - - $intendedPath = $parentDocument->getRealPath(); - $pKey = $parentDocument->getKey(); - if (!empty($pKey)) { - $intendedPath .= $parentDocument->getKey() . '/'; - } - - $documentWithSamePath = Document::getByPath($intendedPath . $document->getKey()); - - if ($documentWithSamePath != null) { - $allowUpdate = false; - } - - if ($document->isLocked()) { - $allowUpdate = false; - } - } - } - - if ($allowUpdate) { - $blockedVars = ['id', 'controller', 'action', 'module']; - - if (!$document->isAllowed('rename') && $request->request->get('key')) { - $blockedVars[] = 'key'; - Logger::debug('prevented renaming document because of missing permissions '); - } - - $updateData = [...$request->request->all(), ...$request->query->all()]; - - foreach ($updateData as $key => $value) { - if (!in_array($key, $blockedVars)) { - $document->setValue($key, $value); - } - } - - $document->setUserModification($this->getAdminUser()->getId()); - - try { - $document->save(); - - if ($request->request->get('index') !== null) { - $this->updateIndexesOfDocumentSiblings($document, $request->request->get('index')); - } - - $data = [ - 'success' => true, - 'treeData' => $this->getTreeNodeConfig($document), - ]; - if ($oldPath && $oldPath != $document->getRealFullPath()) { - $this->firePostMoveEvent($document, $oldDocument, $oldPath); - } - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } else { - $msg = 'prevented moving document, because document with same path+key already exists' . - ' or the document is locked. ID: ' . $document->getId(); - Logger::debug($msg); - - return $this->adminJson(['success' => false, 'message' => $msg]); - } - } elseif ($document->isAllowed('rename') && $request->request->get('key')) { - //just rename - try { - $document->setKey($request->request->get('key')); - $document->setUserModification($this->getAdminUser()->getId()); - $document->save(); - $data = [ - 'success' => true, - 'treeData' => $this->getTreeNodeConfig($document), - ]; - - if ($oldPath && $oldPath != $document->getRealFullPath()) { - $this->firePostMoveEvent($document, $oldDocument, $oldPath); - } - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } else { - Logger::debug('Prevented update document, because of missing permissions.'); - } - - return $this->adminJson($data); - } - - private function firePostMoveEvent(Document $document, Document $oldDocument, string $oldPath): void - { - $arguments = [ - 'oldPath' => $oldPath, - 'oldDocument' => $oldDocument, - ]; - $documentEvent = new OpenDxp\Event\Model\DocumentEvent($document, $arguments); - $this->dispatchEvent($documentEvent, OpenDxp\Event\DocumentEvents::POST_MOVE_ACTION); - } - - protected function updateIndexesOfDocumentSiblings(Document $document, int $newIndex): void + public function updateAction(Request $request, UpdateDocumentHandler $handler): JsonResponse { - $updateLatestVersionIndex = function ($document, $newIndex): void { - if ($document instanceof Document\PageSnippet && $latestVersion = $document->getLatestVersion()) { - $document = $latestVersion->loadData(); - $document->setIndex($newIndex); - $latestVersion->save(); - } - }; - - // if changed the index change also all documents on the same level - - $document->saveIndex($newIndex); + $updateData = [...$request->request->all(), ...$request->query->all()]; - $list = new Document\Listing(); - $list->setCondition('parentId = ? AND id != ?', [$document->getParentId(), $document->getId()]); - $list->setOrderKey('index'); - $list->setOrder('asc'); - $childrenList = $list->load(); + $result = $handler((int) $request->request->get('id'), $updateData); - $count = 0; - foreach ($childrenList as $child) { - if ($count === $newIndex) { - $count++; - } - $child->saveIndex($count); - $updateLatestVersionIndex($child, $count); - $count++; - } + return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } #[Route('/doc-types', name: 'opendxp_admin_document_document_doctypesget', methods: ['GET'])] - public function docTypesGetAction(Request $request): JsonResponse + public function docTypesGetAction(GetDocTypesListHandler $getDocTypesList): JsonResponse { - // get list of types - $list = new DocType\Listing(); + $result = $getDocTypesList(); - $docTypes = []; - foreach ($list->getDocTypes() as $type) { - if ($this->getAdminUser()->isAllowed($type->getId(), 'docType')) { - $data = $type->getObjectVars(); - $data['writeable'] = $type->isWriteable(); - $docTypes[] = $data; - } - } - - return $this->adminJson(['data' => $docTypes, 'success' => true, 'total' => count($docTypes)]); + return $this->adminJson(ApiResponse::ok(['data' => $result->docTypes, 'total' => $result->total])); } + #[IsGranted(CorePermission::Documents->value)] + #[IsGranted(CorePermission::DocumentTypes->value)] #[Route('/doc-types', name: 'opendxp_admin_document_document_doctypes', methods: ['PUT', 'POST', 'DELETE'])] - public function docTypesAction(Request $request): JsonResponse + public function docTypesAction( + Request $request, + ManageDocTypesHandler $handler, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { if ($request->request->get('data')) { - $this->checkPermission('document_types'); - $data = $this->decodeJson($request->request->get('data')); - if ($request->query->get('xaction') === 'destroy') { - $type = Document\DocType::getById($data['id']); - if (!$type->isWriteable()) { - throw new ConfigWriteException(); - } - $type->delete(); + $result = $handler($xaction, $data); - return $this->adminJson(['success' => true, 'data' => []]); - } - if ($request->query->get('xaction') === 'update') { - // save type - $type = Document\DocType::getById($data['id']); - if (!$type->isWriteable()) { - throw new ConfigWriteException(); - } - $type->setValues($data); - $type->save(); - $responseData = $type->getObjectVars(); - $responseData['writeable'] = $type->isWriteable(); - - return $this->adminJson(['data' => $responseData, 'success' => true]); - } - - if ($request->query->get('xaction') === 'create') { - if (!(new DocType())->isWriteable()) { - throw new ConfigWriteException(); - } - unset($data['id']); - // save type - $type = Document\DocType::create(); - $type->setValues($data); - $type->save(); - $responseData = $type->getObjectVars(); - $responseData['writeable'] = $type->isWriteable(); - - return $this->adminJson(['data' => $responseData, 'success' => true]); - } + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } return $this->adminJson(false); } - /** - * @throws BadRequestHttpException If type is invalid - */ + #[IsGranted(CorePermission::Documents->value)] #[Route('/get-doc-types', name: 'opendxp_admin_document_document_getdoctypes', methods: ['GET'])] - public function getDocTypesAction(Request $request): JsonResponse - { - $list = new DocType\Listing(); - if ($type = $request->query->get('type')) { - if (!Document\Service::isValidType($type)) { - throw new BadRequestHttpException('Invalid type: ' . $type); - } - $list->setFilter(static fn (DocType $docType) => $docType->getType() === $type); - } - - $docTypes = []; - foreach ($list->getDocTypes() as $type) { - $docTypes[] = $type->getObjectVars(); - } - - return $this->adminJson(['docTypes' => $docTypes]); - } - - #[Route('/version-to-session', name: 'opendxp_admin_document_document_versiontosession', methods: ['POST'])] - public function versionToSessionAction(Request $request): Response + public function getDocTypesAction( + GetDocTypesByTypeHandler $getDocTypesByType, + #[MapQueryParameter] ?string $type = null, + ): JsonResponse { - $id = $request->request->getInt('id'); - $version = Version::getById($id); - $document = $version?->loadData(); - if (!$document) { - throw $this->createNotFoundException('Version with id [' . $id . "] doesn't exist"); - } - Document\Service::saveElementToSession($document, $request->getSession()->getId()); + $result = $getDocTypesByType($type); - return new Response(); - } - - #[Route('/publish-version', name: 'opendxp_admin_document_document_publishversion', methods: ['POST'])] - public function publishVersionAction(Request $request): JsonResponse - { - $this->versionToSessionAction($request); - - $id = $request->request->getInt('id'); - $version = Version::getById($id); - $document = $version?->loadData(); - if (!$document) { - throw $this->createNotFoundException('Version with id [' . $id . "] doesn't exist"); - } - - $currentDocument = Document::getById($document->getId()); - if ($currentDocument->isAllowed('publish')) { - $document->setPublished(true); - - try { - $document->setKey($currentDocument->getKey()); - $document->setPath($currentDocument->getRealPath()); - $document->setUserModification($this->getAdminUser()->getId()); - - $document->save(); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - $treeData = []; - $this->addAdminStyle($document, ElementAdminStyleEvent::CONTEXT_EDITOR, $treeData); - - return $this->adminJson(['success' => true, 'treeData' => $treeData]); + return $this->adminJson(['docTypes' => $result->docTypes]); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/get-site-custom-settings', name: 'opendxp_admin_document_document_get_site_custom_settings', methods: ['POST'])] - public function getSiteCustomSettingsAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse + public function getSiteCustomSettingsAction(Request $request, GetSiteCustomSettingsHandler $getSiteCustomSettings): JsonResponse { - $site = Site::getById($request->request->getInt('id')); - - $event = new SiteCustomSettingsEvent($site); - $eventDispatcher->dispatch($event, AdminEvents::SITE_CUSTOM_SETTINGS); + $result = $getSiteCustomSettings($request->request->getInt('id')); - $customSettings = $event->getConfigNodes(); - - return $this->adminJson([ - 'data' => $customSettings, - ]); + return $this->adminJson(['data' => $result->nodes]); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/update-site', name: 'opendxp_admin_document_document_updatesite', methods: ['PUT'])] - public function updateSiteAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse + public function updateSiteAction(Request $request, UpdateSiteHandler $handler): JsonResponse { $domains = $request->request->getString('domains'); $domains = str_replace(' ', '', $domains); $domains = $domains ? explode("\n", $domains) : []; - if (!$site = Site::getByRootId($request->request->getInt('id'))) { - $site = Site::create([ - 'rootId' => $request->request->getInt('id'), - ]); - } - $localizedErrorDocuments = []; - $validLanguages = Tool::getValidLanguages(); - - foreach ($validLanguages as $language) { - // localized error pages + foreach (Tool::getValidLanguages() as $language) { $requestValue = $request->request->get(sprintf('errorDocument_localized_%s', $language)); - if (isset($requestValue)) { $localizedErrorDocuments[$language] = $requestValue; } } - $event = new SiteCustomSettingsEvent($site); - $eventDispatcher->dispatch($event, AdminEvents::SITE_CUSTOM_SETTINGS); - - $customSettings = []; - foreach ($event->getConfigNodes() as $scope => $nodes) { - foreach ($nodes as $node) { - $requestValueName = sprintf('customSettings_%s_%s', $scope, $node['name']); - if ($request->request->has($requestValueName)) { - $value = $request->request->get($requestValueName); - if ($node['type'] === OpenDxp\Bundle\AdminBundle\Enum\SiteCustomConfigNodeType::CHECKBOX->value) { - $value = $value === 'true'; - } - - $customSettings[$scope][$node['name']] = $value; - } - } - } - - $site->setDomains($domains); - $site->setMainDomain($request->request->getString('mainDomain')); - $site->setErrorDocument($request->request->getString('errorDocument')); - $site->setLocalizedErrorDocuments($localizedErrorDocuments); - $site->setRedirectToMainDomain($request->request->getBoolean('redirectToMainDomain')); - $site->setCustomSettings(count($customSettings) === 0 ? null : $customSettings); - $site->save(); + $result = $handler( + rootId: $request->request->getInt('id'), + domains: $domains, + mainDomain: $request->request->getString('mainDomain'), + errorDocument: $request->request->getString('errorDocument'), + localizedErrorDocuments: $localizedErrorDocuments, + redirectToMainDomain: $request->request->getBoolean('redirectToMainDomain'), + requestCustomSettings: $request->request->all(), + ); - $site->setRootDocument(null); // do not send the document to the frontend - - return $this->adminJson($site->getObjectVars()); + return $this->adminJson($result->siteVars); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/remove-site', name: 'opendxp_admin_document_document_removesite', methods: ['DELETE'])] - public function removeSiteAction(Request $request): JsonResponse - { - $site = Site::getByRootId($request->request->getInt('id')); - $site->delete(); - - return $this->adminJson(['success' => true]); - } - - #[Route('/copy-info', name: 'opendxp_admin_document_document_copyinfo', methods: ['GET'])] - public function copyInfoAction(Request $request): JsonResponse - { - $transactionId = time(); - $pasteJobs = []; - - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { - $session->set((string) $transactionId, ['idMapping' => []]); - }, 'opendxp_copy'); - - if ($request->query->get('type') === 'recursive' || $request->query->get('type') === 'recursive-update-references') { - $document = Document::getById((int) $request->query->get('sourceId')); - - // first of all the new parent - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $request->query->get('sourceId'), - 'targetId' => $request->query->get('targetId'), - 'type' => 'child', - 'language' => $request->query->get('language'), - 'enableInheritance' => $request->query->get('enableInheritance'), - 'transactionId' => $transactionId, - 'saveParentId' => true, - 'resetIndex' => true, - ], - ]]; - - $childIds = []; - if ($document->hasChildren()) { - // get amount of children - $list = new Document\Listing(); - $list->setCondition('`path` LIKE ?', [$list->escapeLike($document->getRealFullPath()) . '/%']); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('ASC'); - $childIds = $list->loadIdList(); - - if (count($childIds) > 0) { - foreach ($childIds as $id) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $id, - 'targetParentId' => $request->query->get('targetId'), - 'sourceParentId' => $request->query->get('sourceId'), - 'type' => 'child', - 'language' => $request->query->get('language'), - 'enableInheritance' => $request->query->get('enableInheritance'), - 'transactionId' => $transactionId, - ], - ]]; - } - } - } - - // add id-rewrite steps - if ($request->query->get('type') === 'recursive-update-references') { - for ($i = 0; $i < (count($childIds) + 1); $i++) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_document_document_copyrewriteids'), - 'method' => 'PUT', - 'params' => [ - 'transactionId' => $transactionId, - 'enableInheritance' => $request->query->get('enableInheritance'), - '_dc' => uniqid('', false), - ], - ]]; - } - } - } elseif ($request->query->get('type') === 'child' || $request->query->get('type') === 'replace') { - // the object itself is the last one - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $request->query->get('sourceId'), - 'targetId' => $request->query->get('targetId'), - 'type' => $request->query->get('type'), - 'language' => $request->query->get('language'), - 'enableInheritance' => $request->query->get('enableInheritance'), - 'transactionId' => $transactionId, - 'resetIndex' => ($request->query->get('type') === 'child'), - ], - ]]; - } - - return $this->adminJson([ - 'pastejobs' => $pasteJobs, - ]); - } - - #[Route('/copy-rewrite-ids', name: 'opendxp_admin_document_document_copyrewriteids', methods: ['PUT'])] - public function copyRewriteIdsAction(Request $request): JsonResponse - { - $transactionId = $request->request->get('transactionId'); - - $idStore = Session::useBag($request->getSession(), static fn (AttributeBagInterface $session) => $session->get($transactionId), 'opendxp_copy'); - - if (!array_key_exists('rewrite-stack', $idStore)) { - $idStore['rewrite-stack'] = array_values($idStore['idMapping']); - } - - $id = array_shift($idStore['rewrite-stack']); - $document = Document::getById((int) $id); - - if ($document) { - // create rewriteIds() config parameter - $rewriteConfig = ['document' => $idStore['idMapping']]; - - $document = Document\Service::rewriteIds($document, $rewriteConfig, [ - 'enableInheritance' => $request->request->get('enableInheritance') === 'true', - ]); - - $document->setUserModification($this->getAdminUser()->getId()); - $document->save(); - } - - // write the store back to the session - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId, $idStore): void { - $session->set($transactionId, $idStore); - }, 'opendxp_copy'); - - return $this->adminJson([ - 'success' => true, - 'id' => $id, - ]); - } - - #[Route('/copy', name: 'opendxp_admin_document_document_copy', methods: ['POST'])] - public function copyAction(Request $request): JsonResponse - { - $success = false; - $sourceId = (int)$request->request->get('sourceId'); - $source = Document::getById($sourceId); - $session = Session::getSessionBag($request->getSession(), 'opendxp_copy'); - - $targetId = (int)$request->request->get('targetId'); - - $sessionBag = $session->get($request->request->get('transactionId')); - - if ($request->request->get('targetParentId')) { - $sourceParent = Document::getById((int) $request->request->get('sourceParentId')); - - // this is because the key can get the prefix "_copy" if the target does already exists - if ($sessionBag['parentId']) { - $targetParent = Document::getById((int) $sessionBag['parentId']); - } else { - $targetParent = Document::getById((int) $request->request->get('targetParentId')); - } - - $targetPath = preg_replace('@^' . $sourceParent->getRealFullPath() . '@', $targetParent . '/', $source->getRealPath()); - $target = Document::getByPath($targetPath); - } else { - $target = Document::getById($targetId); - } - - if ($target instanceof Document) { - if ($target->isAllowed('create')) { - if ($source !== null) { - if ($source instanceof Document\PageSnippet && $latestVersion = $source->getLatestVersion()) { - $source = $latestVersion->loadData(); - $source->setPublished(false); //as latest version is used which is not published - } - - if ($request->request->get('type') === 'child') { - $enableInheritance = $request->request->get('enableInheritance') === 'true'; - - $language = (string) $request->request->get('language') ?: null; - if ($language && !Tool::isValidLanguage($language)) { - throw new BadRequestHttpException('Invalid language: ' . $language); - } - - $resetIndex = $request->request->get('resetIndex') === 'true'; - - $newDocument = $this->_documentService->copyAsChild($target, $source, $enableInheritance, $resetIndex, $language); - - $sessionBag['idMapping'][(int)$source->getId()] = (int)$newDocument->getId(); - - // this is because the key can get the prefix "_copy" if the target does already exists - if ($request->request->get('saveParentId')) { - $sessionBag['parentId'] = $newDocument->getId(); - } - $session->set($request->request->get('transactionId'), $sessionBag); - } elseif ($request->request->get('type') === 'replace') { - $this->_documentService->copyContents($target, $source); - } - - $success = true; - } else { - Logger::error('prevended copy/paste because document with same path+key already exists in this location'); - } - } else { - Logger::error('could not execute copy/paste because of missing permissions on target [ ' . $targetId . ' ]'); - - throw $this->createAccessDeniedHttpException(); - } - } - - return $this->adminJson(['success' => $success]); - } - - #[Route('/diff-versions/from/{from}/to/{to}', name: 'opendxp_admin_document_document_diffversions', requirements: ['from' => "\d+", 'to' => "\d+"], methods: ['GET'])] - public function diffVersionsAction( - Request $request, - int $from, - int $to, - DocumentRendererInterface $documentRenderer, - RouterInterface $router - ): Response { - // return with error if prerequisites do not match - if (!HtmlToImage::isSupported() || !class_exists('Imagick')) { - return $this->render('@OpenDxpAdmin/admin/document/document/diff_versions_unsupported.html.twig'); - } - - $versionFrom = Version::getById($from); - $docFrom = $versionFrom?->loadData(); - - if (!$docFrom) { - throw $this->createNotFoundException('Version with id [' . $from . "] doesn't exist"); - } - - $versionTo = Version::getById($to); - $docTo = $versionTo?->loadData(); - - if (!$docTo) { - throw $this->createNotFoundException('Version with id [' . $to . "] doesn't exist"); - } - - $comparisonId = uniqid(date('Y-m-d') . '-', true); - $tempFileTemplate = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/version-diff-tmp-' . $comparisonId . '-%s.%s'; - $fromImageFile = sprintf($tempFileTemplate, 'from', 'png'); - $toImageFile = sprintf($tempFileTemplate, 'to', 'png'); - $fromHtmlFile = sprintf($tempFileTemplate, 'from', 'html'); - $toHtmlFile = sprintf($tempFileTemplate, 'to', 'html'); - - $viewParams = []; - - $docContentFrom = $documentRenderer->render($docFrom); - $docContentTo = $documentRenderer->render($docTo); - - file_put_contents($fromHtmlFile, $docContentFrom); - file_put_contents($toHtmlFile, $docContentTo); - - $prefix = Config::getSystemConfiguration('documents')['preview_url_prefix']; - if (empty($prefix)) { - $prefix = $request->getSchemeAndHttpHost(); - } - - try { - HtmlToImage::convert($prefix . $router->generate('opendxp_admin_document_document_diff_versions_html', ['id' => basename($fromHtmlFile)]), $fromImageFile); - HtmlToImage::convert($prefix . $router->generate('opendxp_admin_document_document_diff_versions_html', ['id' => basename($toHtmlFile)]), $toImageFile); - } finally { - unlink($fromHtmlFile); - unlink($toHtmlFile); - } - - $image1 = new Imagick($fromImageFile); - $image2 = new Imagick($toImageFile); - - if ($image1->getImageWidth() === $image2->getImageWidth() && $image1->getImageHeight() === $image2->getImageHeight()) { - $result = $image1->compareImages($image2, Imagick::METRIC_MEANSQUAREERROR); - $result[0]->setImageFormat('png'); - - $viewParams['image'] = base64_encode($result[0]->getImageBlob()); - - $result[0]->clear(); - $result[0]->destroy(); - - } else { - $viewParams['image1'] = base64_encode(file_get_contents($fromImageFile)); - $viewParams['image2'] = base64_encode(file_get_contents($toImageFile)); - } - - // cleanup - $image1->clear(); - $image1->destroy(); - $image2->clear(); - $image2->destroy(); - - unlink($fromImageFile); - unlink($toImageFile); - - return $this->render('@OpenDxpAdmin/admin/document/document/diff_versions.html.twig', $viewParams); - } - - public function diffVersionsHtmlAction(Request $request): BinaryFileResponse + public function removeSiteAction(Request $request, RemoveSiteHandler $removeSite): JsonResponse { - $file = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . basename($request->query->get('id')); - if (file_exists($file)) { - return new BinaryFileResponse($file); - } + $removeSite($request->request->getInt('id')); - throw $this->createNotFoundException('Version diff file not found'); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/get-id-for-path', name: 'opendxp_admin_document_document_getidforpath', methods: ['GET'])] - public function getIdForPathAction(Request $request): JsonResponse + public function getIdForPathAction( + GetDocumentIdForPathHandler $getDocumentIdForPath, + #[MapQueryParameter] ?string $path = null, + ): JsonResponse { - if ($doc = Document::getByPath($request->query->get('path'))) { - return $this->adminJson([ - 'id' => $doc->getId(), - 'type' => $doc->getType(), - ]); + $result = $getDocumentIdForPath($path); + if (!$result) { + return $this->adminJson(false); } - return $this->adminJson(false); + return $this->adminJson(['id' => $result->id, 'type' => $result->type]); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/language-tree', name: 'opendxp_admin_document_document_languagetree', methods: ['GET'])] - public function languageTreeAction(Request $request): JsonResponse + public function languageTreeAction( + GetLanguageTreeHandler $handler, + #[MapQueryParameter] int $node = 0, + #[MapQueryParameter] ?string $languages = null, + ): JsonResponse { - $document = Document::getById((int) $request->query->get('node')); - - $languages = explode(',', $request->query->get('languages')); + $result = $handler($node, explode(',', (string) $languages)); - $result = []; - foreach ($document->getChildren() as $child) { - $result[] = $this->getTranslationTreeNodeConfig($child, $languages); - } - - return $this->adminJson($result); + return $this->adminJson($result->nodes); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Documents->value)] #[Route('/language-tree-root', name: 'opendxp_admin_document_document_languagetreeroot', methods: ['GET'])] - public function languageTreeRootAction(Request $request): JsonResponse + public function languageTreeRootAction( + GetLanguageTreeRootHandler $handler, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { - $document = Document::getById((int) $request->query->get('id')); - - if (!$document) { - return $this->adminJson([ - 'success' => false, - ]); - } - $service = new Document\Service(); - - $locales = Tool::getSupportedLocales(); - - $lang = $document->getProperty('language'); - - $columns = [ - [ - 'xtype' => 'treecolumn', - 'text' => $lang ? $locales[$lang] : '', - 'dataIndex' => 'text', - 'cls' => $lang ? 'x-column-header_' . strtolower($lang) : null, - 'width' => 300, - 'sortable' => false, - ], - ]; - - $translations = $service->getTranslations($document); - - $combinedTranslations = $translations; - - if ($parentDocument = $document->getParent()) { - $parentTranslations = $service->getTranslations($parentDocument); - foreach ($parentTranslations as $language => $languageDocumentId) { - $combinedTranslations[$language] = $translations[$language] ?? $languageDocumentId; - } - } - - foreach ($combinedTranslations as $language => $languageDocumentId) { - $languageDocument = Document::getById($languageDocumentId); - - if ($languageDocument && $languageDocument->isAllowed('list') && $language != $document->getProperty('language')) { - $columns[] = [ - 'text' => $locales[$language], - 'dataIndex' => $language, - 'cls' => 'x-column-header_' . strtolower($language), - 'width' => 300, - 'sortable' => false, - ]; - } - } + $result = $handler($id); return $this->adminJson([ - 'root' => $this->getTranslationTreeNodeConfig($document, array_keys($translations), $translations), - 'columns' => $columns, - 'languages' => array_keys($translations), + 'root' => $result->root, + 'columns' => $result->columns, + 'languages' => $result->languages, ]); } - private function getTranslationTreeNodeConfig(Document $document, array $languages, ?array $translations = null): array - { - $service = new Document\Service(); - - $config = $this->elementService->getElementTreeNodeConfig($document); - - $translations = is_null($translations) ? $service->getTranslations($document) : $translations; - - foreach ($languages as $language) { - if ($languageDocument = $translations[$language] ?? false) { - $languageDocument = Document::getById((int)$languageDocument); - $config[$language] = [ - 'text' => $languageDocument->getKey(), - 'id' => $languageDocument->getId(), - 'type' => $languageDocument->getType(), - 'fullPath' => $languageDocument->getFullPath(), - 'published' => $languageDocument->getPublished(), - 'itemType' => 'document', - 'permissions' => $languageDocument->getUserPermissions($this->getAdminUser()), - ]; - } elseif (!$document instanceof Document\Folder) { - $config[$language] = [ - 'text' => '--', - 'itemType' => 'empty', - ]; - } - } - - return $config; - } - + #[IsGranted(CorePermission::Documents->value)] #[Route('/convert', name: 'opendxp_admin_document_document_convert', methods: ['PUT'])] - public function convertAction(Request $request): JsonResponse + public function convertAction(Request $request, ConvertDocumentHandler $handler): JsonResponse { - $document = Document::getById((int) $request->request->get('id')); - if (!$document) { - throw $this->createNotFoundException(); - } - - $type = $request->request->get('type'); - $class = '\\OpenDxp\\Model\\Document\\' . ucfirst($type); - if (Tool::classExists($class)) { - $new = new $class; - - // overwrite internal store to avoid "duplicate full path" error - RuntimeCache::set('document_' . $document->getId(), $new); + $handler((int) $request->request->get('id'), $request->request->get('type')); - $props = $document->getObjectVars(); - foreach ($props as $name => $value) { - if (in_array($name, ['children', 'siblings', 'scheduledTasks', 'controller', 'template'])) { - continue; - } - $new->setValue($name, $value); - } - - if ($type === 'hardlink' || $type === 'folder') { - // remove navigation settings - foreach (['name', 'title', 'target', 'exclude', 'class', 'anchor', 'parameters', 'relation', 'accesskey', 'tabindex'] as $propertyName) { - $new->removeProperty('navigation_' . $propertyName); - } - } - - $new->setType($type); - $new->save(); - } - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/translation-determine-parent', name: 'opendxp_admin_document_document_translationdetermineparent', methods: ['GET'])] - public function translationDetermineParentAction(Request $request): JsonResponse + public function translationDetermineParentAction( + DetermineTranslationParentHandler $handler, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $language = null, + ): JsonResponse { - $success = false; - $targetDocument = null; - - $document = Document::getById((int) $request->query->get('id')); - if ($document) { - $service = new Document\Service(); - $document = $document->getId() === 1 ? $document : $document->getParent(); - - $translations = $service->getTranslations($document); - if (isset($translations[$request->query->get('language')])) { - $targetDocument = Document::getById($translations[$request->query->get('language')]); - $success = true; - } - } + $result = $handler($id, $language); - return $this->adminJson([ - 'success' => $success, - 'targetPath' => $targetDocument?->getRealFullPath(), - 'targetId' => $targetDocument?->getid(), - ]); + return $this->adminJson(ApiResponse::fromBool($result->found, [ + 'targetPath' => $result->targetPath, + 'targetId' => $result->targetId, + ])); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/translation-add', name: 'opendxp_admin_document_document_translationadd', methods: ['POST'])] - public function translationAddAction(Request $request): JsonResponse + public function translationAddAction(Request $request, AddDocumentTranslationHandler $handler): JsonResponse { - $sourceDocument = Document::getById((int) $request->request->get('sourceId')); - $targetDocument = Document::getByPath($request->request->get('targetPath')); - - if ($sourceDocument && $targetDocument) { - if (empty($sourceDocument->getProperty('language'))) { - throw new Exception(sprintf('Source Document(ID:%s) Language(Properties) missing', $sourceDocument->getId())); - } - - if (empty($targetDocument->getProperty('language'))) { - throw new Exception(sprintf('Target Document(ID:%s) Language(Properties) missing', $sourceDocument->getId())); - } - - $service = new Document\Service; - if ($service->getTranslationSourceId($targetDocument) != $targetDocument->getId()) { - throw new Exception('Target Document already linked to Source Document ID('.$service->getTranslationSourceId($targetDocument).'). Please unlink existing relation first.'); - } - $service->addTranslation($sourceDocument, $targetDocument); - } + $handler($request->request->getInt('sourceId'), $request->request->getString('targetPath')); - return $this->adminJson([ - 'success' => true, - ]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/translation-remove', name: 'opendxp_admin_document_document_translationremove', methods: ['DELETE'])] - public function translationRemoveAction(Request $request): JsonResponse + public function translationRemoveAction(Request $request, RemoveDocumentTranslationHandler $handler): JsonResponse { - $sourceDocument = Document::getById($request->request->getInt('sourceId')); - $targetDocument = Document::getById($request->request->getInt('targetId')); - if ($sourceDocument && $targetDocument) { - $service = new Document\Service; - $service->removeTranslationLink($sourceDocument, $targetDocument); - } + $handler($request->request->getInt('sourceId'), $request->request->getInt('targetId')); - return $this->adminJson([ - 'success' => true, - ]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Documents->value)] #[Route('/translation-check-language', name: 'opendxp_admin_document_document_translationchecklanguage', methods: ['GET'])] - public function translationCheckLanguageAction(Request $request): JsonResponse + public function translationCheckLanguageAction( + CheckTranslationLanguageHandler $handler, + #[MapQueryParameter] ?string $path = null, + ): JsonResponse { - $success = false; - $language = null; - $translationLinks = null; - - $document = Document::getByPath($request->query->get('path')); - if ($document) { - $language = $document->getProperty('language'); - if ($language) { - $success = true; - } - - //check if document is already linked to other langauges - $translationLinks = array_keys($this->_documentService->getTranslations($document)); - } - - return $this->adminJson([ - 'success' => $success, - 'language' => $language, - 'translationLinks' => $translationLinks, - ]); - } - - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - // check permissions - $this->checkActionPermission($event, 'documents', ['docTypesGetAction', 'diffVersionsHtmlAction']); + $result = $handler($path); - $this->_documentService = new Document\Service($this->getAdminUser()); + return $this->adminJson(ApiResponse::fromBool($result->found, [ + 'language' => $result->language, + 'translationLinks' => $result->translationLinks, + ])); } #[Override] diff --git a/src/Controller/Admin/Document/DocumentControllerBase.php b/src/Controller/Admin/Document/DocumentControllerBase.php index 7e1d79be..fd4d619c 100644 --- a/src/Controller/Admin/Document/DocumentControllerBase.php +++ b/src/Controller/Admin/Document/DocumentControllerBase.php @@ -18,38 +18,35 @@ use Exception; use OpenDxp; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Controller\Traits\AdminStyleTrait; -use OpenDxp\Bundle\AdminBundle\Controller\Traits\ApplySchedulerDataTrait; -use OpenDxp\Bundle\AdminBundle\Controller\Traits\UserNameTrait; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Event\ElementAdminStyleEvent; +use OpenDxp\Bundle\AdminBundle\Handler\Document\ChangeMainDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Payload\Document\EmailPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Document\FolderPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Document\HardlinkPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Document\LinkPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Document\PagePayload; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; +use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper; +use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; -use OpenDxp\Bundle\PersonalizationBundle\Model\Document\Targeting\TargetingDocumentInterface; -use OpenDxp\Controller\KernelControllerEventInterface; use OpenDxp\Controller\Traits\ElementEditLockHelperTrait; -use OpenDxp\Logger; use OpenDxp\Model; use OpenDxp\Model\Document; -use OpenDxp\Model\Element; use OpenDxp\Model\Element\ElementInterface; -use OpenDxp\Model\Property; -use OpenDxp\Model\Version; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Session\SessionInterface; -use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal */ -abstract class DocumentControllerBase extends AdminAbstractController implements KernelControllerEventInterface +#[IsGranted(CorePermission::Documents->value)] +abstract class DocumentControllerBase extends AdminAbstractController { - use ApplySchedulerDataTrait; - use AdminStyleTrait; use ElementEditLockHelperTrait; - use UserNameTrait; public const string TASK_PUBLISH = 'publish'; @@ -65,365 +62,86 @@ abstract class DocumentControllerBase extends AdminAbstractController implements public const string TASK_DELETE = 'delete'; - public function __construct(protected ElementServiceInterface $elementService) - { - } - - /** - * @throws Exception - */ - protected function preSendDataActions(array &$data, Model\Document $document, ?Version $draftVersion = null): JsonResponse - { - $documentFromDatabase = Model\Document::getById($document->getId(), ['force' => true]); - - $data['versionDate'] = $documentFromDatabase->getModificationDate(); - $data['userPermissions'] = $document->getUserPermissions(); - $data['idPath'] = Element\Service::getIdPath($document); - - $data['php'] = [ - 'classes' => [$document::class, ...array_values(class_parents($document))], - 'interfaces' => array_values(class_implements($document)), - ]; - - $this->addAdminStyle($document, ElementAdminStyleEvent::CONTEXT_EDITOR, $data); - - if ($draftVersion && $documentFromDatabase->getModificationDate() < $draftVersion->getDate()) { - $data['draft'] = [ - 'id' => $draftVersion->getId(), - 'modificationDate' => $draftVersion->getDate(), - 'isAutoSave' => $draftVersion->isAutoSave(), - ]; - } - - $event = new GenericEvent($this, [ - 'data' => $data, - 'document' => $document, - ]); - OpenDxp::getEventDispatcher()->dispatch($event, AdminEvents::DOCUMENT_GET_PRE_SEND_DATA); - $data = $event->getArgument('data'); - - if ($document->isAllowed('view')) { - return $this->adminJson($data); - } - - throw $this->createAccessDeniedHttpException(); - } - - protected function addPropertiesToDocument(Request $request, Model\Document $document): void - { - // properties - if ($request->request->has('properties')) { - $properties = []; - // assign inherited properties - foreach ($document->getProperties() as $p) { - if ($p->isInherited()) { - $properties[$p->getName()] = $p; - } - } - - $propertiesData = $this->decodeJson($request->request->get('properties')); - - if (is_array($propertiesData)) { - foreach ($propertiesData as $propertyName => $propertyData) { - $value = $propertyData['data']; - - try { - $property = new Property(); - $property->setType($propertyData['type']); - $property->setName($propertyName); - $property->setCtype('document'); - $property->setDataFromEditmode($value); - $property->setInheritable($propertyData['inheritable']); - - if ($propertyName === 'language') { - $property->setInherited($this->getPropertyInheritance($document, $propertyName, $value)); - } - - $properties[$propertyName] = $property; - } catch (Exception) { - Logger::warning("Can't add " . $propertyName . ' to document ' . $document->getRealFullPath()); - } - } - } - if ($document->isAllowed('properties')) { - $document->setProperties($properties); - } - } - - // force loading of properties - $document->getProperties(); - } - - protected function addSettingsToDocument(Request $request, Model\Document $document): void - { - // settings - if ($request->request->has('settings') && $document->isAllowed('settings')) { - $settings = $this->decodeJson($request->request->get('settings')); - if (array_key_exists('prettyUrl', $settings)) { - $settings['prettyUrl'] = htmlspecialchars($settings['prettyUrl']); - } - $document->setValues($settings); - } - } - - protected function addDataToDocument(Request $request, Model\Document $document): void - { - if ($document instanceof Model\Document\PageSnippet) { - $isTargetSpecificEditable = - interface_exists(TargetingDocumentInterface::class) - && $document instanceof TargetingDocumentInterface - && $document->hasTargetGroupSpecificEditables(); - - if ($request->request->get('appendEditables') || $isTargetSpecificEditable) { - $document->getEditables(); - } else { - // ensure no editables (e.g. from session, version, ...) are still referenced - $document->setEditables(null); - } - - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data')); - foreach ($data as $name => $value) { - $data = $value['data'] ?? null; - $type = $value['type']; - $document->setRawEditable($name, $type, $data); - } - } - } - } - - protected function addTranslationsData(Model\Document $document, array &$data): void - { - $service = new Model\Document\Service; - $translations = $service->getTranslations($document); - $unlinkTranslations = $service->getTranslations($document, 'unlink'); - $language = $document->getProperty('language'); - unset($translations[$language], $unlinkTranslations[$language]); - $data['translations'] = $translations; - $data['unlinkTranslations'] = $unlinkTranslations; - } + public function __construct( + protected ElementServiceInterface $elementService, + protected readonly SessionService $sessionService, + protected readonly DocumentPayloadMapper $mapper, + ) {} #[Route('/save-to-session', name: 'savetosession', methods: ['POST'])] public function saveToSessionAction(Request $request): JsonResponse { - if ($documentId = (int) $request->request->get('id')) { - if (!$document = Model\Document\Service::getElementFromSession('document', $documentId, $request->getSession()->getId())) { - $document = Model\Document\PageSnippet::getById($documentId); - if (!$document) { - throw $this->createNotFoundException(); - } - $document = $this->getLatestVersion($document); - } - - // set dump state to true otherwise the properties will be removed because of the session-serialize - $document->setInDumpState(true); - $this->setValuesToDocument($request, $document); - - Model\Document\Service::saveElementToSession($document, $request->getSession()->getId()); + if (!($documentId = (int) $request->request->get('id'))) { + return $this->adminJson(ApiResponse::ok()); } - return $this->adminJson(['success' => true]); - } - - protected function saveToSession(Model\Document $doc, SessionInterface $session, bool $useForSave = false): void - { - // save to session - Model\Document\Service::saveElementToSession($doc, $session->getId()); - - if ($useForSave) { - Model\Document\Service::saveElementToSession($doc, $session->getId(), '_useForSave'); + $document = $this->sessionService->getOrLoadDocument($documentId); + if (!$document) { + throw $this->createNotFoundException(); } - } - - /** - * @return Model\Document|null $sessionDocument - */ - protected function getFromSession(Model\Document $doc, SessionInterface $session): ?Model\Document - { - $sessionDocument = null; - // check if there's a document in session which should be used as data-source - // see also PageController::clearEditableDataAction() | this is necessary to reset all fields and to get rid of - // outdated and unused data elements in this document (eg. entries of area-blocks) + $document->setInDumpState(true); - if (($sessionDocument = Model\Document\Service::getElementFromSession('document', $doc->getId(), $session->getId())) && - (Model\Document\Service::getElementFromSession('document', $doc->getId(), $session->getId(), '_useForSave'))) { - Model\Document\Service::removeElementFromSession('document', $doc->getId(), $session->getId(), '_useForSave'); + if ($document instanceof Document\Email) { + $this->mapper->applyPagePayload(EmailPayload::fromRequest($request), $document); + } elseif ($document instanceof Document\PageSnippet) { + $this->mapper->applyPagePayload(PagePayload::fromRequest($request), $document); + } elseif ($document instanceof Document\Link) { + $this->mapper->applyLinkPayload(LinkPayload::fromRequest($request), $document); + } elseif ($document instanceof Document\Hardlink) { + $this->mapper->applyHardlinkPayload(HardlinkPayload::fromRequest($request), $document); + } elseif ($document instanceof Document\Folder) { + $this->mapper->applyFolderPayload(FolderPayload::fromRequest($request), $document); } - return $sessionDocument; + $this->sessionService->saveDocument($document); + + return $this->adminJson(ApiResponse::ok()); } #[Route('/remove-from-session', name: 'removefromsession', methods: ['DELETE'])] public function removeFromSessionAction(Request $request): JsonResponse { - Model\Document\Service::removeElementFromSession('document', $request->request->get('id'), $request->getSession()->getId()); - - return $this->adminJson(['success' => true]); - } - - protected function minimizeProperties(Model\Document $document, array &$data): void - { - $data['properties'] = Model\Element\Service::minimizePropertiesForEditmode($document->getProperties()); - } - - protected function getPropertyInheritance(Model\Document $document, string $propertyName, mixed $propertyValue): bool - { - if ($document->getParent()) { - return $propertyValue == $document->getParent()->getProperty($propertyName); - } - - return false; - } - - /** - * @template T of Model\Document\PageSnippet - * - * @param T $document - * - * @return T - */ - protected function getLatestVersion(Model\Document\PageSnippet $document, ?Version &$draftVersion = null): Model\Document\PageSnippet - { - $latestVersion = $document->getLatestVersion($this->getAdminUser()->getId()); - if ($latestVersion) { - $latestDoc = $latestVersion->loadData(); - if ($latestDoc instanceof Model\Document\PageSnippet) { - $draftVersion = $latestVersion; - - return $latestDoc; - } - } + $this->sessionService->removeDocument((int) $request->request->get('id')); - return $document; + return $this->adminJson(ApiResponse::ok()); } /** * This is used for pages and snippets to change the main document (which is not saved with the normal save button) - * - * @throws Exception */ #[Route('/change-main-document', name: 'changemaindocument', methods: ['PUT'])] - public function changeMainDocumentAction(Request $request): JsonResponse + public function changeMainDocumentAction(Request $request, ChangeMainDocumentHandler $changeMainDocument): JsonResponse { - $doc = Model\Document\PageSnippet::getById((int) $request->request->get('id')); - if ($doc instanceof Model\Document\PageSnippet) { - $doc->setEditables([]); - $doc->setContentMainDocumentId($request->request->get('contentMainDocumentPath'), true); - $doc->saveVersion(); - } - - return $this->adminJson(['success' => true]); - } + $changeMainDocument( + (int) $request->request->get('id'), + (string) $request->request->get('contentMainDocumentPath'), + ); - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - // check permissions - $this->checkPermission('documents'); - } - - abstract protected function setValuesToDocument(Request $request, Model\Document $document): void; - - protected function handleTask(string $task, Model\Document\PageSnippet $page): void - { - if ($task === self::TASK_PUBLISH || $task === self::TASK_VERSION) { - $page->deleteAutoSaveVersions($this->getAdminUser()->getId()); - } + return $this->adminJson(ApiResponse::ok()); } - protected function checkForLock(Model\Document $document, string $sessionId): JsonResponse|bool + public function getTreeNodeConfig(ElementInterface $element): array { - // check for lock - if ($document->isAllowed(self::TASK_SAVE) - || $document->isAllowed(self::TASK_PUBLISH) - || $document->isAllowed(self::TASK_UNPUBLISH) - || $document->isAllowed(self::TASK_DELETE)) { - if (Element\Editlock::isLocked($document->getId(), 'document', $sessionId)) { - return $this->getEditLockResponse($document->getId(), 'document'); - } - Element\Editlock::lock($document->getId(), 'document', $sessionId); - } - - return true; + return $this->elementService->getElementTreeNodeConfig($element); } /** - * @throws Element\ValidationException * @throws Exception */ - protected function saveDocument(Model\Document $document, Request $request, bool $latestVersion = false, ?string $task = null): array + protected function preSendDataActions(array $data, Model\Document $document): JsonResponse { - if ($latestVersion && $document instanceof Model\Document\PageSnippet) { - $document = $this->getLatestVersion($document); - } - - //update modification info - $document->setModificationDate(time()); - $document->setUserModification($this->getAdminUser()->getId()); - - $task = strtolower($task ?? $request->query->get('task')); - $version = null; - switch ($task) { - case $task === self::TASK_PUBLISH && $document->isAllowed($task): - $this->setValuesToDocument($request, $document); - $document->setPublished(true); - $document->save(); - - break; - case $task === self::TASK_UNPUBLISH && $document->isAllowed($task): - $this->setValuesToDocument($request, $document); - $document->setPublished(false); - $document->save(); - - break; - case in_array($task, [self::TASK_SAVE, self::TASK_VERSION, self::TASK_AUTOSAVE]) - && $document->isAllowed(self::TASK_SAVE): - if ($document instanceof Model\Document\PageSnippet) { - $this->setValuesToDocument($request, $document); - if ($task === self::TASK_AUTOSAVE || $document->isPublished()) { - $version = $document->saveVersion(true, true, null, $task === self::TASK_AUTOSAVE); - } else { - $document->save(); - } - } - - break; - case $task === self::TASK_SCHEDULER && $document->isAllowed('settings'): - if ($document instanceof Model\Document\PageSnippet - || $document instanceof Model\Document\Hardlink - || $document instanceof Model\Document\Link) { - $this->applySchedulerDataToElement($request, $document, $this->getAdminUser()); - $document->saveScheduledTasks(); - } - - break; - default: - throw $this->createAccessDeniedHttpException(); - } + $event = new GenericEvent($this, [ + 'data' => $data, + 'document' => $document, + ]); + OpenDxp::getEventDispatcher()->dispatch($event, AdminEvents::DOCUMENT_GET_PRE_SEND_DATA); + $data = $event->getArgument('data'); - if ($document instanceof Model\Document\PageSnippet) { - $this->handleTask($task, $document); + if ($document->isAllowed('view')) { + return $this->adminJson($data); } - return [$task, $document, $version]; - } - - protected function populateUsersNames(Document $document, array &$data): void - { - $userOwnerName = $this->getUserName($document->getUserOwner()); - $userModificationName = ($document->getUserOwner() === $document->getUserModification()) ? $userOwnerName : $this->getUserName($document->getUserModification()); - $data['userOwnerUsername'] = $userOwnerName['userName']; - $data['userOwnerFullname'] = $userOwnerName['fullName']; - $data['userModificationUsername'] = $userModificationName['userName']; - $data['userModificationFullname'] = $userModificationName['fullName']; - } - - public function getTreeNodeConfig(ElementInterface $element): array - { - return $this->elementService->getElementTreeNodeConfig($element); + throw $this->createAccessDeniedHttpException(); } } diff --git a/src/Controller/Admin/Document/DocumentCopyController.php b/src/Controller/Admin/Document/DocumentCopyController.php new file mode 100644 index 00000000..c0417d39 --- /dev/null +++ b/src/Controller/Admin/Document/DocumentCopyController.php @@ -0,0 +1,189 @@ +value)] +class DocumentCopyController extends AdminAbstractController +{ + #[Route('/copy-info', name: 'opendxp_admin_document_document_copyinfo', methods: ['GET'])] + public function copyInfoAction( + GetDocumentChildIdsHandler $getChildIds, + Request $request, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] int $sourceId = 0, + #[MapQueryParameter] ?string $targetId = null, + #[MapQueryParameter] ?string $language = null, + #[MapQueryParameter] ?string $enableInheritance = null, + ): JsonResponse { + $transactionId = time(); + $pasteJobs = []; + + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { + $session->set((string) $transactionId, ['idMapping' => []]); + }, 'opendxp_copy'); + + if ($type === 'recursive' || $type === 'recursive-update-references') { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $sourceId, + 'targetId' => $targetId, + 'type' => 'child', + 'language' => $language, + 'enableInheritance' => $enableInheritance, + 'transactionId' => $transactionId, + 'saveParentId' => true, + 'resetIndex' => true, + ], + ]]; + + $childIds = $getChildIds($sourceId)->ids; + + foreach ($childIds as $id) { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $id, + 'targetParentId' => $targetId, + 'sourceParentId' => $sourceId, + 'type' => 'child', + 'language' => $language, + 'enableInheritance' => $enableInheritance, + 'transactionId' => $transactionId, + ], + ]]; + } + + if ($type === 'recursive-update-references') { + for ($i = 0; $i < (count($childIds) + 1); $i++) { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_document_document_copyrewriteids'), + 'method' => 'PUT', + 'params' => [ + 'transactionId' => $transactionId, + 'enableInheritance' => $enableInheritance, + '_dc' => uniqid('', false), + ], + ]]; + } + } + } elseif ($type === 'child' || $type === 'replace') { + $pasteJobs[] = [[ + 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $sourceId, + 'targetId' => $targetId, + 'type' => $type, + 'language' => $language, + 'enableInheritance' => $enableInheritance, + 'transactionId' => $transactionId, + 'resetIndex' => ($type === 'child'), + ], + ]]; + } + + return $this->adminJson(['pastejobs' => $pasteJobs]); + } + + #[Route('/copy-rewrite-ids', name: 'opendxp_admin_document_document_copyrewriteids', methods: ['PUT'])] + public function copyRewriteIdsAction(RewriteDocumentIdsHandler $rewriteIds, Request $request): JsonResponse + { + $transactionId = $request->request->get('transactionId'); + + $idStore = Session::useBag($request->getSession(), static fn (AttributeBagInterface $session) => $session->get($transactionId), 'opendxp_copy'); + + if (!array_key_exists('rewrite-stack', $idStore)) { + $idStore['rewrite-stack'] = array_values($idStore['idMapping']); + } + + $id = array_shift($idStore['rewrite-stack']); + $enableInheritance = $request->request->get('enableInheritance') === 'true'; + + $rewriteIds((int) $id, $idStore['idMapping'], $enableInheritance); + + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId, $idStore): void { + $session->set($transactionId, $idStore); + }, 'opendxp_copy'); + + return $this->adminJson(ApiResponse::ok(['id' => $id])); + } + + #[Route('/copy', name: 'opendxp_admin_document_document_copy', methods: ['POST'])] + public function copyAction(CopyDocumentHandler $copyDocument, Request $request): JsonResponse + { + $sourceId = (int) $request->request->get('sourceId'); + $targetId = (int) $request->request->get('targetId'); + $type = (string) $request->request->get('type'); + + $session = Session::getSessionBag($request->getSession(), 'opendxp_copy'); + $sessionBag = $session->get($request->request->get('transactionId')); + + $sourceParentId = $request->request->get('targetParentId') ? (int) $request->request->get('sourceParentId') : null; + $targetParentId = $request->request->get('targetParentId') ? (int) $request->request->get('targetParentId') : null; + $sessionParentId = !empty($sessionBag['parentId']) ? (int) $sessionBag['parentId'] : null; + + $enableInheritance = $request->request->get('enableInheritance') === 'true'; + $resetIndex = $request->request->get('resetIndex') === 'true'; + $language = ($request->request->get('language') ?: null); + + $result = $copyDocument( + $sourceId, + $targetId, + $type, + $sourceParentId, + $targetParentId, + $sessionParentId, + $enableInheritance, + $resetIndex, + $language, + ); + + if ($result->newDocument !== null) { + $sessionBag['idMapping'][$result->sourceId] = $result->newDocument->getId(); + + if ($request->request->get('saveParentId')) { + $sessionBag['parentId'] = $result->newDocument->getId(); + } + + $session->set($request->request->get('transactionId'), $sessionBag); + } + + return $this->adminJson(ApiResponse::ok()); + } +} diff --git a/src/Controller/Admin/Document/DocumentVersionController.php b/src/Controller/Admin/Document/DocumentVersionController.php new file mode 100644 index 00000000..452e371e --- /dev/null +++ b/src/Controller/Admin/Document/DocumentVersionController.php @@ -0,0 +1,86 @@ +value)] + #[Route('/version-to-session', name: 'opendxp_admin_document_document_versiontosession', methods: ['POST'])] + public function versionToSessionAction(Request $request, SaveVersionToSessionHandler $saveToSession): Response + { + $saveToSession($request->request->getInt('id')); + + return new Response(); + } + + #[IsGranted(CorePermission::Documents->value)] + #[Route('/publish-version', name: 'opendxp_admin_document_document_publishversion', methods: ['POST'])] + public function publishVersionAction(Request $request, PublishVersionHandler $publishVersion): JsonResponse + { + $result = $publishVersion($request->request->getInt('id')); + + return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); + } + + #[IsGranted(CorePermission::Documents->value)] + #[Route('/diff-versions/from/{from}/to/{to}', name: 'opendxp_admin_document_document_diffversions', requirements: ['from' => "\d+", 'to' => "\d+"], methods: ['GET'])] + public function diffVersionsAction(Request $request, DiffVersionsHandler $diffVersions, int $from, int $to): Response + { + $result = $diffVersions($from, $to, $request->getSchemeAndHttpHost()); + + if (!$result->supported) { + return $this->render('@OpenDxpAdmin/admin/document/document/diff_versions_unsupported.html.twig'); + } + + return $this->render('@OpenDxpAdmin/admin/document/document/diff_versions.html.twig', [ + 'image' => $result->image, + 'image1' => $result->image1, + 'image2' => $result->image2, + ]); + } + + public function diffVersionsHtmlAction( + #[MapQueryParameter] ?string $id = null, + ): BinaryFileResponse + { + $file = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . basename($id); + if (file_exists($file)) { + return new BinaryFileResponse($file); + } + + throw $this->createNotFoundException('Version diff file not found'); + } +} diff --git a/src/Controller/Admin/Document/EmailController.php b/src/Controller/Admin/Document/EmailController.php index 6fa621ae..5948a886 100644 --- a/src/Controller/Admin/Document/EmailController.php +++ b/src/Controller/Admin/Document/EmailController.php @@ -1,5 +1,4 @@ query->get('id')); - - if (!$email) { - throw $this->createNotFoundException('Email not found'); + try { + $result = $handler($id); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - if (($lock = $this->checkForLock($email, $request->getSession()->getId())) instanceof JsonResponse) { - return $lock; - } - - $email = clone $email; - $draftVersion = null; - $email = $this->getLatestVersion($email, $draftVersion); - - $versions = Element\Service::getSafeVersionInfo($email->getVersions()); - $email->setVersions(array_splice($versions, -1, 1)); - $email->setParent(null); - - // unset useless data - $email->setEditables(null); - $email->setChildren(null); - - $data = $email->getObjectVars(); - $data['locked'] = $email->isLocked(); - - $this->addTranslationsData($email, $data); - $this->minimizeProperties($email, $data); - $this->populateUsersNames($email, $data); - - $data['url'] = $email->getUrl(); - - return $this->preSendDataActions($data, $email, $draftVersion); + return $this->preSendDataActions($result->data, $result->email); } - /** - * @throws Exception - */ #[Route('/save', name: 'save', methods: ['PUT', 'POST'])] - public function saveAction(Request $request): JsonResponse + public function saveAction(Request $request, SaveEmailHandler $handler): JsonResponse { - $page = Document\Email::getById((int) $request->request->get('id')); - if (!$page) { - throw $this->createNotFoundException('Email not found'); + try { + $result = $handler((int) $request->request->get('id'), EmailPayload::fromRequest($request)); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - [$task, $page, $version] = $this->saveDocument($page, $request); - $this->saveToSession($page, $request->getSession()); - - if ($task === self::TASK_PUBLISH || $task === self::TASK_UNPUBLISH) { - $treeData = $this->getTreeNodeConfig($page); - - return $this->adminJson([ - 'success' => true, + if ($result->task === self::TASK_PUBLISH || $result->task === self::TASK_UNPUBLISH) { + return $this->adminJson(ApiResponse::ok([ 'data' => [ - 'versionDate' => $page->getModificationDate(), - 'versionCount' => $page->getVersionCount(), + 'versionDate' => $result->email->getModificationDate(), + 'versionCount' => $result->email->getVersionCount(), ], - 'treeData' => $treeData, - ]); + 'treeData' => $result->treeData, + ])); } + $draftData = []; - if ($version) { + if ($result->version) { $draftData = [ - 'id' => $version->getId(), - 'modificationDate' => $version->getDate(), - 'isAutoSave' => $version->isAutoSave(), + 'id' => $result->version->getId(), + 'modificationDate' => $result->version->getDate(), + 'isAutoSave' => $result->version->isAutoSave(), ]; } - return $this->adminJson(['success' => true, 'draft' => $draftData]); - } - - protected function setValuesToDocument(Request $request, Document $document): void - { - $this->addSettingsToDocument($request, $document); - $this->addDataToDocument($request, $document); - $this->addPropertiesToDocument($request, $document); - $this->applySchedulerDataToElement($request, $document, $this->getAdminUser()); + return $this->adminJson(ApiResponse::ok(['draft' => $draftData])); } } diff --git a/src/Controller/Admin/Document/FolderController.php b/src/Controller/Admin/Document/FolderController.php index cb073f52..7a423b1b 100644 --- a/src/Controller/Admin/Document/FolderController.php +++ b/src/Controller/Admin/Document/FolderController.php @@ -1,4 +1,5 @@ query->get('id')); - if (!$folder) { - throw $this->createNotFoundException('Folder not found'); - } - - $folder = clone $folder; - $folder->setParent(null); - - $data = $folder->getObjectVars(); - $data['locked'] = $folder->isLocked(); - - $this->addTranslationsData($folder, $data); - $this->minimizeProperties($folder, $data); - $this->populateUsersNames($folder, $data); - - return $this->preSendDataActions($data, $folder); + $result = $handler($id); + return $this->preSendDataActions($result->data, $result->folder); } /** * @throws Exception */ #[Route('/save', name: 'save', methods: ['PUT', 'POST'])] - public function saveAction(Request $request): JsonResponse + public function saveAction(Request $request, SaveFolderHandler $handler): JsonResponse { - $folder = Document\Folder::getById((int) $request->request->get('id')); - if (!$folder) { - throw $this->createNotFoundException('Folder not found'); - } + $result = $handler((int) $request->request->get('id'), FolderPayload::fromRequest($request)); - $result = $this->saveDocument($folder, $request, false, self::TASK_PUBLISH); - /** @var Document\Folder $folder */ - $folder = $result[1]; - $treeData = $this->getTreeNodeConfig($folder); - - return $this->adminJson(['success' => true, 'treeData' => $treeData]); - } - - protected function setValuesToDocument(Request $request, Document $document): void - { - $this->addPropertiesToDocument($request, $document); + return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } } diff --git a/src/Controller/Admin/Document/HardlinkController.php b/src/Controller/Admin/Document/HardlinkController.php index e67e03cf..d2d2cce4 100644 --- a/src/Controller/Admin/Document/HardlinkController.php +++ b/src/Controller/Admin/Document/HardlinkController.php @@ -1,4 +1,5 @@ query->get('id')); - - if (!$link) { - throw $this->createNotFoundException('Hardlink not found'); - } - - if (($lock = $this->checkForLock($link, $request->getSession()->getId())) instanceof JsonResponse) { - return $lock; - } - - $link = clone $link; - $link->setParent(null); - - $data = $link->getObjectVars(); - $data['locked'] = $link->isLocked(); - $data['scheduledTasks'] = array_map( - static fn (Task $task) => $task->getObjectVars(), - $link->getScheduledTasks() - ); - - $this->addTranslationsData($link, $data); - $this->minimizeProperties($link, $data); - $this->populateUsersNames($link, $data); - - if ($link->getSourceDocument()) { - $data['sourcePath'] = $link->getSourceDocument()->getRealFullPath(); + try { + $result = $handler($id); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - return $this->preSendDataActions($data, $link); + return $this->preSendDataActions($result->data, $result->link); } /** * @throws Exception */ #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] - public function saveAction(Request $request): JsonResponse + public function saveAction(Request $request, SaveHardlinkHandler $handler): JsonResponse { - $link = Document\Hardlink::getById((int) $request->request->get('id')); - if (!$link) { - throw $this->createNotFoundException('Hardlink not found'); - } + $result = $handler((int) $request->request->get('id'), HardlinkPayload::fromRequest($request)); - $result = $this->saveDocument($link, $request); - /** @var Document\Hardlink $link */ - $link = $result[1]; - $treeData = $this->getTreeNodeConfig($link); - - return $this->adminJson([ - 'success' => true, + return $this->adminJson(ApiResponse::ok([ 'data' => [ - 'versionDate' => $link->getModificationDate(), - 'versionCount' => $link->getVersionCount(), + 'versionDate' => $result->link->getModificationDate(), + 'versionCount' => $result->link->getVersionCount(), ], - 'treeData' => $treeData, - ]); - } - - /** - * @param Document\Hardlink $document - */ - protected function setValuesToDocument(Request $request, Document $document): void - { - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data')); - - $sourceId = null; - if ($sourceDocument = Document::getByPath($data['sourcePath'])) { - $sourceId = $sourceDocument->getId(); - } - $document->setSourceId($sourceId); - $document->setValues($data); - } - - $this->addPropertiesToDocument($request, $document); - $this->applySchedulerDataToElement($request, $document, $this->getAdminUser()); + 'treeData' => $result->treeData, + ])); } } diff --git a/src/Controller/Admin/Document/LinkController.php b/src/Controller/Admin/Document/LinkController.php index 1b19a20f..c953ef11 100644 --- a/src/Controller/Admin/Document/LinkController.php +++ b/src/Controller/Admin/Document/LinkController.php @@ -1,4 +1,5 @@ query->get('id')); - - if (!$link) { - throw $this->createNotFoundException('Link not found'); - } - - if (($lock = $this->checkForLock($link, $request->getSession()->getId())) instanceof JsonResponse) { - return $lock; + try { + $result = $handler($id); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - $link = clone $link; - - $link->setElement(null); - $link->setParent(null); - - $data = $serializer->serialize($link->getObjectVars(), 'json', []); - $data = json_decode($data, true); - $data['locked'] = $link->isLocked(); - $data['rawHref'] = $link->getRawHref(); - $data['scheduledTasks'] = array_map( - static fn (Task $task) => $task->getObjectVars(), - $link->getScheduledTasks() - ); - - $this->addTranslationsData($link, $data); - $this->minimizeProperties($link, $data); - $this->populateUsersNames($link, $data); - - return $this->preSendDataActions($data, $link); + return $this->preSendDataActions($result->data, $result->link); } /** * @throws Exception */ #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] - public function saveAction(Request $request): JsonResponse + public function saveAction(Request $request, SaveLinkHandler $handler): JsonResponse { - $link = Document\Link::getById((int) $request->request->get('id')); - if (!$link) { - throw $this->createNotFoundException('Link not found'); - } + $result = $handler((int) $request->request->get('id'), LinkPayload::fromRequest($request)); - $result = $this->saveDocument($link, $request); - /** @var Document\Link $link */ - $link = $result[1]; - $treeData = $this->getTreeNodeConfig($link); - - return $this->adminJson([ - 'success' => true, + return $this->adminJson(ApiResponse::ok([ 'data' => [ - 'versionDate' => $link->getModificationDate(), - 'versionCount' => $link->getVersionCount(), + 'versionDate' => $result->link->getModificationDate(), + 'versionCount' => $result->link->getVersionCount(), ], - 'treeData' => $treeData, - ]); - } - - /** - * @param Document\Link $document - */ - protected function setValuesToDocument(Request $request, Document $document): void - { - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data')); - - $path = $data['path']; - - if (!empty($path)) { - $target = null; - if ($data['linktype'] === 'internal' && $data['internalType']) { - $target = Element\Service::getElementByPath($data['internalType'], $path); - if ($target) { - $data['internal'] = $target->getId(); - } - } - - if (!$target) { - if ($target = Document::getByPath($path)) { - $data['internalType'] = 'document'; - $data['internal'] = $target->getId(); - } elseif ($target = Asset::getByPath($path)) { - $data['internalType'] = 'asset'; - $data['internal'] = $target->getId(); - } elseif ($target = Concrete::getByPath($path)) { - $data['internalType'] = 'object'; - $data['internal'] = $target->getId(); - } else { - $data['linktype'] = 'direct'; - $data['internalType'] = null; - $data['internal'] = null; - $data['direct'] = $path; - } - - if ($target) { - $data['linktype'] = 'internal'; - $data['direct'] = ''; - } - } - } else { - // clear content of link - $data['linktype'] = 'internal'; - $data['direct'] = ''; - $data['internalType'] = null; - $data['internal'] = null; - } - - unset($data['path']); - - $document->setValues($data); - } - - $this->addPropertiesToDocument($request, $document); - $this->applySchedulerDataToElement($request, $document, $this->getAdminUser()); + 'treeData' => $result->treeData, + ])); } } diff --git a/src/Controller/Admin/Document/PageController.php b/src/Controller/Admin/Document/PageController.php index fc8a6c05..89c2ad25 100644 --- a/src/Controller/Admin/Document/PageController.php +++ b/src/Controller/Admin/Document/PageController.php @@ -1,5 +1,4 @@ query->get('id')); - - if (!$page) { - throw $this->createNotFoundException('Page not found'); - } - - if (($lock = $this->checkForLock($page, $request->getSession()->getId())) instanceof JsonResponse) { - return $lock; + try { + $result = $handler($id); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - $page = clone $page; - $draftVersion = null; - $page = $this->getLatestVersion($page, $draftVersion); - - $pageVersions = Element\Service::getSafeVersionInfo($page->getVersions()); - $page->setVersions(array_splice($pageVersions, -1, 1)); - $page->setParent(null); - - // unset useless data - $page->setEditables(null); - $page->setChildren(null); - - $data = $page->getObjectVars(); - $data['locked'] = $page->isLocked(); - - $this->addTranslationsData($page, $data); - $this->minimizeProperties($page, $data); - $this->populateUsersNames($page, $data); - - if ($page->getContentMainDocument()) { - $data['contentMainDocumentPath'] = $page->getContentMainDocument()->getRealFullPath(); - } - - if ($page->getStaticGeneratorEnabled()) { - $data['staticLastGenerated'] = $staticPageGenerator->getLastModified($page); - } - - $data['url'] = $page->getUrl(); - $data['scheduledTasks'] = array_map( - static fn (Task $task) => $task->getObjectVars(), - $page->getScheduledTasks() - ); - - return $this->preSendDataActions($data, $page, $draftVersion); + return $this->preSendDataActions($result->data, $result->page); } - /** - * @throws Exception - */ #[Route('/save', name: 'save', methods: ['PUT', 'POST'])] - public function saveAction(Request $request, StaticPageGenerator $staticPageGenerator): JsonResponse + public function saveAction(Request $request, StaticPageGenerator $staticPageGenerator, SavePageHandler $handler): JsonResponse { - $oldPage = Document\Page::getById((int) $request->request->get('id')); - if (!$oldPage) { - throw $this->createNotFoundException('Page not found'); + try { + $result = $handler((int) $request->request->get('id'), PagePayload::fromRequest($request)); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - /** @var Document\Page|null $pageSession */ - $pageSession = $this->getFromSession($oldPage, $request->getSession()); - - $page = $pageSession ?: $this->getLatestVersion($oldPage); - - if ($request->request->has('missingRequiredEditable')) { - $page->setMissingRequiredEditable($request->request->get('missingRequiredEditable') === 'true'); - } - - if ($request->request->has('settings')) { - $settings = $this->decodeJson($request->request->get('settings')); - if ($settings['published'] ?? false) { - $page->setMissingRequiredEditable(null); - } - } - - [$task, $page, $version] = $this->saveDocument($page, $request); - $arguments = [ - 'oldPage' => $oldPage, - 'task' => $task, - ]; - $documentEvent = new DocumentEvent($page, $arguments); - $this->dispatchEvent($documentEvent, DocumentEvents::PAGE_POST_SAVE_ACTION); - if ($task === self::TASK_PUBLISH || $task === self::TASK_UNPUBLISH) { - $treeData = $this->getTreeNodeConfig($page); - + if ($result->task === self::TASK_PUBLISH || $result->task === self::TASK_UNPUBLISH) { $data = [ - 'versionDate' => $page->getModificationDate(), - 'versionCount' => $page->getVersionCount(), + 'versionDate' => $result->page->getModificationDate(), + 'versionCount' => $result->page->getVersionCount(), ]; - - if ($staticGeneratorEnabled = $page->getStaticGeneratorEnabled()) { + if ($staticGeneratorEnabled = $result->page->getStaticGeneratorEnabled()) { $data['staticGeneratorEnabled'] = $staticGeneratorEnabled; - $data['staticLastGenerated'] = $staticPageGenerator->getLastModified($page); + $data['staticLastGenerated'] = $staticPageGenerator->getLastModified($result->page); } - return $this->adminJson([ - 'success' => true, - 'treeData' => $treeData, - 'data' => $data, - ]); + return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData, 'data' => $data])); } - $this->saveToSession($page, $request->getSession()); + $draftData = []; - if ($version) { + if ($result->version) { $draftData = [ - 'id' => $version->getId(), - 'modificationDate' => $version->getDate(), - 'isAutoSave' => $version->isAutoSave(), + 'id' => $result->version->getId(), + 'modificationDate' => $result->version->getDate(), + 'isAutoSave' => $result->version->isAutoSave(), ]; } - $treeData = $this->getTreeNodeConfig($page); - return $this->adminJson(['success' => true, 'treeData' => $treeData, 'draft' => $draftData]); + return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData, 'draft' => $draftData])); } #[Route('/generate-previews', name: 'generatepreviews', methods: ['GET'])] - public function generatePreviewsAction(Request $request, MessageBusInterface $messengerBusOpendxpCore): JsonResponse + public function generatePreviewsAction(GeneratePagePreviewsHandler $handler): JsonResponse { - $list = new Document\Listing(); - $list->setCondition('`type` = ?', ['page']); + $handler(); - // @todo: this seems completely wrong. - foreach ($list->loadIdList() as $docId) { - $messengerBusOpendxpCore->dispatch( - new GeneratePagePreviewMessage($docId, \OpenDxp\Tool::getHostUrl()) - ); - - break; - } - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/display-preview-image', name: 'display_preview_image', methods: ['GET'])] - public function displayPreviewImageAction(Request $request): BinaryFileResponse + public function displayPreviewImageAction( + GetPagePreviewImagePathHandler $handler, + #[MapQueryParameter] int $id = 0, + ): BinaryFileResponse { - $document = Document\Page::getById((int) $request->query->get('id')); - if ($document instanceof Document\Page) { - return new BinaryFileResponse($document->getPreviewImageFilesystemPath(), 200, [ - 'Content-Type' => 'image/jpg', - ]); - } + $filePath = $handler($id); - throw $this->createNotFoundException('Page not found'); + return new BinaryFileResponse($filePath, 200, [ + 'Content-Type' => 'image/jpg', + ]); } #[Route('/check-pretty-url', name: 'checkprettyurl', methods: ['POST'])] - public function checkPrettyUrlAction(Request $request): JsonResponse + public function checkPrettyUrlAction(Request $request, CheckPrettyUrlHandler $handler): JsonResponse { $docId = $request->request->getInt('id'); $path = trim($request->request->get('path', '')); - $success = true; - - if ($path === '') { - return $this->adminJson([ - 'success' => $success, - ]); - } - - $message = []; - $path = rtrim($path, '/'); - - // must start with / - if ($path !== '' && !str_starts_with($path, '/')) { - $success = false; - $message[] = 'URL must start with /.'; - } - - if (strlen($path) < 2) { - $success = false; - $message[] = 'URL must be at least 2 characters long.'; - } - - if (!Element\Service::isValidPath($path, 'document')) { - $success = false; - $message[] = 'URL is invalid.'; - } - - $list = new Document\Listing(); - $list->setCondition('(CONCAT(`path`, `key`) = ? OR id IN (SELECT id from documents_page WHERE prettyUrl = ?)) - AND id != ?', [ - $path, $path, $docId, - ]); + $result = $handler($docId, $path); - if ($list->getTotalCount() > 0) { - $checkDocument = Document::getById($docId); - $checkSite = Frontend::getSiteForDocument($checkDocument); - $checkSiteId = empty($checkSite) ? 0 : $checkSite->getId(); - - foreach ($list as $document) { - if (empty($document)) { - continue; - } - - $site = Frontend::getSiteForDocument($document); - $siteId = empty($site) ? 0 : $site->getId(); - - if ($siteId === $checkSiteId) { - $success = false; - $message[] = 'URL path already exists.'; - - break; - } - } - } - - return $this->adminJson([ - 'success' => $success, - 'message' => implode('
', $message), - ]); + return $this->adminJson(ApiResponse::fromBool($result->success, ['message' => implode('
', $result->messages)])); } #[Route('/clear-editable-data', name: 'cleareditabledata', methods: ['PUT'])] - public function clearEditableDataAction(Request $request): JsonResponse + public function clearEditableDataAction(Request $request, ResetEditablesSessionHandler $handler): JsonResponse { - $docId = $request->request->getInt('id'); - $doc = Document\PageSnippet::getById($docId); - - if (!$doc) { - throw $this->createNotFoundException('Document not found'); - } - - foreach ($doc->getEditables() as $editable) { - // remove all but target group data - // Hardcoded the TARGET_GROUP_EDITABLE_PREFIX prefix here as we shouldn't remove the bundle specific editables even if bundle is not enabled/installed - if (!preg_match('/^' . preg_quote('persona_ -', '/') . '/', $editable->getName())) { - $doc->removeEditable($editable->getName()); - } - } - - $this->saveToSession($doc, $request->getSession(), true); + $handler($request->request->getInt('id')); - return $this->adminJson([ - 'success' => true, - ]); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ #[Route('/qr-code', name: 'qrcode', methods: ['GET'])] - public function qrCodeAction(Request $request): BinaryFileResponse + public function qrCodeAction( + GenerateQrCodeHandler $handler, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $download = null, + ): BinaryFileResponse { - $page = Document\Page::getById((int) $request->query->get('id')); - - if (!$page) { - throw $this->createNotFoundException('Page not found'); - } - - $url = $page->getUrl(); - - $result = Builder::create() - ->writer(new PngWriter()) - ->data($url) - ->size($request->query->get('download') ? 4000 : 500) - ->build(); - - $tmpFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/qr-code-' . uniqid('', false) . '.png'; - $result->saveToFile($tmpFile); + $tmpFile = $handler($id, (bool) $download); $response = new BinaryFileResponse($tmpFile); $response->headers->set('Content-Type', 'image/png'); - if ($request->query->get('download')) { + if ($download) { $response->setContentDisposition('attachment', 'qrcode-preview.png'); } @@ -328,60 +157,26 @@ public function qrCodeAction(Request $request): BinaryFileResponse } /** - * @throws NotFoundHttpException|Exception + * @throws NotFoundHttpException */ #[Route('/areabrick-render-index-editmode', name: 'areabrick-render-index-editmode', methods: ['POST'])] public function areabrickRenderIndexEditmode( Request $request, - BlockStateStack $blockStateStack, - EditmodeEditableDefinitionCollector $definitionCollector, - Environment $twig, - EditableRenderer $editableRenderer, + RenderAreabrickIndexEditmodeHandler $renderAreabrickIndexEditmode, DocumentResolver $documentResolver, - LocaleServiceInterface $localeService + Environment $twig, ): JsonResponse { - $blockStateStackData = json_decode($request->request->get('blockStateStack'), true); - $blockStateStack->loadArray($blockStateStackData); - - $document = Document\PageSnippet::getById((int) $request->request->get('documentId')); - if (!$document) { - throw $this->createNotFoundException(); - } - - $document = clone $document; - $document->setEditables([]); - $documentResolver->setDocument($request, $document); - - $twig->addGlobal('document', $document); - $twig->addGlobal('editmode', true); - - // we can't use EditmodeResolver::setForceEditmode() here, because it would also render included documents in editmode - // so we use the attribute as a workaround $request->attributes->set(EditmodeResolver::ATTRIBUTE_EDITMODE, true); - // setting locale manually here before rendering, to make sure editables use the right locale from document - $localeService->setLocale($document->getProperty('language')); + $result = $renderAreabrickIndexEditmode(RenderAreabrickIndexEditmodePayload::fromRequest($request)); - $areaBlockConfig = json_decode($request->request->get('areablockConfig'), true); - /** @var Document\Editable\Areablock $areablock */ - $areablock = $editableRenderer->getEditable($document, 'areablock', $request->request->get('realName'), $areaBlockConfig, true); - $areablock->setRealName($request->request->get('realName')); - $areablock->setEditmode(true); - $areaBrickData = json_decode($request->request->get('areablockData'), true); - $areablock->setDataFromEditmode($areaBrickData); - $htmlCode = trim($areablock->renderIndex((int) $request->request->get('index'), true)); + $documentResolver->setDocument($request, $result->document); + $twig->addGlobal('document', $result->document); + $twig->addGlobal('editmode', true); return new JsonResponse([ - 'editableDefinitions' => $definitionCollector->getDefinitions(), - 'htmlCode' => $htmlCode, + 'editableDefinitions' => $result->editableDefinitions, + 'htmlCode' => $result->htmlCode, ]); } - - protected function setValuesToDocument(Request $request, Document $document): void - { - $this->addSettingsToDocument($request, $document); - $this->addDataToDocument($request, $document); - $this->addPropertiesToDocument($request, $document); - $this->applySchedulerDataToElement($request, $document, $this->getAdminUser()); - } } diff --git a/src/Controller/Admin/Document/RenderletController.php b/src/Controller/Admin/Document/RenderletController.php index 0b59a9fe..a8ca9c4e 100644 --- a/src/Controller/Admin/Document/RenderletController.php +++ b/src/Controller/Admin/Document/RenderletController.php @@ -18,19 +18,11 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\Document; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Document\Editable\EditableHandler; -use OpenDxp\Event\DocumentEvents; -use OpenDxp\Localization\LocaleServiceInterface; -use OpenDxp\Model\Document; -use OpenDxp\Model\Element\ElementInterface; -use OpenDxp\Model\Element\Service; -use OpenDxp\Templating\Renderer\ActionRenderer; -use Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter; -use Symfony\Component\EventDispatcher\GenericEvent; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet\RenderRenderletHandler; +use OpenDxp\Bundle\AdminBundle\Payload\Document\RenderRenderletPayload; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @internal @@ -43,77 +35,10 @@ class RenderletController extends AdminAbstractController #[Route('/document_tag/renderlet', name: 'opendxp_admin_document_renderlet_renderlet', methods: ['GET'])] public function renderletAction( Request $request, - ActionRenderer $actionRenderer, - EditableHandler $editableHandler, - LocaleServiceInterface $localeService, - EventDispatcherInterface $eventDispatcher + RenderRenderletHandler $handler, ): Response { + $result = $handler(RenderRenderletPayload::fromRequest($request)); - $query = $request->query->all(); - $attributes = []; - - // load element to make sure the request is valid - $element = $this->loadElement($request); - - $event = new GenericEvent($this, [ - 'requestParams' => $query, - 'element' => $element, - ]); - - $eventDispatcher->dispatch($event, DocumentEvents::EDITABLE_RENDERLET_PRE_RENDER); - - $controller = $request->query->get('controller'); - - // set document if set in request - if ($documentId = $request->query->get('opendxp_parentDocument')) { - $document = Document\PageSnippet::getById((int) $documentId); - if ($document) { - $attributes = $actionRenderer->addDocumentAttributes($document, $attributes); - unset($attributes[DynamicRouter::CONTENT_TEMPLATE]); - } - } - - // override template if set - if ($template = $request->query->get('template')) { - $attributes[DynamicRouter::CONTENT_TEMPLATE] = $template; - } - - foreach (['controller', 'action', 'module', 'bundle'] as $key) { - if (isset($query[$key])) { - unset($query[$key]); - } - } - - // setting locale manually here before rendering the action to make sure editables use the right locale - if this - // is needed in multiple places, move this to the tag handler instead (see #1834) - if (isset($attributes['_locale'])) { - $localeService->setLocale($attributes['_locale']); - } - - $result = $editableHandler->renderAction($controller, $attributes, $query); - - return new Response($result); - } - - private function loadElement(Request $request): ElementInterface - { - $element = null; - - $id = $request->query->get('id'); - $type = $request->query->get('type'); - - if ($id && $type) { - $element = Service::getElementById($type, (int)$id); - } - - if (!$element instanceof ElementInterface) { - throw $this->createNotFoundException(sprintf('Element with type %s and ID %d was not found', $type ?: 'null', $id ?: 'null')); - } - - if (!$element->isAllowed('view')) { - throw $this->createAccessDeniedException(sprintf('Access to element with type %s and ID %d is not allowed', $type, $id)); - } - - return $element; + return new Response($result->html); } -} +} \ No newline at end of file diff --git a/src/Controller/Admin/Document/SnippetController.php b/src/Controller/Admin/Document/SnippetController.php index a680ffbc..cbf7a60b 100644 --- a/src/Controller/Admin/Document/SnippetController.php +++ b/src/Controller/Admin/Document/SnippetController.php @@ -1,4 +1,5 @@ query->get('id')); - - if (!$snippet) { - throw $this->createNotFoundException('Snippet not found'); - } - - if (($lock = $this->checkForLock($snippet, $request->getSession()->getId())) instanceof JsonResponse) { - return $lock; - } - - $snippet = clone $snippet; - $draftVersion = null; - $snippet = $this->getLatestVersion($snippet, $draftVersion); - - $versions = Element\Service::getSafeVersionInfo($snippet->getVersions()); - $snippet->setVersions(array_splice($versions, -1, 1)); - $snippet->setParent(null); - - // unset useless data - $snippet->setEditables(null); - - $data = $snippet->getObjectVars(); - $data['locked'] = $snippet->isLocked(); - - $this->addTranslationsData($snippet, $data); - $this->minimizeProperties($snippet, $data); - $this->populateUsersNames($snippet, $data); - - $data['url'] = $snippet->getUrl(); - $data['scheduledTasks'] = array_map( - static fn (Task $task) => $task->getObjectVars(), - $snippet->getScheduledTasks() - ); - - if ($snippet->getContentMainDocument()) { - $data['contentMainDocumentPath'] = $snippet->getContentMainDocument()->getRealFullPath(); + try { + $result = $handler($id); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - - return $this->preSendDataActions($data, $snippet, $draftVersion); + return $this->preSendDataActions($result->data, $result->snippet); } /** * @throws Exception */ #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] - public function saveAction(Request $request): JsonResponse + public function saveAction(Request $request, SaveSnippetHandler $handler): JsonResponse { - $snippet = Document\Snippet::getById((int) $request->request->get('id')); - if (!$snippet) { - throw $this->createNotFoundException('Snippet not found'); - } - - /** @var Document\Snippet|null $snippetSession */ - $snippetSession = $this->getFromSession($snippet, $request->getSession()); - - $snippet = $snippetSession ?: $this->getLatestVersion($snippet); - - if ($request->request->has('missingRequiredEditable')) { - $snippet->setMissingRequiredEditable($request->request->get('missingRequiredEditable') === 'true'); + try { + $result = $handler((int) $request->request->get('id'), SnippetPayload::fromRequest($request)); + } catch (ElementLockedException $e) { + return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - [$task, $snippet, $version] = $this->saveDocument($snippet, $request); - - if ($task === self::TASK_PUBLISH || $task === self::TASK_UNPUBLISH) { - $this->saveToSession($snippet, $request->getSession()); - - $treeData = $this->getTreeNodeConfig($snippet); - - return $this->adminJson([ - 'success' => true, + if ($result->task === self::TASK_PUBLISH || $result->task === self::TASK_UNPUBLISH) { + return $this->adminJson(ApiResponse::ok([ 'data' => [ - 'versionDate' => $snippet->getModificationDate(), - 'versionCount' => $snippet->getVersionCount(), + 'versionDate' => $result->snippet->getModificationDate(), + 'versionCount' => $result->snippet->getVersionCount(), ], - 'treeData' => $treeData, - ]); + 'treeData' => $result->treeData, + ])); } - $this->saveToSession($snippet, $request->getSession()); - $draftData = []; - if ($version) { + if ($result->version) { $draftData = [ - 'id' => $version->getId(), - 'modificationDate' => $version->getDate(), - 'isAutoSave' => $version->isAutoSave(), + 'id' => $result->version->getId(), + 'modificationDate' => $result->version->getDate(), + 'isAutoSave' => $result->version->isAutoSave(), ]; } - return $this->adminJson(['success' => true, 'draft' => $draftData]); - } - - protected function setValuesToDocument(Request $request, Document $document): void - { - $this->addSettingsToDocument($request, $document); - $this->addDataToDocument($request, $document); - $this->applySchedulerDataToElement($request, $document, $this->getAdminUser()); - $this->addPropertiesToDocument($request, $document); + return $this->adminJson(ApiResponse::ok(['draft' => $draftData])); } } diff --git a/src/Controller/Admin/ElementController.php b/src/Controller/Admin/ElementController.php index ab67ef2a..71f97b62 100644 --- a/src/Controller/Admin/ElementController.php +++ b/src/Controller/Admin/ElementController.php @@ -16,25 +16,39 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; -use Exception; -use OpenDxp; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\DependencyInjection\OpenDxpAdminExtension; -use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Db; -use OpenDxp\Event\Model\ResolveElementEvent; -use OpenDxp\Logger; -use OpenDxp\Model; -use OpenDxp\Model\Asset; -use OpenDxp\Model\DataObject; -use OpenDxp\Model\Document; -use OpenDxp\Model\Element; -use OpenDxp\Model\Version; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Element\AddNoteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\AnalyzePermissionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteAllVersionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteDraftHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteNoteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\FindUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNicePathHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNoteListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetPredefinedPropertiesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetReplaceAssignmentsBatchJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetRequiredByDependenciesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetRequiresDependenciesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetSubtypeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetVersionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\LockElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\ReplaceAssignmentsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\TypePathHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElementsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockPropagateHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\VersionUpdateHandler; +use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal @@ -42,75 +56,40 @@ class ElementController extends AdminAbstractController { #[Route('/element/lock-element', name: 'opendxp_admin_element_lockelement', methods: ['PUT'])] - public function lockElementAction(Request $request): Response + public function lockElementAction(Request $request, LockElementHandler $lockElement): Response { - Element\Editlock::lock($request->request->getInt('id'), $request->request->get('type'), $request->getSession()->getId()); + $lockElement($request->request->getInt('id'), $request->request->get('type'), $request->getSession()->getId()); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/element/unlock-element', name: 'opendxp_admin_element_unlockelement', methods: ['PUT'])] - public function unlockElementAction(Request $request): Response + public function unlockElementAction(Request $request, UnlockElementHandler $unlockElement): Response { - Element\Editlock::unlock((int)$request->request->get('id'), $request->request->get('type')); + $unlockElement((int) $request->request->get('id'), $request->request->get('type')); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/element/unlock-elements', name: 'opendxp_admin_element_unlockelements', methods: ['POST'])] - public function unlockElementsAction(Request $request): Response + public function unlockElementsAction(Request $request, UnlockElementsHandler $unlockElements): Response { - $request = json_decode($request->getContent(), true) ?? []; - foreach ($request['elements'] as $elementIdentifierData) { - Element\Editlock::unlock((int)$elementIdentifierData['id'], $elementIdentifierData['type']); - } + $body = json_decode($request->getContent(), true) ?? []; + $unlockElements($body['elements'] ?? []); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } - /** - * Returns the element data denoted by the given type and ID or path. - */ #[Route('/element/get-subtype', name: 'opendxp_admin_element_getsubtype', methods: ['GET'])] - public function getSubtypeAction(Request $request): JsonResponse + public function getSubtypeAction( + GetSubtypeHandler $getSubtype, + #[MapQueryParameter] string $id = '', + #[MapQueryParameter] ?string $type = null, + ): JsonResponse { - $idOrPath = trim($request->query->get('id', '')); - $type = $request->query->get('type'); - - $event = new ResolveElementEvent($type, $idOrPath); - OpenDxp::getEventDispatcher()->dispatch($event, AdminEvents::RESOLVE_ELEMENT); - $idOrPath = $event->getId(); - $type = $event->getType(); - - if (is_numeric($idOrPath)) { - $el = Element\Service::getElementById($type, (int) $idOrPath); - } elseif ($type === 'document') { - $el = Document\Service::getByUrl($idOrPath); - } else { - $el = Element\Service::getElementByPath($type, $idOrPath); - } - - if ($el) { - $subtype = null; - if ($el instanceof Asset || $el instanceof Document) { - $subtype = $el->getType(); - } elseif ($el instanceof DataObject\Concrete) { - $subtype = $el->getClassName(); - } elseif ($el instanceof DataObject\Folder) { - $subtype = 'folder'; - } - - return $this->adminJson([ - 'subtype' => $subtype, - 'id' => $el->getId(), - 'type' => $type, - 'success' => true, - ]); - } + $result = ($getSubtype)($id, $type); - return $this->adminJson([ - 'success' => false, - ]); + return $this->adminJson(ApiResponse::ok(['subtype' => $result->subtype, 'id' => $result->id, 'type' => $result->type])); } protected function processNoteTypesFromParameters(string $parameterName): JsonResponse @@ -127,9 +106,11 @@ protected function processNoteTypesFromParameters(string $parameterName): JsonRe } #[Route('/element/note-types', name: 'opendxp_admin_element_notetypes', methods: ['GET'])] - public function noteTypes(Request $request): JsonResponse + public function noteTypes( + #[MapQueryParameter] ?string $ctype = null, + ): JsonResponse { - return match ($request->query->get('ctype')) { + return match ($ctype) { 'document' => $this->processNoteTypesFromParameters(OpenDxpAdminExtension::PARAM_DOCUMENTS_NOTES_EVENTS_TYPES), 'asset' => $this->processNoteTypesFromParameters(OpenDxpAdminExtension::PARAM_ASSETS_NOTES_EVENTS_TYPES), 'object' => $this->processNoteTypesFromParameters(OpenDxpAdminExtension::PARAM_DATAOBJECTS_NOTES_EVENTS_TYPES), @@ -138,734 +119,263 @@ public function noteTypes(Request $request): JsonResponse } #[Route('/element/note-list', name: 'opendxp_admin_element_notelist', methods: ['POST'])] - public function noteListAction(Request $request): JsonResponse + #[IsGranted(CorePermission::NotesEvents->value)] + public function noteListAction( + Request $request, + GetNoteListHandler $getNoteList, + DeleteNoteHandler $deleteNote, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { - $this->checkPermission('notes_events'); - if ($request->query->get('xaction') === 'destroy') { + if ($xaction === 'destroy') { $data = $this->decodeJson($request->request->get('data')); - $success = false; - if (($note = Element\Note::getById($data['id'])) && !$note->getLocked()) { - $note->delete(); - $success = true; - } - - return $this->adminJson(['success' => $success]); - } - - $list = new Element\Note\Listing(); + $success = $deleteNote((int) $data['id']); - $offset = (int) $request->request->get('start', 0); - $limit = $request->request->get('limit'); - $limit = $limit ? (int) $limit : null; - - $list->setLimit($limit); - $list->setOffset($offset); - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->request->all()); - if ($sortingSettings['orderKey'] && $sortingSettings['order']) { - $list->setOrderKey($sortingSettings['orderKey']); - $list->setOrder($sortingSettings['order']); - } else { - $list->setOrderKey(['date', 'id']); - $list->setOrder(['DESC', 'DESC']); - } - - $conditions = []; - $filterText = $request->request->get('filterText'); - - if ($filterText) { - $conditions[] = '(' - . '`title` LIKE ' . $list->quote('%'. $filterText .'%') - . ' OR `description` LIKE ' . $list->quote('%'.$filterText.'%') - . ' OR `type` LIKE ' . $list->quote('%'.$filterText.'%') - . ' OR `user` IN (SELECT `id` FROM `users` WHERE `name` LIKE ' . $list->quote('%'.$filterText.'%') . ')' - . " OR DATE_FORMAT(FROM_UNIXTIME(`date`), '%Y-%m-%d') LIKE " . $list->quote('%'.$filterText.'%') - . ')'; + return $this->adminJson(ApiResponse::fromBool($success)); } - $filterJson = $request->request->get('filter'); - if ($filterJson) { - $db = Db::get(); - $filters = $this->decodeJson($filterJson); - $propertyKey = 'property'; - $comparisonKey = 'operator'; - - foreach ($filters as $filter) { - $operator = '='; - - if ($filter['type'] === 'string') { - $operator = 'LIKE'; - } elseif ($filter['type'] === 'numeric') { - if ($filter[$comparisonKey] === 'lt') { - $operator = '<'; - } elseif ($filter[$comparisonKey] === 'gt') { - $operator = '>'; - } elseif ($filter[$comparisonKey] === 'eq') { - $operator = '='; - } - } elseif ($filter['type'] === 'date') { - if ($filter[$comparisonKey] === 'lt') { - $operator = '<'; - } elseif ($filter[$comparisonKey] === 'gt') { - $operator = '>'; - } elseif ($filter[$comparisonKey] === 'eq') { - $operator = '='; - } - $filter['value'] = strtotime($filter['value']); - } elseif ($filter[$comparisonKey] === 'list') { - $operator = '='; - } elseif ($filter[$comparisonKey] === 'boolean') { - $operator = '='; - $filter['value'] = (int) $filter['value']; - } - // system field - $value = ($filter['value']??''); - if ($operator === 'LIKE') { - $value = '%' . $value . '%'; - } - - if ($filter[$propertyKey] === 'user') { - $conditions[] = '`user` IN (SELECT `id` FROM `users` WHERE `name` LIKE ' . $list->quote($value) . ')'; - } elseif ($filter['type'] === 'date' && $filter[$comparisonKey] === 'eq') { - $maxTime = $value + (86400 - 1); - //specifies the top point of the range used in the condition - $dateCondition = '`' . $filter[$propertyKey] . '` ' . ' BETWEEN ' . $db->quote($value) . ' AND ' . $db->quote($maxTime); - $conditions[] = $dateCondition; - } else { - $conditions[] = $db->quoteIdentifier($filter[$propertyKey]).' '.$operator.' '.$db->quote($value); - } - } - } - - if ($request->request->has('cid') && $request->request->has('ctype')) { - $conditions[] = '(cid = ' . $list->quote($request->request->get('cid')) . ' AND ctype = ' . $list->quote($request->request->get('ctype')) . ')'; - } - - if ($conditions !== []) { - $condition = implode(' AND ', $conditions); - $list->setCondition($condition); - } - - $list->load(); - - $notes = []; - - foreach ($list->getNotes() as $note) { - $e = Element\Service::getNoteData($note); - $notes[] = $e; - } + $result = ($getNoteList)( + offset: $request->request->getInt('start', 0), + limit: $request->request->getInt('limit') ?: null, + sortingSettings: QueryParams::extractSortingSettings($request->request->all()), + filterText: $request->request->get('filterText'), + filterJson: $request->request->get('filter'), + cid: $request->request->has('cid') ? $request->request->get('cid') : null, + ctype: $request->request->has('ctype') ? $request->request->get('ctype') : null, + ); - return $this->adminJson([ - 'data' => $notes, - 'success' => true, - 'total' => $list->getTotalCount(), - ]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[Route('/element/note-add', name: 'opendxp_admin_element_noteadd', methods: ['POST'])] - public function noteAddAction(Request $request): JsonResponse + #[IsGranted(CorePermission::NotesEvents->value)] + public function noteAddAction(Request $request, AddNoteHandler $addNote): JsonResponse { - $this->checkPermission('notes_events'); - - $note = new Element\Note(); - $note->setCid((int) $request->request->get('cid')); - $note->setCtype($request->request->get('ctype')); - $note->setDate(time()); - $note->setTitle($request->request->get('title')); - $note->setDescription($request->request->get('description')); - $note->setType($request->request->get('type')); - $note->setLocked(false); - $note->save(); - - return $this->adminJson([ - 'success' => true, - ]); + + ($addNote)( + cid: (int) $request->request->get('cid'), + ctype: $request->request->get('ctype'), + title: $request->request->get('title'), + description: $request->request->get('description'), + type: $request->request->get('type'), + ); + + return $this->adminJson(ApiResponse::ok()); } #[Route('/element/find-usages', name: 'opendxp_admin_element_findusages', methods: ['GET'])] - public function findUsagesAction(Request $request): JsonResponse + public function findUsagesAction( + FindUsagesHandler $findUsages, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $id = null, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] ?string $path = null, + #[MapQueryParameter] int $limit = 50, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] ?string $sort = null, + ): JsonResponse { - $element = null; - if ($request->query->get('id')) { - $element = Element\Service::getElementById($request->query->get('type'), $request->query->getInt('id')); - } elseif ($request->query->get('path')) { - $element = Element\Service::getElementByPath($request->query->get('type'), $request->query->get('path')); - } - - $results = []; - $success = false; - $hasHidden = false; - $total = 0; - $limit = (int)$request->query->get('limit', '50'); - $offset = (int)$request->query->get('start', '0'); - - if ($element instanceof Element\ElementInterface) { - $total = $element->getDependencies()->getRequiredByTotalCount(); - - if ($request->query->has('sort')) { - $sort = json_decode($request->query->get('sort'))[0]; - $orderBy = $sort->property; - $orderDirection = $sort->direction; - } else { - $orderBy = null; - $orderDirection = null; - } - - $queryOffset = $offset; - $queryLimit = $limit; - - while (count($results) < min($limit, $total) && $queryOffset < $total) { - $elements = $element->getDependencies() - ->getRequiredByWithPath($queryOffset, $queryLimit, $orderBy, $orderDirection); - - foreach ($elements as $el) { - $item = Element\Service::getElementById($el['type'], (int) $el['id']); - - if ($item instanceof Element\ElementInterface) { - if ($item->isAllowed('list')) { - $results[] = $el; - } else { - $hasHidden = true; - } - } - } - - $queryOffset += count($elements); - $queryLimit = $limit - count($results); - } - - $success = true; - } + $result = ($findUsages)( + id: $id, + type: $type, + path: $path, + limit: $limit, + offset: $start, + sort: $sort, + ); - return $this->adminJson([ - 'data' => $results, - 'total' => $total, - 'hasHidden' => $hasHidden, - 'success' => $success, - ]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total, 'hasHidden' => $result->hasHidden])); } #[Route('/element/get-replace-assignments-batch-jobs', name: 'opendxp_admin_element_getreplaceassignmentsbatchjobs', methods: ['GET'])] - public function getReplaceAssignmentsBatchJobsAction(Request $request): JsonResponse + public function getReplaceAssignmentsBatchJobsAction( + GetReplaceAssignmentsBatchJobsHandler $getReplaceAssignmentsBatchJobs, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $id = null, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] ?string $path = null, + ): JsonResponse { - $element = null; - - if ($request->query->get('id')) { - $element = Element\Service::getElementById($request->query->get('type'), $request->query->getInt('id')); - } elseif ($request->query->get('path')) { - $element = Element\Service::getElementByPath($request->query->get('type'), $request->query->get('path')); - } + $jobs = $getReplaceAssignmentsBatchJobs($id, $type, $path); - if ($element instanceof Element\ElementInterface) { - return $this->adminJson([ - 'success' => true, - 'jobs' => $element->getDependencies()->getRequiredBy(), - ]); - } - - return $this->adminJson(['success' => false], Response::HTTP_NOT_FOUND); + return $this->adminJson(ApiResponse::ok(['jobs' => $jobs])); } #[Route('/element/replace-assignments', name: 'opendxp_admin_element_replaceassignments', methods: ['POST'])] - public function replaceAssignmentsAction(Request $request): JsonResponse + public function replaceAssignmentsAction(Request $request, ReplaceAssignmentsHandler $replaceAssignments): JsonResponse { - $success = false; - $message = ''; - $element = Element\Service::getElementById($request->request->get('type'), $request->request->getInt('id')); - $sourceEl = Element\Service::getElementById($request->request->get('sourceType'), $request->request->getInt('sourceId')); - $targetEl = Element\Service::getElementById($request->request->get('targetType'), $request->request->getInt('targetId')); - - if ($element && $sourceEl && $targetEl - && $request->request->get('sourceType') === $request->request->get('targetType') - && $sourceEl->getType() === $targetEl->getType() - && $element->isAllowed('save') - ) { - $rewriteConfig = [ - $request->request->get('sourceType') => [ - $sourceEl->getId() => $targetEl->getId(), - ], - ]; - - if ($element instanceof Document) { - $element = Document\Service::rewriteIds($element, $rewriteConfig); - } elseif ($element instanceof DataObject\AbstractObject) { - $element = DataObject\Service::rewriteIds($element, $rewriteConfig); - } elseif ($element instanceof Asset) { - $element = Asset\Service::rewriteIds($element, $rewriteConfig); - } - - $element->setUserModification($this->getAdminUser()->getId()); - $element->save(); - - $success = true; - } else { - $message = 'source-type and target-type do not match'; - } + ($replaceAssignments)( + type: $request->request->get('type'), + id: $request->request->getInt('id'), + sourceType: $request->request->get('sourceType'), + sourceId: $request->request->getInt('sourceId'), + targetType: $request->request->get('targetType'), + targetId: $request->request->getInt('targetId'), + ); - return $this->adminJson([ - 'success' => $success, - 'message' => $message, - ]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/element/unlock-propagate', name: 'opendxp_admin_element_unlockpropagate', methods: ['PUT'])] - public function unlockPropagateAction(Request $request): JsonResponse + public function unlockPropagateAction(Request $request, UnlockPropagateHandler $unlockPropagate): JsonResponse { - $success = false; + $success = $unlockPropagate($request->request->get('type'), $request->request->getInt('id')); - $element = Element\Service::getElementById($request->request->get('type'), $request->request->getInt('id')); - if ($element) { - $element->unlockPropagate(); - $success = true; - } - - return $this->adminJson([ - 'success' => $success, - ]); + return $this->adminJson(ApiResponse::fromBool($success)); } #[Route('/element/type-path', name: 'opendxp_admin_element_typepath', methods: ['GET'])] - public function typePathAction(Request $request): JsonResponse + public function typePathAction( + TypePathHandler $typePath, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $type = null, + ): JsonResponse { - $id = $request->query->getInt('id'); - $type = $request->query->get('type'); - $data = []; - - if ($type === 'asset') { - $element = Asset::getById($id); - } elseif ($type === 'document') { - $element = Document::getById($id); - } else { - $element = DataObject::getById($id); - } + $result = ($typePath)($id, $type); - if (!$element) { - $data['success'] = false; + $data = [ + 'index' => $result->index, + 'idPath' => $result->idPath, + 'typePath' => $result->typePath, + 'fullpath' => $result->fullpath, + ]; - return $this->adminJson($data); + if ($result->sortIndexPath !== null) { + $data['sortIndexPath'] = $result->sortIndexPath; } - $typePath = Element\Service::getTypePath($element); - - $data['success'] = true; - $data['index'] = method_exists($element, 'getIndex') ? (int) $element->getIndex() : 0; - $data['idPath'] = Element\Service::getIdPath($element); - $data['typePath'] = $typePath; - $data['fullpath'] = $element->getRealFullPath(); - - if ($type !== 'asset') { - $sortIndexPath = Element\Service::getSortIndexPath($element); - $data['sortIndexPath'] = $sortIndexPath; - } - - return $this->adminJson($data); + return $this->adminJson(ApiResponse::ok($data)); } #[Route('/element/version-update', name: 'opendxp_admin_element_versionupdate', methods: ['PUT'])] - public function versionUpdateAction(Request $request): JsonResponse + public function versionUpdateAction(Request $request, VersionUpdateHandler $versionUpdate): JsonResponse { $data = $this->decodeJson($request->request->get('data')); + ($versionUpdate)($data); - $version = Version::getById($data['id']); - - if ($data['public'] != $version->getPublic() || $data['note'] != $version->getNote()) { - $version->setPublic($data['public']); - $version->setNote($data['note']); - $version->save(); - } - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ #[Route('/element/get-nice-path', name: 'opendxp_admin_element_getnicepath', methods: ['POST'])] - public function getNicePathAction(Request $request): JsonResponse + public function getNicePathAction(Request $request, GetNicePathHandler $getNicePath): JsonResponse { $source = $this->decodeJson($request->request->get('source')); - - if ($source['type'] !== 'object') { - throw new Exception('currently only objects as source elements are supported'); - } - - $result = []; - $id = $source['id']; - $source = DataObject\Concrete::getById($id); $context = $request->request->has('context') ? $this->decodeJson($request->request->get('context')) : []; - - $ownerType = $context['containerType']; - $fieldname = $context['fieldname']; - - $fd = $this->getNicePathFormatterFieldDefinition($source, $context); - $targets = $this->decodeJson($request->request->get('targets')); - $result = $this->convertResultWithPathFormatter($source, $context, $result, $targets); - - if ($request->request->getBoolean('loadEditModeData')) { - $idProperty = $request->request->get('idProperty', 'id'); - $methodName = 'get' . ucfirst($fieldname); - if ($ownerType === 'object' && method_exists($source, $methodName)) { - $data = DataObject\Service::useInheritedValues(true, [$source, $methodName]); - $editModeData = $fd->getDataForEditmode($data, $source); - // Inherited values show as an empty array - if (is_array($editModeData) && $editModeData !== []) { - foreach ($editModeData as $relationObjectAttribute) { - - $relationObjectAttribute['$$nicepath'] = isset($relationObjectAttribute[$idProperty], $result[$relationObjectAttribute[$idProperty]]) - ? $result[$relationObjectAttribute[$idProperty]] - : null; - - $result[$relationObjectAttribute[$idProperty]] = $relationObjectAttribute; - } - } else { - foreach ($result as $resultItemId => $resultItem) { - $result[$resultItemId] = ['$$nicepath' => $resultItem]; - } - } - } else { - Logger::error('Loading edit mode data is not supported for ownertype: ' . $ownerType); - } - } + $result = ($getNicePath)( + source: $source, + context: $context, + targets: $targets, + loadEditModeData: $request->request->getBoolean('loadEditModeData'), + idProperty: $request->request->get('idProperty', 'id'), + ); - return $this->adminJson(['success' => true, 'data' => $result]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } - /** - * @throws Exception - */ #[Route('/element/get-versions', name: 'opendxp_admin_element_getversions', methods: ['GET'])] - public function getVersionsAction(Request $request): JsonResponse + public function getVersionsAction( + GetVersionsHandler $getVersions, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $elementType = null, + ): JsonResponse { - $id = (int)$request->query->get('id'); - $type = $request->query->get('elementType'); - $allowedTypes = ['asset', 'document', 'object']; - - if ($id && in_array($type, $allowedTypes)) { - $element = Model\Element\Service::getElementById($type, $id); - if ($element) { - if ($element->isAllowed('versions')) { - $schedule = $element->getScheduledTasks(); - $schedules = []; - foreach ($schedule as $task) { - if ($task->getActive()) { - $schedules[$task->getVersion()] = $task->getDate(); - } - } - - //only load auto-save versions from current user - $list = new Version\Listing(); - $list->setLoadAutoSave(true); - $list->setCondition('cid = ? AND ctype = ? AND (autoSave=0 OR (autoSave=1 AND userId = ?)) ', [ - $element->getId(), - Element\Service::getElementType($element), - $this->getAdminUser()->getId(), - ]) - ->setOrderKey('date') - ->setOrder('ASC'); - - $versions = $list->load(); - - $versions = Model\Element\Service::getSafeVersionInfo($versions); - $versions = array_reverse($versions); //reverse array to sort by ID DESC - foreach ($versions as &$version) { - $version['scheduled'] = null; - if (array_key_exists($version['id'], $schedules)) { - $version['scheduled'] = $schedules[$version['id']]; - } - } - - return $this->adminJson(['versions' => $versions]); - } - - throw $this->createAccessDeniedException('Permission denied, ' . $type . ' id [' . $id . ']'); - } - - throw $this->createNotFoundException($type . ' with id [' . $id . "] doesn't exist"); - } + $result = ($getVersions)($id, $elementType); - throw $this->createNotFoundException('Element type not found'); + return $this->adminJson(['versions' => $result->versions]); } #[Route('/element/delete-draft', name: 'opendxp_admin_element_deletedraft', methods: ['DELETE'])] - public function deleteDraftAction(Request $request): JsonResponse + public function deleteDraftAction(Request $request, DeleteDraftHandler $deleteDraft): JsonResponse { - $version = Version::getById((int) $request->request->get('id')); + $deleteDraft((int) $request->request->get('id')); - if ($version) { - $version->delete(); - } - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/element/delete-version', name: 'opendxp_admin_element_deleteversion', methods: ['DELETE'])] - public function deleteVersionAction(Request $request): JsonResponse + public function deleteVersionAction(Request $request, DeleteVersionHandler $deleteVersion): JsonResponse { - $version = Model\Version::getById((int) $request->request->get('id')); - $version->delete(); + $deleteVersion((int) $request->request->get('id')); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/element/delete-all-versions', name: 'opendxp_admin_element_deleteallversion', methods: ['DELETE'])] - public function deleteAllVersionAction(Request $request): JsonResponse + public function deleteAllVersionAction(Request $request, DeleteAllVersionsHandler $deleteAllVersions): JsonResponse { - $elementId = $request->request->getInt('id'); - $elementModificationdate = $request->request->get('date'); - $elementType = $request->request->get('type'); - - $versions = new Model\Version\Listing(); - $versions->setCondition('cid = ' . $versions->quote($elementId) . - ' AND date <> ' . $versions->quote($elementModificationdate) . - ' AND ctype = ' . $versions->quote($elementType) + ($deleteAllVersions)( + elementId: $request->request->getInt('id'), + elementModificationdate: $request->request->get('date'), + elementType: $request->request->get('type'), ); - foreach ($versions->load() as $version) { - $version->delete(); - } - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/element/get-requires-dependencies', name: 'opendxp_admin_element_getrequiresdependencies', methods: ['GET'])] - public function getRequiresDependenciesAction(Request $request): JsonResponse + public function getRequiresDependenciesAction( + GetRequiresDependenciesHandler $getRequiresDependencies, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $elementType = null, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] int $limit = 25, + #[MapQueryParameter] ?string $filter = null, + ): JsonResponse { - $id = $request->query->getInt('id'); - $type = $request->query->get('elementType'); - $allowedTypes = ['asset', 'document', 'object']; - $offset = (int) $request->query->get('start', '0'); - $limit = (int) $request->query->get('limit', '25'); - $filterRequires = $request->query->get('filter'); - $value = null; - $elements = null; - - if ($id && in_array($type, $allowedTypes)) { - $element = Model\Element\Service::getElementById($type, $id); - $dependencies = $element->getDependencies(); - - if ($filterRequires) { - $filters = $this->decodeJson($filterRequires); - - foreach ($filters as $filter) { - - if ($filter['type'] === 'string') { - $value = ($filter['value']??''); - } - - $elements = $element->getDependencies()->getFilterRequiresByPath($offset, $limit, $value); - - } - - if (count($elements) > 0) { - $result = Model\Element\Service::getFilterRequiresForFrontend($elements); - $result['total'] = count($result['requires']); - - return $this->adminJson($result); - } - - return $this->adminJson($elements); - - } - - if ($element instanceof Model\Element\ElementInterface) { - $dependenciesResult = Model\Element\Service::getRequiresDependenciesForFrontend($dependencies, $offset, $limit); - - $dependenciesResult['start'] = $offset; - $dependenciesResult['limit'] = $limit; - $dependenciesResult['total'] = $dependencies->getRequiresTotalCount(); - - return $this->adminJson($dependenciesResult); - } - } + $result = ($getRequiresDependencies)( + id: $id, + type: $elementType, + offset: $start, + limit: $limit, + filterJson: $filter, + ); - return $this->adminJson(false); + return $this->adminJson($result->data); } #[Route('/element/get-required-by-dependencies', name: 'opendxp_admin_element_getrequiredbydependencies', methods: ['GET'])] - public function getRequiredByDependenciesAction(Request $request): JsonResponse + public function getRequiredByDependenciesAction( + GetRequiredByDependenciesHandler $getRequiredByDependencies, + #[MapQueryParameter] int $id = 0, + #[MapQueryParameter] ?string $elementType = null, + #[MapQueryParameter] int $start = 0, + #[MapQueryParameter] int $limit = 25, + #[MapQueryParameter] ?string $filter = null, + ): JsonResponse { - $id = $request->query->getInt('id'); - $type = $request->query->get('elementType'); - $allowedTypes = ['asset', 'document', 'object']; - $offset = (int) $request->query->get('start', '0'); - $limit = (int) $request->query->get('limit', '25'); - $filterRequiredBy = $request->query->get('filter'); - $value = null; - $elements = null; - - if ($id && in_array($type, $allowedTypes)) { - $element = Model\Element\Service::getElementById($type, $id); - $dependencies = $element->getDependencies(); - - if ($filterRequiredBy) { - $filters = $this->decodeJson($filterRequiredBy); - - foreach ($filters as $filter) { - - if ($filter['type'] === 'string') { - $value = ($filter['value']??''); - } - - $elements = $element->getDependencies()->getFilterRequiredByPath($offset, $limit, $value); - - } - - $result = [ - 'start' => $offset, - 'limit' => $limit, - 'requiredBy' => [], // Initialize 'requiredBy' as an empty array - ]; - - if (count($elements) > 0) { - $result = Model\Element\Service::getFilterRequiredByPathForFrontend($elements); - $result['total'] = count($result['requiredBy']); - - return $this->adminJson($result); - } - - return $this->adminJson($elements); - - } - - if ($element instanceof Model\Element\ElementInterface) { - $dependenciesResult = Model\Element\Service::getRequiredByDependenciesForFrontend($dependencies, $offset, $limit); - - $dependenciesResult['start'] = $offset; - $dependenciesResult['limit'] = $limit; - $dependenciesResult['total'] = $dependencies->getRequiredByTotalCount(); - - return $this->adminJson($dependenciesResult); - } - } + $result = ($getRequiredByDependencies)( + id: $id, + type: $elementType, + offset: $start, + limit: $limit, + filterJson: $filter, + ); - return $this->adminJson(false); + return $this->adminJson($result->data); } #[Route('/element/get-predefined-properties', name: 'opendxp_admin_element_getpredefinedproperties', methods: ['GET'])] - public function getPredefinedPropertiesAction(Request $request, TranslatorInterface $translator): JsonResponse + public function getPredefinedPropertiesAction( + GetPredefinedPropertiesHandler $getPredefinedProperties, + #[MapQueryParameter] ?string $elementType = null, + #[MapQueryParameter] ?string $query = null, + ): JsonResponse { - $properties = []; - $type = $request->query->get('elementType'); - $query = $request->query->get('query'); - $allowedTypes = ['asset', 'document', 'object']; - - if (in_array($type, $allowedTypes, true)) { - $list = new Model\Property\Predefined\Listing(); - $list->setFilter(function (Model\Property\Predefined $predefined) use ($type, $query, $translator) { - if (!str_contains($predefined->getCtype(), $type)) { - return false; - } - - return !($query && stripos($translator->trans($predefined->getName(), [], 'admin'), (string) $query) === false); - }); - - foreach ($list->getProperties() as $type) { - $properties[] = $type->getObjectVars(); - } - } + $result = ($getPredefinedProperties)($elementType, $query); - return $this->adminJson(['properties' => $properties]); + return $this->adminJson(['properties' => $result->properties]); } #[Route('/element/analyze-permissions', name: 'opendxp_admin_element_analyzepermissions', methods: ['POST'])] - public function analyzePermissionsAction(Request $request): Response + public function analyzePermissionsAction(Request $request, AnalyzePermissionsHandler $analyzePermissions): Response { - $userId = $request->request->getInt('userId'); - if ($userId) { - $userList = []; - if ($user = Model\User::getById($userId)) { - $userList[] = $user; - } - } else { - $userList = new Model\User\Listing(); - $userList->setCondition('`type` = ?', ['user']); - $userList = $userList->load(); - } - - $elementType = $request->request->get('elementType'); - $elementId = $request->request->getInt('elementId'); - - $element = Element\Service::getElementById($elementType, $elementId); - - $result = Element\PermissionChecker::check($element, $userList); - - return $this->adminJson( - [ - 'data' => $result, - 'success' => true, - ] + $result = ($analyzePermissions)( + userId: $request->request->getInt('userId') ?: null, + elementType: $request->request->get('elementType'), + elementId: $request->request->getInt('elementId'), ); - } - - /** - * @throws Exception - */ - protected function getNicePathFormatterFieldDefinition(DataObject\Concrete $source, array $context): DataObject\ClassDefinition\Data|bool|null - { - $ownerType = $context['containerType']; - $fieldname = $context['fieldname']; - $fd = null; - - if ($ownerType === 'object') { - $subContainerType = $context['subContainerType'] ?? null; - if ($subContainerType) { - $subContainerKey = $context['subContainerKey']; - $subContainer = $source->getClass()->getFieldDefinition($subContainerKey); - if (method_exists($subContainer, 'getFieldDefinition')) { - $fd = $subContainer->getFieldDefinition($fieldname); - } - } else { - $fd = $source->getClass()->getFieldDefinition($fieldname); - } - } elseif ($ownerType === 'localizedfield') { - $localizedfields = $source->getClass()->getFieldDefinition('localizedfields'); - if ($localizedfields instanceof DataObject\ClassDefinition\Data\Localizedfields) { - $fd = $localizedfields->getFieldDefinition($fieldname); - } - } elseif ($ownerType === 'objectbrick') { - $fdBrick = DataObject\Objectbrick\Definition::getByKey($context['containerKey']); - $fd = $fdBrick->getFieldDefinition($fieldname); - } elseif ($ownerType === 'fieldcollection') { - $containerKey = $context['containerKey']; - $fdCollection = DataObject\Fieldcollection\Definition::getByKey($containerKey); - if (($context['subContainerType'] ?? null) === 'localizedfield') { - /** @var DataObject\ClassDefinition\Data\Localizedfields $fdLocalizedFields */ - $fdLocalizedFields = $fdCollection->getFieldDefinition('localizedfields'); - $fd = $fdLocalizedFields->getFieldDefinition($fieldname); - } else { - $fd = $fdCollection->getFieldDefinition($fieldname); - } - } - - return $fd; - } - - /** - * @throws Exception - */ - protected function convertResultWithPathFormatter(DataObject\Concrete $source, array $context, array $result, array $targets): array - { - $fd = $this->getNicePathFormatterFieldDefinition($source, $context); - - if ($fd instanceof DataObject\ClassDefinition\PathFormatterAwareInterface) { - $formatter = $fd->getPathFormatterClass(); - - if (null !== $formatter) { - $pathFormatter = DataObject\ClassDefinition\Helper\PathFormatterResolver::resolvePathFormatter( - $fd->getPathFormatterClass() - ); - - if ($pathFormatter instanceof DataObject\ClassDefinition\PathFormatterInterface) { - $result = $pathFormatter->formatPath($result, $source, $targets, [ - 'fd' => $fd, - 'context' => $context, - ]); - } - } - } - return $result; + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } } diff --git a/src/Controller/Admin/ElementControllerBase.php b/src/Controller/Admin/ElementControllerBase.php index 08e4e5cb..de46e8b1 100644 --- a/src/Controller/Admin/ElementControllerBase.php +++ b/src/Controller/Admin/ElementControllerBase.php @@ -16,25 +16,16 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; -use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Event\AssetEvents; -use OpenDxp\Bundle\AdminBundle\Event\Model\AssetDeleteInfoEvent; -use OpenDxp\Bundle\AdminBundle\Event\Model\DataObjectDeleteInfoEvent; -use OpenDxp\Bundle\AdminBundle\Event\Model\DocumentDeleteInfoEvent; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfoHandler; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; -use OpenDxp\Event\DataObjectEvents; -use OpenDxp\Event\DocumentEvents; -use OpenDxp\Logger; -use OpenDxp\Model\Asset; -use OpenDxp\Model\DataObject\AbstractObject; -use OpenDxp\Model\Document; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Model\Element\ElementInterface; use OpenDxp\Model\Element\Service; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal @@ -54,15 +45,15 @@ protected function getTreeNodeConfig(ElementInterface $element): array } #[Route('/tree-get-root', name: 'treegetroot', methods: ['GET'])] - public function treeGetRootAction(Request $request): JsonResponse + public function treeGetRootAction( + #[MapQueryParameter] ?string $elementType = null, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $id = null, + ): JsonResponse { - $type = $request->query->get('elementType'); + $type = $elementType; $allowedTypes = ['asset', 'document', 'object']; - $id = 1; - if ($request->query->get('id')) { - $id = (int)$request->query->get('id'); - } + $id = $id ?? 1; if (in_array($type, $allowedTypes)) { $root = Service::getElementById($type, $id); @@ -70,163 +61,20 @@ public function treeGetRootAction(Request $request): JsonResponse return $this->adminJson($this->getTreeNodeConfig($root)); } - return $this->adminJson(['success' => false, 'id' => $id]); + return $this->adminJson(ApiResponse::error(null, ['id' => $id])); } - return $this->adminJson(['success' => false, 'message' => 'missing_permission']); + return $this->adminJson(ApiResponse::error('missing_permission')); } - /** - * @throws Exception - */ #[Route('/delete-info', name: 'deleteinfo', methods: ['GET'])] - public function deleteInfoAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse + public function deleteInfoAction( + GetDeleteInfoHandler $handler, + Request $request, + #[MapQueryParameter] ?string $id = null, + #[MapQueryParameter] ?string $type = null, + ): JsonResponse { - $hasDependency = false; - $errors = false; - $deleteJobs = []; - $itemResults = []; - - $totalChildren = 0; - - $ids = $request->query->get('id'); - $ids = explode(',', $ids); - $type = $request->query->get('type'); - - foreach ($ids as $id) { - try { - $element = Service::getElementById($type, (int) $id); - if (!$element) { - continue; - } - - if (!$hasDependency) { - $hasDependency = $element->getDependencies()->isRequired(); - } - } catch (Exception) { - Logger::err('failed to access element with id: ' . $id); - - continue; - } - - // check for children - $event = null; - $eventName = null; - - if ($element instanceof Asset) { - $event = new AssetDeleteInfoEvent($element); - $eventName = AssetEvents::DELETE_INFO; - } elseif ($element instanceof Document) { - $event = new DocumentDeleteInfoEvent($element); - $eventName = DocumentEvents::DELETE_INFO; - } elseif ($element instanceof AbstractObject) { - $event = new DataObjectDeleteInfoEvent($element); - $eventName = DataObjectEvents::DELETE_INFO; - } - - if ($element->isLocked()) { - $itemResults[] = [ - 'id' => $element->getId(), - 'type' => $element->getType(), - 'key' => $element->getKey(), - 'reason' => 'Element is locked', - 'allowed' => false, - ]; - $errors |= true; - - continue; - } - - $eventDispatcher->dispatch($event, $eventName); - - if (!$event->getDeletionAllowed()) { - $itemResults[] = [ - 'id' => $element->getId(), - 'type' => $element->getType(), - 'key' => $element->getKey(), - 'reason' => $event->getReason(), - 'allowed' => false, - ]; - $errors |= true; - - continue; - } - - $itemResults[] = [ - 'id' => $element->getId(), - 'type' => $element->getType(), - 'key' => $element->getKey(), - 'path' => $element->getPath(), - 'allowed' => true, - ]; - - $deleteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_recyclebin_add'), - 'method' => 'POST', - 'params' => [ - 'type' => $type, - 'id' => $element->getId(), - ], - ]]; - - $hasChildren = $element->hasChildren(); - if (!$hasDependency) { - $hasDependency = $hasChildren; - } - - if ($hasChildren) { - // get amount of children - $list = $element::getList(['unpublished' => true]); - $pathColumn = 'path'; - $list->setCondition($pathColumn . ' LIKE ?', [$element->getRealFullPath() . '/%']); - $children = $list->getTotalCount(); - $totalChildren += $children; - - if ($children > 0) { - $deleteObjectsPerRequest = 5; - for ($i = 0, $iMax = ceil($children / $deleteObjectsPerRequest); $i < $iMax; $i++) { - $deleteJobs[] = [[ - 'url' => $request->getBaseUrl() . '/admin/' . $type . '/delete', - 'method' => 'DELETE', - 'params' => [ - 'step' => $i, - 'amount' => $deleteObjectsPerRequest, - 'type' => 'children', - 'id' => $element->getId(), - ], - ]]; - } - } - } - - // the element itself is the last one - $deleteJobs[] = [[ - 'url' => $request->getBaseUrl() . '/admin/' . $type . '/delete', - 'method' => 'DELETE', - 'params' => [ - 'id' => $element->getId(), - ], - ]]; - } - - // get the element key in case of just one - $elementKey = false; - if (count($ids) === 1) { - $element = Service::getElementById($type, (int) $ids[0]); - - if ($element instanceof ElementInterface) { - $elementKey = $element->getKey(); - } - } - - return $this->adminJson([ - 'hasDependencies' => $hasDependency, - 'children' => $totalChildren, - 'deletejobs' => $deleteJobs, - 'batchDelete' => count($ids) > 1, - 'elementKey' => $elementKey, - 'errors' => $errors, - 'itemResults' => $itemResults, - ]); + return $this->adminJson($handler($id, $type, $request->getBaseUrl())); } } diff --git a/src/Controller/Admin/EmailController.php b/src/Controller/Admin/EmailController.php index 1fe9b431..c29f4353 100644 --- a/src/Controller/Admin/EmailController.php +++ b/src/Controller/Admin/EmailController.php @@ -1,5 +1,4 @@ getAdminUser()->isAllowed('emails') && !$this->getAdminUser()->isAllowed('gdpr_data_extractor')) { - throw new Exception("Permission denied, user needs 'emails' permission."); - } - - $list = new Tool\Email\Log\Listing(); - if ($request->request->has('documentId')) { - $list->setCondition('documentId = ' . (int)$request->request->get('documentId')); - } - $list->setLimit((int)$request->request->get('limit', 50)); - $list->setOffset((int)$request->request->get('start', 0)); - $list->setOrderKey('sentDate'); - - if ($request->request->has('filter')) { - $filterTerm = $request->request->get('filter'); - if ($filterTerm === '*') { - $filterTerm = ''; - } - - $filterTerm = str_replace('%', '*', $filterTerm); - $filterTerm = htmlspecialchars($filterTerm, ENT_QUOTES); - - if (strpos($filterTerm, '@')) { - $parts = explode(' ', $filterTerm); - $parts = array_map(static function ($part) { - if (strpos($part, '@')) { - return '"' . $part . '"'; - } - - return $part; - }, $parts); - $filterTerm = implode(' ', $parts); - } - - if (str_starts_with($filterTerm, '@')) { - $filterTerm = str_replace('@', '', $filterTerm); - } - - $condition = '( MATCH (`from`,`to`,`cc`,`bcc`,`subject`,`params`) AGAINST (' . $list->quote($filterTerm) . ' IN BOOLEAN MODE) )'; - - if ($request->request->has('documentId')) { - $condition .= 'AND documentId = ' . (int)$request->request->get('documentId'); - } - - $list->setCondition($condition); - } - - $list->setOrder('DESC'); - - $data = $list->load(); - $jsonData = []; - - foreach ($data as $entry) { - $tmp = $entry->getObjectVars(); - unset($tmp['bodyHtml'], $tmp['bodyText']); - $jsonData[] = $tmp; - } - - return $this->adminJson([ - 'data' => $jsonData, - 'success' => true, - 'total' => $list->getTotalCount(), - ]); + $result = $getEmailLogs( + documentId: $request->request->has('documentId') ? (int)$request->request->get('documentId') : null, + limit: (int)$request->request->get('limit', 50), + offset: (int)$request->request->get('start', 0), + filter: $request->request->has('filter') ? $request->request->get('filter') : null, + ); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Emails->value)] #[Route('/show-email-log', name: 'opendxp_admin_email_showemaillog', methods: ['GET'])] - public function showEmailLogAction(Request $request, ?Profiler $profiler): JsonResponse|Response - { + public function showEmailLogAction( + GetEmailLogHandler $getEmailLog, + GetEmailLogParamsHandler $getEmailLogParams, + ?Profiler $profiler, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] int $id = 0, + ): JsonResponse|Response { if ($profiler) { $profiler->disable(); } - if (!$this->getAdminUser()->isAllowed('emails')) { - throw $this->createAccessDeniedHttpException("Permission denied, user needs 'emails' permission."); + if ($type === 'params') { + return $this->adminJson($getEmailLogParams(id: $id)); } - $type = $request->query->get('type'); - $emailLog = Tool\Email\Log::getById((int) $request->query->get('id')); - - if (!$emailLog) { - throw $this->createNotFoundException(); - } + $result = $getEmailLog($id); if ($type === 'text') { - return $this->render('@OpenDxpAdmin/admin/email/text.html.twig', ['log' => $emailLog->getTextLog()]); + return $this->render('@OpenDxpAdmin/admin/email/text.html.twig', ['log' => $result->textLog]); } if ($type === 'html') { - return new Response($emailLog->getHtmlLog(), 200, [ + return new Response($result->htmlLog, 200, [ 'Content-Security-Policy' => "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data:", ]); } - if ($type === 'params') { - try { - $params = $emailLog->getParams(); - } catch (Exception) { - Logger::warning('Could not decode JSON param string'); - $params = []; - } - foreach ($params as &$entry) { - $this->enhanceLoggingData($entry); - } - - return $this->adminJson($params); - } - if ($type === 'details') { - $data = $emailLog->getObjectVars(); - - return $this->adminJson($data); + return $this->adminJson($result->objectVars); } return new Response('No Type specified'); - } - protected function enhanceLoggingData(?array &$data, ?array &$fullEntry = null): void - { - if (!is_array($data)) { - return; - } - - if (!empty($data['objectClass'])) { - $class = '\\' . ltrim($data['objectClass'], '\\'); - $reflection = new ReflectionClass($class); - - if (!empty($data['objectId']) && $reflection->implementsInterface(ElementInterface::class)) { - $obj = $class::getById($data['objectId']); - $data['objectPath'] = is_null($obj) ? '' : $obj->getRealFullPath(); - - //check for classmapping - if (stristr($class, '\\OpenDxp\\Model') === false) { - $niceClassName = '\\' . ltrim($reflection->getParentClass()->getName(), '\\'); - } else { - $niceClassName = $class; - } - - $niceClassName = str_replace(['\\OpenDxp\\Model\\', '_'], ['', '\\'], $niceClassName); - - $tmp = explode('\\', $niceClassName); - if (in_array($tmp[0], ['DataObject', 'Document', 'Asset'])) { - $data['objectClassBase'] = $tmp[0]; - $data['objectClassSubType'] = $tmp[1]; - } - } - } - - foreach ($data as &$value) { - - if (!is_array($value)) { - continue; - } - - $this->enhanceLoggingData($value, $data); - } - - unset($value); - - if ($data['children'] ?? false) { - foreach ($data['children'] as $key => $entry) { - if (is_string($key)) { //key must be integers - unset($data['children'][$key]); - } - } - $data['iconCls'] = 'opendxp_icon_folder'; - $data['data'] = ['type' => 'simple', 'value' => 'Children (' . count($data['children']) . ')']; - } else { - //setting the icon class - if (empty($data['iconCls'])) { - if (($data['objectClassBase'] ?? '') === 'DataObject') { - $fullEntry['iconCls'] = 'opendxp_icon_object'; - } elseif (($data['objectClassBase'] ?? '') === 'Asset') { - $fullEntry['iconCls'] = match ($data['objectClassSubType']) { - 'Image' => 'opendxp_icon_image', - 'Video' => 'opendxp_icon_wmv', - 'Text' => 'opendxp_icon_txt', - 'Document' => 'opendxp_icon_pdf', - default => 'opendxp_icon_asset', - }; - } elseif (str_starts_with($data['objectClass'] ?? '', 'Document')) { - $fullEntry['iconCls'] = 'opendxp_icon_' . strtolower($data['objectClassSubType']); - } else { - $data['iconCls'] = 'opendxp_icon_text'; - } - } - - $data['leaf'] = true; - } - } - - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Emails->value)] #[Route('/delete-email-log', name: 'opendxp_admin_email_deleteemaillog', methods: ['DELETE'])] - public function deleteEmailLogAction(Request $request): JsonResponse + public function deleteEmailLogAction(Request $request, DeleteEmailLogHandler $deleteEmailLog): JsonResponse { - if (!$this->getAdminUser()->isAllowed('emails')) { - throw $this->createAccessDeniedHttpException("Permission denied, user needs 'emails' permission."); - } - - $success = false; - $emailLog = Tool\Email\Log::getById((int) $request->request->get('id')); - if ($emailLog instanceof Tool\Email\Log) { - $emailLog->delete(); - $success = true; - } + $deleteEmailLog(id: (int)$request->request->get('id')); - return $this->adminJson([ - 'success' => $success, - ]); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Emails->value)] #[Route('/resend-email', name: 'opendxp_admin_email_resendemail', methods: ['POST'])] - public function resendEmailAction(Request $request): JsonResponse + public function resendEmailAction(Request $request, ResendEmailHandler $resendEmail): JsonResponse { - if (!$this->getAdminUser()->isAllowed('emails')) { - throw $this->createAccessDeniedHttpException("Permission denied, user needs 'emails' permission."); - } - - $success = false; - $emailLog = Tool\Email\Log::getById((int) $request->request->get('id')); - - if ($emailLog instanceof Tool\Email\Log) { - $mail = new Mail(); - $mail->preventDebugInformationAppending(); - $mail->setIgnoreDebugMode(true); - - if (!empty($request->request->get('to'))) { - $emailLog->setTo(null); - $emailLog->setCc(null); - $emailLog->setBcc(null); - } else { - $mail->disableLogging(); - } - - if ($html = $emailLog->getHtmlLog()) { - $mail->html($html); - } - - if ($text = $emailLog->getTextLog()) { - $mail->text($text); - } - - foreach (['From', 'To', 'Cc', 'Bcc', 'ReplyTo'] as $field) { - if (!$values = $request->request->get(strtolower($field))) { - $getter = 'get' . $field; - $values = $emailLog->{$getter}(); - } - - $values = \OpenDxp\Helper\Mail::parseEmailAddressField($values); - - if ($values) { - [$value] = $values; - $prefix = 'add'; - $mail->{$prefix . $field}(new Address($value['email'], $value['name'])); - } - } - - $mail->subject($emailLog->getSubject()); - - // add document - if ($emailLog->getDocumentId()) { - $mail->setDocument($emailLog->getDocumentId()); - } - - // re-add params - try { - $params = $emailLog->getParams(); - } catch (Exception) { - Logger::warning('Could not decode JSON param string'); - $params = []; - } - - foreach ($params as $entry) { - $data = null; - $hasChildren = isset($entry['children']) && is_array($entry['children']); - - if ($hasChildren) { - $childData = []; - foreach ($entry['children'] as $childParam) { - $childData[$childParam['key']] = $this->parseLoggingParamObject($childParam); - } - $data = $childData; - } else { - $data = $this->parseLoggingParamObject($entry); - } - - $mail->setParam($entry['key'], $data); - } - - $mail->send(); - $success = true; - } - - return $this->adminJson([ - 'success' => $success, - ]); + $resendEmail( + id: (int)$request->request->get('id'), + fieldOverrides: [ + 'from' => $request->request->get('from') ?: null, + 'to' => $request->request->get('to') ?: null, + 'cc' => $request->request->get('cc') ?: null, + 'bcc' => $request->request->get('bcc') ?: null, + 'replyto' => $request->request->get('replyto') ?: null, + ], + ); + + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Emails->value)] #[Route('/send-test-email', name: 'opendxp_admin_email_sendtestemail', methods: ['POST'])] - public function sendTestEmailAction(Request $request): JsonResponse + public function sendTestEmailAction(Request $request, SendTestEmailHandler $sendTestEmail): JsonResponse { - if (!$this->getAdminUser()->isAllowed('emails')) { - throw new Exception("Permission denied, user needs 'emails' permission."); - } - // Simulate a frontend request to prefix assets $request->attributes->set(RequestHelper::ATTRIBUTE_FRONTEND_REQUEST, true); - $mail = new Mail(); - - if ($request->request->get('emailType') === 'text') { - $mail->text(strip_tags($request->request->get('content'))); - } elseif ($request->request->get('emailType') === 'html') { - $mail->html($request->request->get('content')); - } elseif ($request->request->get('emailType') === 'document') { - $doc = \OpenDxp\Model\Document::getByPath($request->request->get('documentPath')); - - if ($doc instanceof \OpenDxp\Model\Document\Email) { - $mail->setDocument($doc); - - if ($request->request->has('mailParamaters') && $mailParamsArray = json_decode($request->request->get('mailParamaters'), true)) { - foreach ($mailParamsArray as $mailParam) { - if ($mailParam['key']) { - $mail->setParam($mailParam['key'], $mailParam['value']); - } - } - } - } else { - throw new Exception('Email document not found!'); - } - } - - if ($from = $request->request->get('from')) { - $addressArray = \OpenDxp\Helper\Mail::parseEmailAddressField($from); - if ($addressArray) { - //use the first address only - [$cleanedFromAddress] = $addressArray; - $mail->from(new Address($cleanedFromAddress['email'], $cleanedFromAddress['name'])); - } + $mailParamsArray = null; + if ($request->request->has('mailParamaters')) { + $mailParamsArray = json_decode($request->request->get('mailParamaters'), true) ?: null; } - $toAddresses = \OpenDxp\Helper\Mail::parseEmailAddressField($request->request->get('to')); - foreach ($toAddresses as $cleanedToAddress) { - $mail->addTo($cleanedToAddress['email'], $cleanedToAddress['name']); - } - - $mail->subject($request->request->get('subject')); - $mail->setIgnoreDebugMode(true); + $sendTestEmail( + emailType: (string)$request->request->get('emailType'), + content: $request->request->get('content'), + documentPath: $request->request->get('documentPath'), + mailParameters: $mailParamsArray, + from: $request->request->get('from'), + to: (string)$request->request->get('to'), + subject: (string)$request->request->get('subject'), + ); - $mail->send(); - - return $this->adminJson([ - 'success' => true, - ]); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Emails->value)] #[Route('/blocklist', name: 'opendxp_admin_email_blocklist', methods: ['POST'])] - public function blocklistAction(Request $request): JsonResponse - { - if (!$this->getAdminUser()->isAllowed('emails')) { - throw new Exception("Permission denied, user needs 'emails' permission."); - } - + public function blocklistAction( + Request $request, + GetBlocklistHandler $getBlocklist, + CreateBlocklistEntryHandler $createBlocklistEntry, + UpdateBlocklistEntryHandler $updateBlocklistEntry, + DeleteBlocklistEntryHandler $deleteBlocklistEntry, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { if ($request->request->has('data')) { $data = $this->decodeJson($request->request->get('data')); @@ -432,83 +173,39 @@ public function blocklistAction(Request $request): JsonResponse $value = trim($value); } } + unset($value); } - if ($request->query->get('xaction') === 'destroy') { - $address = Tool\Email\Blocklist::getByAddress($data['address']); - $address->delete(); + if ($xaction === 'destroy') { + $deleteBlocklistEntry(address: $data['address']); - return $this->adminJson(['success' => true, 'data' => []]); + return $this->adminJson(ApiResponse::ok(['data' => []])); } - if ($request->query->get('xaction') === 'update') { - $address = Tool\Email\Blocklist::getByAddress($data['address']); - $address->setValues($data); - $address->save(); + if ($xaction === 'update') { + $entryData = $updateBlocklistEntry(data: $data); - return $this->adminJson(['data' => $address->getObjectVars(), 'success' => true]); + return $this->adminJson(ApiResponse::ok(['data' => $entryData])); } - if ($request->query->get('xaction') === 'create') { - unset($data['id']); + if ($xaction === 'create') { + $entryData = $createBlocklistEntry(data: $data); - $address = new Tool\Email\Blocklist(); - $address->setValues($data); - $address->save(); - - return $this->adminJson(['data' => $address->getObjectVars(), 'success' => true]); + return $this->adminJson(ApiResponse::ok(['data' => $entryData])); } } else { + $sortingSettings = QueryParams::extractSortingSettings($request->request->all()); - $list = new Tool\Email\Blocklist\Listing(); - - $list->setLimit((int) $request->request->get('limit', 50)); - $list->setOffset((int) $request->request->get('start', 0)); + $result = $getBlocklist( + limit: (int)$request->request->get('limit', 50), + offset: (int)$request->request->get('start', 0), + sortingSettings: $sortingSettings, + filter: $request->request->has('filter') ? $request->request->get('filter') : null, + ); - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->request->all()); - - if ($sortingSettings['orderKey']) { - $list->setOrderKey($sortingSettings['orderKey']); - $list->setOrder($sortingSettings['order']); - } - - if ($request->request->has('filter')) { - $list->setCondition('`address` LIKE ' . $list->quote('%'.$request->request->get('filter').'%')); - } - - $data = $list->load(); - $jsonData = []; - foreach ($data as $entry) { - $jsonData[] = $entry->getObjectVars(); - } - - return $this->adminJson([ - 'success' => true, - 'data' => $jsonData, - 'total' => $list->getTotalCount(), - ]); - } - - return $this->adminJson(['success' => false]); - } - - protected function parseLoggingParamObject(array $params): mixed - { - $data = null; - if ($params['data']['type'] === 'object') { - $class = '\\' . ltrim($params['data']['objectClass'], '\\'); - $reflection = new ReflectionClass($class); - - if (!empty($params['data']['objectId']) && $reflection->implementsInterface(ElementInterface::class)) { - $obj = $class::getById($params['data']['objectId']); - if (!is_null($obj)) { - $data = $obj; - } - } - } else { - $data = $params['data']['value']; + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - return $data; + throw new BadRequestHttpException(); } } diff --git a/src/Controller/Admin/IndexController.php b/src/Controller/Admin/IndexController.php index ca2b7ccd..1fd9590d 100644 --- a/src/Controller/Admin/IndexController.php +++ b/src/Controller/Admin/IndexController.php @@ -1,5 +1,4 @@ getAdminUser(); - $perspectiveConfig = new \OpenDxp\Bundle\AdminBundle\Perspective\Config(); - $templateParams = [ - 'config' => $config, - 'systemSettings' => SystemSettingsConfig::get(), - 'adminSettings' => AdminConfig::get(), - 'perspectiveConfig' => $perspectiveConfig, - ]; - - $this - ->setAdminLanguage($request, $user) - ->addRuntimePerspective($templateParams, $user) - ->addPluginAssets($bundleManager, $templateParams); - $this->buildOpenDxpSettings( - $request, - $templateParams, - $user, - $kernel, - $maintenanceExecutor, - $csrfProtection, - $maintenanceModeHelper - ); + $request->setLocale($user->getLanguage()); + if ($translator instanceof LocaleAwareInterface) { + $translator->setLocale($user->getLanguage()); + } if ($user->getTwoFactorAuthentication('required') && !$user->getTwoFactorAuthentication('enabled')) { return $this->redirectToRoute('opendxp_admin_2fa_setup'); } - // allow to alter settings via an event - $settingsEvent = new IndexActionSettingsEvent($templateParams['settings'] ?? []); - $this->eventDispatcher->dispatch($settingsEvent, AdminEvents::INDEX_ACTION_SETTINGS); - $templateParams['settings'] = $settingsEvent->getSettings(); + $result = $indexAction($request); - return $this->render($settingsEvent->getTemplate() ?: '@OpenDxpAdmin/admin/index/index.html.twig', $templateParams); + return $this->render($result->template ?? '@OpenDxpAdmin/admin/index/index.html.twig', $result->templateParams); } #[Route('/index/statistics', name: 'opendxp_admin_index_statistics', methods: ['GET'])] - public function statisticsAction(Request $request, Connection $db, KernelInterface $kernel): JsonResponse + public function statisticsAction(Request $request, StatisticsActionHandler $statisticsAction): JsonResponse { if (!$request->isXmlHttpRequest()) { throw $this->createAccessDeniedHttpException(); } - try { - $mysqlVersion = $db->fetchOne('SELECT VERSION()'); - } catch (Throwable) { - $mysqlVersion = null; - } - - try { - $data = [ - 'instance_id' => $this->getInstanceId(), - 'revision' => Version::getRevision(), - 'version' => Version::getVersion(), - 'major_version' => Version::getMajorVersion(), - 'php_version' => PHP_VERSION, - 'db_version' => $mysqlVersion, - 'bundles' => array_keys($kernel->getBundles()), - ]; - } catch (Throwable) { - $data = []; - } - - try { - $this->httpClient->request( - 'POST', - 'https://metrics.opendxp.io/statistics', - [ - 'json' => $data, - ] - ); - } catch (GuzzleException) { - // fail silently - } - - return $this->adminJson([ - 'success' => true, - ]); - } - - protected function addRuntimePerspective(array &$templateParams, User $user): static - { - $runtimePerspective = \OpenDxp\Bundle\AdminBundle\Perspective\Config::getRuntimePerspective($user); - $templateParams['runtimePerspective'] = $runtimePerspective; - - return $this; - } - - protected function addPluginAssets(OpenDxpBundleManager $bundleManager, array &$templateParams): static - { - $templateParams['pluginJsPaths'] = $bundleManager->getJsPaths(); - $templateParams['pluginCssPaths'] = $bundleManager->getCssPaths(); - - return $this; - } - - protected function setAdminLanguage(Request $request, User $user): static - { - // set user language - $request->setLocale($user->getLanguage()); - if ($this->translator instanceof LocaleAwareInterface) { - $this->translator->setLocale($user->getLanguage()); - } - - return $this; - } - - protected function buildOpenDxpSettings( - Request $request, - array &$templateParams, - User $user, KernelInterface $kernel, - ExecutorInterface $maintenanceExecutor, - CsrfProtectionHandler $csrfProtection, - Tool\MaintenanceModeHelperInterface $maintenanceModeHelper - ): static { - $config = $templateParams['config']; - $systemSettings = $templateParams['systemSettings']; - $adminSettings = $templateParams['adminSettings']; - $requiredLanguages = $systemSettings['general']['valid_languages']; - $dashboardHelper = new Dashboard($user); - $customAdminEntrypoint = $this->getParameter('opendxp_admin.custom_admin_route_name'); - - try { - $adminEntrypointUrl = $this->generateUrl($customAdminEntrypoint, [], UrlGeneratorInterface::ABSOLUTE_URL); - } catch (Exception) { - // if the custom admin entrypoint is not defined, return null in the settings - $adminEntrypointUrl = null; - } - - if (array_key_exists('required_languages', $systemSettings['general'])) { - $requiredLanguages = $systemSettings['general']['required_languages']; - } - - $settings = [ - 'instanceId' => $this->getInstanceId(), - 'version' => Version::getVersion(), - 'build' => Version::getRevision(), - 'debug' => OpenDxp::inDebugMode(), - 'devmode' => OpenDxp::inDevMode(), - 'disableMinifyJs' => OpenDxp::disableMinifyJs(), - 'environment' => $kernel->getEnvironment(), - 'sessionId' => htmlentities($request->getSession()->getId(), ENT_QUOTES, 'UTF-8'), - - // languages - 'language' => $request->getLocale(), - 'websiteLanguages' => Admin::reorderWebsiteLanguages( - $this->getAdminUser(), - $systemSettings['general']['valid_languages'], - true - ), - 'requiredLanguages' => $requiredLanguages, - - // flags - 'showCloseConfirmation' => true, - 'debug_admin_translations' => (bool)$systemSettings['general']['debug_admin_translations'], - 'document_generatepreviews' => (bool)$config['documents']['generate_preview'], - 'asset_disable_tree_preview' => (bool)$adminSettings['assets']['disable_tree_preview'], - 'asset_hide_edit' => (bool)$adminSettings['assets']['hide_edit_image'], - 'asset_tree_paging_limit' => $config['assets']['tree_paging_limit'], - 'asset_default_upload_path' => $config['assets']['default_upload_path'], - 'chromium' => HtmlToImage::isSupported(), - 'videoconverter' => Video::isAvailable(), - 'main_domain' => $systemSettings['general']['domain'], - 'custom_admin_entrypoint_url' => $adminEntrypointUrl, - 'timezone' => $config['general']['timezone'] ?: date_default_timezone_get(), - 'tile_layer_url_template' => $config['maps']['tile_layer_url_template'], - 'geocoding_url_template' => $config['maps']['geocoding_url_template'], - 'reverse_geocoding_url_template' => $config['maps']['reverse_geocoding_url_template'], - 'document_tree_paging_limit' => $config['documents']['tree_paging_limit'], - 'object_tree_paging_limit' => $config['objects']['tree_paging_limit'], - 'hostname' => htmlentities(\OpenDxp\Tool::getHostname(), ENT_QUOTES, 'UTF-8'), - 'dependency' => $config['dependency']['enabled'], - - 'document_auto_save_interval' => $config['documents']['auto_save_interval'], - 'object_auto_save_interval' => $config['objects']['auto_save_interval'], - - // perspective and portlets - 'perspective' => $templateParams['runtimePerspective'], - 'availablePerspectives' => \OpenDxp\Bundle\AdminBundle\Perspective\Config::getAvailablePerspectives($user), - 'disabledPortlets' => $dashboardHelper->getDisabledPortlets(), - - // this stuff is used to decide whether the "add" button should be grayed out or not - 'image-thumbnails-writeable' => (new Asset\Image\Thumbnail\Config())->isWriteable(), - 'video-thumbnails-writeable' => (new Asset\Video\Thumbnail\Config())->isWriteable(), - 'document-types-writeable' => (new DocType())->isWriteable(), - 'predefined-properties-writeable' => (new Predefined())->isWriteable(), - 'predefined-asset-metadata-writeable' => (new \OpenDxp\Model\Metadata\Predefined())->isWriteable(), - 'perspectives-writeable' => \OpenDxp\Bundle\AdminBundle\Perspective\Config::isWriteable(), - 'custom-views-writeable' => \OpenDxp\Bundle\AdminBundle\CustomView\Config::isWriteable(), - 'class-definition-writeable' => !isset($_SERVER['OPENDXP_CLASS_DEFINITION_WRITABLE']) || - (bool) $_SERVER['OPENDXP_CLASS_DEFINITION_WRITABLE'], - 'object-custom-layout-writeable' => (new CustomLayout())->isWriteable(), - 'select-options-writeable' => (new \OpenDxp\Model\DataObject\SelectOptions\Config())->isWriteable(), - - // search types - 'asset_search_types' => Asset::getTypes(), - - // document types - 'document_types_configuration' => Document::getTypesConfiguration(), - 'document_search_types' => Document::getTypes(), - 'document_valid_types' => array_values(array_filter(Document::getTypes(), fn ($type) => $type !== 'folder')), - // email search compatible document types - 'document_email_search_types' => $config['documents']['email_search'], - 'select_options_provider_class' => SelectOptionsOptionsProvider::class, - ]; - - $this - ->addSystemVarSettings($settings) - ->addMaintenanceSettings($settings, $maintenanceExecutor, $maintenanceModeHelper) - ->addMailSettings($settings, $config, $systemSettings) - ->addCustomViewSettings($settings) - ->addNotificationSettings($settings, $config); - - $settings['csrfToken'] = $csrfProtection->getCsrfToken($request->getSession()); - - $templateParams['settings'] = $settings; - - return $this; - } - - private function getInstanceId(): string - { - $instanceId = 'not-set'; - - try { - $instanceId = $this->getParameter('secret'); - $instanceId = sha1(substr($instanceId, 3, -3)); - } catch (Exception) { - // nothing to do - } - - return $instanceId; - } - - protected function addSystemVarSettings(array &$settings): static - { - // upload limit - $max_upload = OpenDxp\Helper\FileSystemHelper::filesizeToBytes(ini_get('upload_max_filesize') . 'B'); - $max_post = OpenDxp\Helper\FileSystemHelper::filesizeToBytes(ini_get('post_max_size') . 'B'); - $upload_mb = min($max_upload, $max_post) ?: $max_upload; - - $settings['upload_max_filesize'] = (int) $upload_mb; - - // session lifetime (gc) - $session_gc_maxlifetime = ini_get('session.gc_maxlifetime'); - if (empty($session_gc_maxlifetime)) { - $session_gc_maxlifetime = 120; - } - - $settings['session_gc_maxlifetime'] = (int)$session_gc_maxlifetime; - - return $this; - } - - protected function addMaintenanceSettings( - array &$settings, - ExecutorInterface $maintenanceExecutor, - Tool\MaintenanceModeHelperInterface $maintenanceModeHelper - ): static { - // check maintenance - $maintenance_active = false; - // maintenance script should run at least every hour + a little tolerance - if (($lastExecution = $maintenanceExecutor->getLastExecution()) && time() - $lastExecution < 3660) { - $maintenance_active = true; - } - - $settings['maintenance_active'] = $maintenance_active; - $settings['maintenance_mode'] = $maintenanceModeHelper->isActive(); - - return $this; - } - - protected function addMailSettings(array &$settings, Config $config, array $systemSettings): static - { - //mail settings - $mailIncomplete = false; - if (isset($config['email']) && $systemSettings['email']) { - if (OpenDxp::inDebugMode() && empty($systemSettings['email']['debug']['email_addresses'])) { - $mailIncomplete = true; - } - if (empty($config['email']['sender']['email'])) { - $mailIncomplete = true; - } - } - - $settings['mail'] = !$mailIncomplete; - $settings['mailDefaultAddress'] = $config['email']['sender']['email'] ?? null; - - return $this; - } - - protected function addCustomViewSettings(array &$settings): static - { - $cvData = []; - - // still needed when publishing objects - $cvConfig = \OpenDxp\Bundle\AdminBundle\CustomView\Config::get(); - - foreach ($cvConfig as $node) { - $tmpData = $node; - // backwards compatibility - $treeType = $tmpData['treetype'] ?: 'object'; - $rootNode = Service::getElementByPath($treeType, $tmpData['rootfolder']); - - if ($rootNode) { - $tmpData['rootId'] = $rootNode->getId(); - $tmpData['allowedClasses'] = $tmpData['classes'] ?? null; - $tmpData['showroot'] = (bool)$tmpData['showroot']; - - // Check if a user has privileges to that node - if ($rootNode->isAllowed('list')) { - $cvData[] = $tmpData; - } - } - } - - $settings['customviews'] = $cvData; - - return $this; - } - - /** - * @return $this - */ - protected function addNotificationSettings(array &$settings, Config $config): static - { - $enabled = (bool)$config['notifications']['enabled']; - - $settings['notifications_enabled'] = $enabled; - $settings['checknewnotification_enabled'] = $enabled && (bool) $config['notifications']['check_new_notification']['enabled']; - - // convert the config parameter interval (seconds) in milliseconds - $settings['checknewnotification_interval'] = $config['notifications']['check_new_notification']['interval'] * 1000; + $statisticsAction(); - return $this; + return $this->adminJson(ApiResponse::ok()); } public function onKernelResponseEvent(ResponseEvent $event): void diff --git a/src/Controller/Admin/InstallController.php b/src/Controller/Admin/InstallController.php index df2ef2ef..82cc06ec 100644 --- a/src/Controller/Admin/InstallController.php +++ b/src/Controller/Admin/InstallController.php @@ -23,6 +23,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Profiler\Profiler; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal @@ -31,14 +32,19 @@ class InstallController extends AdminAbstractController { #[Route('/check', name: 'opendxp_admin_install_check', methods: ['GET', 'POST'])] - public function checkAction(Request $request, Connection $db, ?Profiler $profiler): Response + public function checkAction( + Request $request, + Connection $db, + ?Profiler $profiler, + #[MapQueryParameter] bool $headless = false, + ): Response { if ($profiler) { $profiler->disable(); } $viewParams = Requirements::checkAll($db); - $viewParams['headless'] = $request->query->getBoolean('headless') || $request->request->getBoolean('headless'); + $viewParams['headless'] = $headless || $request->request->getBoolean('headless'); return $this->render('@OpenDxpAdmin/admin/install/check.html.twig', $viewParams); } diff --git a/src/Controller/Admin/LoginController.php b/src/Controller/Admin/LoginController.php index b78791fc..95de0b59 100644 --- a/src/Controller/Admin/LoginController.php +++ b/src/Controller/Admin/LoginController.php @@ -17,42 +17,31 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; -use Browser; -use Endroid\QrCode\Builder\Builder; -use Endroid\QrCode\Writer\PngWriter; -use Exception; use OpenDxp; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Event\Login\LoginRedirectEvent; -use OpenDxp\Bundle\AdminBundle\Event\Login\LostPasswordEvent; +use OpenDxp\Bundle\AdminBundle\Handler\Login\GenerateTwoFactorSetupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Login\LostPasswordHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Login\SaveTwoFactorSetupHandler; +use OpenDxp\Bundle\AdminBundle\Factory\LoginPageFactory; use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler; -use OpenDxp\Bundle\AdminBundle\System\AdminConfig; -use OpenDxp\Config; use OpenDxp\Controller\KernelControllerEventInterface; use OpenDxp\Controller\KernelResponseEventInterface; -use OpenDxp\Extension\Bundle\OpenDxpBundleManager; use OpenDxp\Http\Request\Host\GeneralHostResolver; use OpenDxp\Http\ResponseHelper; use OpenDxp\Logger; -use OpenDxp\Model\User; use OpenDxp\Security\SecurityHelper; -use OpenDxp\Tool; -use OpenDxp\Tool\Authentication; -use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; -use Symfony\Component\EventDispatcher\GenericEvent; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\HttpKernel\Event\ResponseEvent; -use Symfony\Component\RateLimiter\RateLimiterFactory; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use Symfony\Component\Security\Http\SecurityRequestAttributes; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\Translation\LocaleAwareInterface; @@ -66,21 +55,18 @@ class LoginController extends AdminAbstractController implements KernelControlle public function __construct( protected ResponseHelper $responseHelper, protected TranslatorInterface $translator, - protected OpenDxpBundleManager $bundleManager, - protected EventDispatcherInterface $eventDispatcher - ) { - } + protected EventDispatcherInterface $eventDispatcher, + private readonly LoginPageFactory $loginPageFactory, + ) {} public function onKernelControllerEvent(ControllerEvent $event): void { - // use browser language for login page if possible $locale = 'en'; + $availableLocales = \OpenDxp\Tool\Admin::getLanguages(); - $availableLocales = Tool\Admin::getLanguages(); foreach ($event->getRequest()->getLanguages() as $userLocale) { if (in_array($userLocale, $availableLocales)) { $locale = $userLocale; - break; } } @@ -101,11 +87,9 @@ public function onKernelResponseEvent(ResponseEvent $event): void #[Route('/login/', name: 'opendxp_admin_login_fallback')] public function loginAction( Request $request, - AuthenticationUtils $authenticationUtils, CsrfProtectionHandler $csrfProtection, - Config $config + #[MapQueryParameter(name: 'too_many_attempts')] ?string $tooManyAttempts = null, ): RedirectResponse|Response { - $queryParams = $request->query->all(); if ($request->attributes->get('_route') === 'opendxp_admin_login_fallback') { @@ -117,58 +101,22 @@ public function loginAction( return new RedirectResponse($redirectUrl); } - // check csrf token before generating a new one with force=true if (!$csrfProtection->getCsrfToken($request->getSession())) { $csrfProtection->regenerateCsrfToken($request->getSession()); } - $user = $this->getUser(); - if ($user instanceof UserInterface) { + if ($this->getUser() instanceof UserInterface) { return $this->redirectToRoute('opendxp_admin_index'); } - $params = $this->buildLoginPageViewParams($config); - - $session_gc_maxlifetime = ini_get('session.gc_maxlifetime'); - if (empty($session_gc_maxlifetime)) { - $session_gc_maxlifetime = 120; - } - - $params['csrfTokenRefreshInterval'] = ((int)$session_gc_maxlifetime - 60) * 1000; - - if ($request->query->has('too_many_attempts')) { - $params['error'] = SecurityHelper::convertHtmlSpecialChars($request->query->get('too_many_attempts')); - } - if ($request->query->has('auth_failed')) { - $params['error'] = 'error_auth_failed'; - } - if ($request->query->has('session_expired')) { - $params['error'] = 'error_session_expired'; - } - if ($request->query->has('deeplink')) { - $params['deeplink'] = true; - } - - $params['browserSupported'] = $this->detectBrowser(); - $params['debug'] = OpenDxp::inDebugMode(); - - $params['includeTemplates'] = []; - $event = new GenericEvent($this, [ - 'parameters' => $params, - 'config' => $config, - 'request' => $request, - ]); - - $this->eventDispatcher->dispatch($event, AdminEvents::LOGIN_BEFORE_RENDER); - $params = $event->getArgument('parameters'); - - $params['login_error'] = $authenticationUtils->getLastAuthenticationError(); - - return $this->render('@OpenDxpAdmin/admin/login/login.html.twig', $params); + return $this->render( + '@OpenDxpAdmin/admin/login/login.html.twig', + $this->loginPageFactory->create($request)->forLoginPage($tooManyAttempts), + ); } #[Route('/login/csrf-token', name: 'opendxp_admin_login_csrf_token')] - public function csrfTokenAction(Request $request, CsrfProtectionHandler $csrfProtection): \Symfony\Component\HttpFoundation\JsonResponse + public function csrfTokenAction(Request $request, CsrfProtectionHandler $csrfProtection): JsonResponse { if (!$this->getAdminUser()) { $csrfProtection->regenerateCsrfToken($request->getSession()); @@ -182,21 +130,21 @@ public function csrfTokenAction(Request $request, CsrfProtectionHandler $csrfPro #[Route('/logout', name: 'opendxp_admin_logout', methods: ['POST'])] public function logoutAction(): void { - // this route will never be matched, but will be handled by the logout handler + // handled by the logout handler } /** * Dummy route used to check authentication */ #[Route('/login/login', name: 'opendxp_admin_login_check')] - public function loginCheckAction(Request $request): RedirectResponse - { + public function loginCheckAction( + #[MapQueryParameter] ?string $perspective = null, + ): RedirectResponse { $params = []; - if ($request->query->has('perspective')) { - $params['perspective'] = strip_tags($request->query->get('perspective')); + if ($perspective !== null) { + $params['perspective'] = strip_tags($perspective); } - // just in case the authenticator didn't redirect return new RedirectResponse($this->generateUrl('opendxp_admin_login', $params)); } @@ -204,81 +152,24 @@ public function loginCheckAction(Request $request): RedirectResponse public function lostpasswordAction( Request $request, CsrfProtectionHandler $csrfProtection, - Config $config, - RateLimiterFactory $resetPasswordLimiter, - RouterInterface $router, - GeneralHostResolver $generalHostResolver + LostPasswordHandler $lostPassword, + GeneralHostResolver $generalHostResolver, ): Response { - $params = $this->buildLoginPageViewParams($config); - $error = null; + $params = $this->loginPageFactory->create($request)->base(); if ($request->getMethod() === 'POST' && $request->request->has('username')) { - - $username = $request->request->get('username'); - $user = User::getByName($username); - if (!$user instanceof User) { - $error = 'user_unknown'; - } - - $limiter = $resetPasswordLimiter->create($request->getClientIp()); - - if (false === $limiter->consume(1)->isAccepted()) { - $error = 'user_reset_password_too_many_attempts'; - } - - if (!$error) { - if (!$user->isActive()) { - $error = 'user_inactive'; - } - if (!$user->getEmail()) { - $error = 'user_no_email_address'; - } - if (!$user->getPassword()) { - $error = 'user_no_password'; - } + $result = $lostPassword( + username: (string) $request->request->get('username'), + clientIp: (string) $request->getClientIp(), + domain: $generalHostResolver->resolve(['source' => $request]) ?? '', + ); + + if ($result->eventResponse !== null) { + return $result->eventResponse; } - if (!$error) { - $token = Authentication::generateTokenByUser($user); - - try { - $domain = $generalHostResolver->resolve(['source' => $request]); - if (!$domain) { - throw new Exception('No main domain set in system settings, unable to generate reset password link'); - } - - $context = $router->getContext(); - $context->setHost($domain); - - $loginUrl = $this->generateUrl('opendxp_admin_login_check', [ - 'token' => $token, - 'reset' => 'true', - ], UrlGeneratorInterface::ABSOLUTE_URL); - - $event = new LostPasswordEvent($user, $loginUrl); - $this->eventDispatcher->dispatch($event, AdminEvents::LOGIN_LOSTPASSWORD); - - // only send mail if it wasn't prevented in event - if ($event->getSendMail()) { - $mail = Tool::getMail([$user->getEmail()], 'OpenDXP lost password service'); - $mail->setIgnoreDebugMode(true); - $mail->text("Login to OpenDXP and change your password using the following link. This temporary login link will expire in 24 hours: \r\n\r\n" . $loginUrl); - $mail->send(); - } - - // directly return event response - if ($event->hasResponse()) { - return $event->getResponse(); - } - } catch (Exception $e) { - Logger::error('Error sending password recovery email: ' . $e->getMessage()); - $error = 'lost_password_email_error'; - } - } - - if ($error) { - Logger::error('Lost password service: ' . $error); - //to avoid timing based enumeration + if ($result->error !== null) { + Logger::error('Lost password service: ' . $result->error); usleep(random_int(50, 200)); } } @@ -289,34 +180,32 @@ public function lostpasswordAction( } #[Route('/login/deeplink', name: 'opendxp_admin_login_deeplink')] - public function deeplinkAction(Request $request): Response - { - // check for deeplink + public function deeplinkAction( + Request $request, + #[MapQueryParameter] string $perspective = '', + ): Response { $queryString = $request->server->get('QUERY_STRING'); if (preg_match('/(document|asset|object)_(\d+)_([a-z]+)/', $queryString, $deeplink)) { $deeplink = $deeplink[0]; - $perspective = strip_tags($request->query->get('perspective', '')); + $perspective = strip_tags($perspective); + if (strpos($queryString, 'token')) { $url = $this->dispatchLoginRedirect([ 'deeplink' => $deeplink, 'perspective' => $perspective, ]); - $url .= '&' . $queryString; - - return $this->redirect($url); + return $this->redirect($url . '&' . $queryString); } if ($queryString) { - $url = $this->dispatchLoginRedirect([ - 'deeplink' => 'true', - 'perspective' => $perspective, - ]); - return $this->render('@OpenDxpAdmin/admin/login/deeplink.html.twig', [ 'tab' => $deeplink, - 'redirect' => $url, + 'redirect' => $this->dispatchLoginRedirect([ + 'deeplink' => 'true', + 'perspective' => $perspective, + ]), ]); } } @@ -324,29 +213,17 @@ public function deeplinkAction(Request $request): Response throw $this->createNotFoundException(); } - /** - * @return array{config: Config, pluginCssPaths: string[]} - */ - protected function buildLoginPageViewParams(Config $config): array - { - return [ - 'config' => $config, - 'adminSettings' => AdminConfig::get(), - 'pluginCssPaths' => $this->bundleManager->getCssPaths(), - ]; - } - #[Route('/login/2fa', name: 'opendxp_admin_2fa')] - public function twoFactorAuthenticationAction(Request $request, Config $config): Response + public function twoFactorAuthenticationAction(Request $request): Response { - $params = $this->buildLoginPageViewParams($config); + $params = $this->loginPageFactory->create($request)->base(); if ($request->hasSession()) { $session = $request->getSession(); $authException = $session->get(SecurityRequestAttributes::AUTHENTICATION_ERROR); + if ($authException instanceof AuthenticationException) { $session->remove(SecurityRequestAttributes::AUTHENTICATION_ERROR); - $params['error'] = $authException->getMessage(); } } else { @@ -359,85 +236,41 @@ public function twoFactorAuthenticationAction(Request $request, Config $config): #[Route('/login/2fa-setup', name: 'opendxp_admin_2fa_setup')] public function twoFactorSetupAuthenticationAction( Request $request, - Config $config, - GoogleAuthenticatorInterface $twoFactor + GenerateTwoFactorSetupHandler $generateSetup, + SaveTwoFactorSetupHandler $saveSetup, + #[MapQueryParameter] ?string $error = null, ): Response { - $params = $this->buildLoginPageViewParams($config); + $params = $this->loginPageFactory->create($request)->base(); $params['setup'] = true; - $user = $this->getAdminUser(); - $proxyUser = $this->getAdminUser(true); - - if ($request->query->get('error')) { - $params['error'] = $request->query->get('error'); + if ($error) { + $params['error'] = $error; } if ($request->isMethod('post')) { - $secret = $request->getSession()->get('2fa_secret'); - - if (!$secret) { - throw new Exception('2fa secret not found'); - } - - $user->setTwoFactorAuthentication('enabled', true); - $user->setTwoFactorAuthentication('type', 'google'); - $user->setTwoFactorAuthentication('secret', $secret); - - if (!$twoFactor->checkCode($proxyUser, $request->request->get('_auth_code'))) { + try { + $saveSetup( + secret: (string) $request->getSession()->get('2fa_secret'), + authCode: (string) $request->request->get('_auth_code'), + ); + } catch (\Throwable) { return new RedirectResponse($this->generateUrl('opendxp_admin_2fa_setup', ['error' => '2fa_wrong'])); } - $user->save(); - return new RedirectResponse($this->generateUrl('opendxp_admin_login')); } - $newSecret = $twoFactor->generateSecret(); - - $request->getSession()->set('2fa_secret', $newSecret); - - $user->setTwoFactorAuthentication('enabled', true); - $user->setTwoFactorAuthentication('type', 'google'); - $user->setTwoFactorAuthentication('secret', $newSecret); - - $url = $twoFactor->getQRContent($proxyUser); - - $result = Builder::create() - ->writer(new PngWriter()) - ->data($url) - ->size(200) - ->build(); - - $params['image'] = $result->getDataUri(); + $result = $generateSetup(); + $request->getSession()->set('2fa_secret', $result->secret); + $params['image'] = $result->qrDataUri; return $this->render('@OpenDxpAdmin/admin/login/two_factor_setup.html.twig', $params); } #[Route('/login/2fa-verify', name: 'opendxp_admin_2fa-verify')] - public function twoFactorAuthenticationVerifyAction(Request $request): void + public function twoFactorAuthenticationVerifyAction(): void { - } - - public function detectBrowser(): bool - { - $supported = false; - $browser = new Browser(); - $browserVersion = (int)$browser->getVersion(); - - if ($browser->getBrowser() == Browser::BROWSER_FIREFOX && $browserVersion >= 72) { - $supported = true; - } - if ($browser->getBrowser() == Browser::BROWSER_CHROME && $browserVersion >= 84) { - $supported = true; - } - if ($browser->getBrowser() == Browser::BROWSER_SAFARI && $browserVersion >= 13.1) { - $supported = true; - } - if ($browser->getBrowser() == Browser::BROWSER_EDGE && $browserVersion >= 90) { - return true; - } - - return $supported; + // handled by firewall } private function dispatchLoginRedirect(array $routeParams = []): string diff --git a/src/Controller/Admin/MiscController.php b/src/Controller/Admin/MiscController.php index 124125bf..a2b674dd 100644 --- a/src/Controller/Admin/MiscController.php +++ b/src/Controller/Admin/MiscController.php @@ -1,5 +1,4 @@ getControllerReferences(); @@ -51,11 +55,7 @@ public function getAvailableControllerReferencesAction(Request $request, Control 'name' => $controller, ], $controllerReferences); - return $this->adminJson([ - 'success' => true, - 'data' => $result, - 'total' => count($result), - ]); + return $this->adminJson(ApiResponse::ok(['data' => $result, 'total' => count($result)])); } #[Route('/get-available-templates', name: 'opendxp_admin_misc_getavailabletemplates', methods: ['GET'])] @@ -75,39 +75,14 @@ public function getAvailableTemplatesAction(ControllerDataProvider $provider): J } #[Route('/json-translations-system', name: 'opendxp_admin_misc_jsontranslationssystem', methods: ['GET'])] - public function jsonTranslationsSystemAction(Request $request, TranslatorInterface $translator): Response - { - $language = $request->query->get('language'); - - /** @var Translator $translator */ - $translator->lazyInitialize('admin', $language); - - $translations = []; - - $fallbackLanguages = []; - if (null !== Locale::getRegion($language)) { - // if language is region specific, add the primary language as fallback - $fallbackLanguages[] = Locale::getPrimaryLanguage($language); - } - if ($language != 'en') { - // add en as a fallback - $fallbackLanguages[] = 'en'; - } - - foreach (['admin', 'admin_ext'] as $domain) { - $translations = array_replace($translations, $translator->getCatalogue($language)->all($domain)); - - foreach ($fallbackLanguages as $fallbackLanguage) { - $translator->lazyInitialize($domain, $fallbackLanguage); - foreach ($translator->getCatalogue($fallbackLanguage)->all($domain) as $key => $value) { - if (empty($translations[$key])) { - $translations[$key] = $value; - } - } - } - } - - $response = new Response('opendxp.system_i18n = ' . $this->encodeJson($translations) . ';'); + public function jsonTranslationsSystemAction( + GetJsonTranslationsHandler $getJsonTranslations, + Translator $translator, + #[MapQueryParameter] ?string $language = null, + ): Response { + $result = $getJsonTranslations($translator, $language); + + $response = new Response('opendxp.system_i18n = ' . $this->encodeJson($result->translations) . ';'); $response->headers->set('Content-Type', 'text/javascript'); return $response; @@ -117,9 +92,9 @@ public function jsonTranslationsSystemAction(Request $request, TranslatorInterfa * @internal */ #[Route('/script-proxy', name: 'opendxp_admin_misc_scriptproxy', methods: ['GET'])] - public function scriptProxyAction(Request $request): Response - { - $storageFile = $request->query->get('storageFile'); + public function scriptProxyAction( + #[MapQueryParameter] ?string $storageFile = null, + ): Response { if (!$storageFile) { throw new InvalidArgumentException('The parameter storageFile is required'); } @@ -149,7 +124,7 @@ public function scriptProxyAction(Request $request): Response } #[Route('/admin-css', name: 'opendxp_admin_misc_admincss', methods: ['GET'])] - public function adminCssAction(Request $request, Config $config): Response + public function adminCssAction(Config $config): Response { // customviews config $cvData = \OpenDxp\Bundle\AdminBundle\CustomView\Config::get(); @@ -170,17 +145,13 @@ public function adminCssAction(Request $request, Config $config): Response } #[Route('/ping', name: 'opendxp_admin_misc_ping', methods: ['GET'])] - public function pingAction(Request $request): JsonResponse + public function pingAction(): JsonResponse { - $response = [ - 'success' => true, - ]; - - return $this->adminJson($response); + return $this->adminJson(ApiResponse::ok()); } #[Route('/available-languages', name: 'opendxp_admin_misc_availablelanguages', methods: ['GET'])] - public function availableLanguagesAction(Request $request): Response + public function availableLanguagesAction(): Response { $locales = Tool::getSupportedLocales(); $response = new Response('opendxp.available_languages = ' . $this->encodeJson($locales) . ';'); @@ -190,29 +161,33 @@ public function availableLanguagesAction(Request $request): Response } #[Route('/get-valid-filename', name: 'opendxp_admin_misc_getvalidfilename', methods: ['GET'])] - public function getValidFilenameAction(Request $request): JsonResponse - { + public function getValidFilenameAction( + #[MapQueryParameter] ?string $value = null, + #[MapQueryParameter] ?string $type = null, + ): JsonResponse { return $this->adminJson([ - 'filename' => \OpenDxp\Model\Element\Service::getValidKey($request->query->get('value'), $request->query->get('type')), + 'filename' => \OpenDxp\Model\Element\Service::getValidKey($value, $type), ]); } + #[IsGranted(CorePermission::MaintenanceMode->value)] #[Route('/maintenance', name: 'opendxp_admin_misc_maintenance', methods: ['POST'])] - public function maintenanceAction(Request $request, Tool\MaintenanceModeHelperInterface $maintenanceModeHelper): JsonResponse - { - $this->checkPermission('maintenance_mode'); - - if ($request->query->get('activate')) { + public function maintenanceAction( + Request $request, + Tool\MaintenanceModeHelperInterface $maintenanceModeHelper, + #[MapQueryParameter] ?string $activate = null, + #[MapQueryParameter] ?string $deactivate = null, + ): JsonResponse { + + if ($activate) { $maintenanceModeHelper->activate($request->getSession()->getId()); } - if ($request->query->get('deactivate')) { + if ($deactivate) { $maintenanceModeHelper->deactivate(); } - return $this->adminJson([ - 'success' => true, - ]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/country-list', name: 'opendxp_admin_misc_countrylist', methods: ['GET'])] @@ -235,7 +210,7 @@ public function countryListAction(LocaleServiceInterface $localeService): JsonRe } #[Route('/language-list', name: 'opendxp_admin_misc_languagelist', methods: ['GET'])] - public function languageListAction(Request $request): JsonResponse + public function languageListAction(): JsonResponse { $locales = Tool::getSupportedLocales(); $options = []; @@ -251,9 +226,10 @@ public function languageListAction(Request $request): JsonResponse } #[Route('/get-language-flag', name: 'opendxp_admin_misc_getlanguageflag', methods: ['GET'])] - public function getLanguageFlagAction(Request $request): BinaryFileResponse - { - $iconPath = AdminTool::getLanguageFlagFile($request->query->get('language')); + public function getLanguageFlagAction( + #[MapQueryParameter] ?string $language = null, + ): BinaryFileResponse { + $iconPath = AdminTool::getLanguageFlagFile($language); $response = new BinaryFileResponse($iconPath); $response->headers->set('Content-Type', 'image/svg+xml'); @@ -262,75 +238,32 @@ public function getLanguageFlagAction(Request $request): BinaryFileResponse } #[Route('/icon-list', name: 'opendxp_admin_misc_iconlist', methods: ['GET'])] - public function iconListAction(Request $request, ?Profiler $profiler): Response - { + public function iconListAction( + GetIconListHandler $getIconList, + ?Profiler $profiler, + #[MapQueryParameter] ?string $type = null, + ): Response { if ($profiler) { $profiler->disable(); } - $type = $request->query->get('type'); - $publicDir = OPENDXP_WEB_ROOT . '/bundles/opendxpadmin'; - $iconDir = $publicDir . '/img'; - $extraInfo = null; - - $icons = match ($type) { - 'color' => FileSystemHelper::scanDirectory($iconDir . '/flat-color-icons/'), - 'white' => FileSystemHelper::scanDirectory($iconDir . '/flat-white-icons/'), - 'twemoji' => FileSystemHelper::scanDirectory($iconDir . '/twemoji/'), - 'flags' => $this->getFlags(), - default => [] - }; - - $source = match ($type) { - 'color', 'white' => - 'based on the ' . - 'Material Design Icons', - 'twemoji' => - 'based on the ' . - 'Twemoji icons', - default => '' - }; - - if ($type === 'twemoji') { - $extraInfo = 'ℹ Click on icon with green border to display all its related variants. Click on the letter to display flags with the clicked initial'; - } - - $iconsCss = file_get_contents($publicDir . '/css/icons.css'); - if ($type === null) { return $this->render('@OpenDxpAdmin/admin/misc/icon_library_reload.html.twig'); } + $result = $getIconList($type); + return $this->render('@OpenDxpAdmin/admin/misc/icon_list.html.twig', [ - 'icons' => $icons, - 'iconsCss' => $iconsCss, - 'type' => $type, - 'extraInfo' => $extraInfo, - 'source' => $source, + 'icons' => $result->icons, + 'iconsCss' => $result->iconsCss, + 'type' => $result->type, + 'extraInfo' => $result->extraInfo, + 'source' => $result->source, ]); } - private function getFlags(): array - { - $locales = Tool::getSupportedLocales(); - $languageOptions = []; - foreach (array_keys($locales) as $short) { - if (!empty($short)) { - $flag = AdminTool::getLanguageFlagFile($short, true, false); - if ($flag) { - $languageOptions[] = $flag; - } - } - } - - $languageOptions = array_unique($languageOptions); - sort($languageOptions); - - return $languageOptions; - } - #[Route('/test', name: 'opendxp_admin_misc_test')] - public function testAction(Request $request): Response + public function testAction(): Response { return new Response('done'); } diff --git a/src/Controller/Admin/NotificationController.php b/src/Controller/Admin/NotificationController.php index abfaef49..ff5e9e72 100644 --- a/src/Controller/Admin/NotificationController.php +++ b/src/Controller/Admin/NotificationController.php @@ -18,16 +18,25 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; +use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\DeleteAllNotificationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\DeleteNotificationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindAllNotificationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindLastUnreadNotificationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindNotificationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\GetRecipientsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\MarkAsReadNotificationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\SendNotificationHandler; use OpenDxp\Model\Element\Service; use OpenDxp\Model\Notification\Service\NotificationService; -use OpenDxp\Model\Notification\Service\NotificationServiceFilterParser; use OpenDxp\Model\Notification\Service\UserService; -use OpenDxp\Model\User; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Contracts\Translation\TranslatorInterface; -use UnexpectedValueException; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal @@ -35,163 +44,122 @@ #[Route('/notification')] class NotificationController extends AdminAbstractController { + #[IsGranted(CorePermission::NotificationsSend->value)] #[Route('/recipients', name: 'opendxp_admin_notification_recipients', methods: ['GET'])] - public function recipientsAction(UserService $service, TranslatorInterface $translator): JsonResponse - { - $this->checkPermission('notifications_send'); - - $data = []; - - foreach ($service->findAll($this->getAdminUser()) as $recipient) { - $group = $translator->trans('group', [], 'admin'); - $prefix = $recipient->getType() === 'role' ? $group . ' - ' : ''; - - $data[] = [ - 'id' => $recipient->getId(), - 'text' => $prefix . $recipient->getName(), - ]; - } - - return $this->adminJson($data); + public function recipientsAction( + GetRecipientsHandler $getRecipients, + UserService $service, + TranslatorInterface $translator, + ): JsonResponse { + $result = $getRecipients($service, $translator); + + return $this->adminJson($result->data); } + #[IsGranted(CorePermission::NotificationsSend->value)] #[Route('/send', name: 'opendxp_admin_notification_send', methods: ['POST'])] - public function sendAction(Request $request, NotificationService $service): JsonResponse - { - $this->checkPermission('notifications_send'); - - $recipientId = $request->request->getInt('recipientId'); - $fromUser = (int) $this->getAdminUser()->getId(); - $title = $request->request->get('title', ''); - $message = $request->request->get('message', ''); - $element = null; - $elementId = $request->request->getInt('elementId'); + public function sendAction( + SendNotificationHandler $sendNotification, + Request $request, + NotificationService $service, + ): JsonResponse { + $elementId = (int) $request->request->get('elementId', 0); $elementType = $request->request->get('elementType'); + $element = null; if ($elementId && $elementType) { $element = Service::getElementById($elementType, $elementId); } - if (User::getById($recipientId) instanceof User) { - $service->sendToUser($recipientId, $fromUser, $title, $message, $element); - } else { - $service->sendToGroup($recipientId, $fromUser, $title, $message, $element); - } + $sendNotification( + service: $service, + recipientId: (int) $request->request->get('recipientId', 0), + title: (string) $request->request->get('title', ''), + message: (string) $request->request->get('message', ''), + element: $element, + ); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Notifications->value)] #[Route('/find', name: 'opendxp_admin_notification_find', methods: ['GET'])] - public function findAction(Request $request, NotificationService $service): JsonResponse - { - $this->checkPermission('notifications'); - - $id = $request->query->getInt('id'); - + public function findAction( + FindNotificationHandler $findNotification, + NotificationService $service, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { try { - $notification = $service->findAndMarkAsRead($id, $this->getAdminUser()->getId()); - } catch (UnexpectedValueException) { - return $this->adminJson( - [ - 'success' => false, - ] - ); - } + $result = $findNotification($service, $id); - $data = $service->format($notification); - - return $this->adminJson([ - 'success' => true, - 'data' => $data, - ]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); + } catch (\Throwable) { + return $this->adminJson(['success' => false]); + } } + #[IsGranted(CorePermission::Notifications->value)] #[Route('/find-all', name: 'opendxp_admin_notification_findall', methods: ['POST'])] - public function findAllAction(Request $request, NotificationService $service): JsonResponse - { - $this->checkPermission('notifications'); - - $filter = ['recipient' => (int) $this->getAdminUser()->getId()]; - $parser = new NotificationServiceFilterParser($request); - - foreach ($parser->parse() as $key => $val) { - $filter[$key] = $val; - } - - $options = [ - 'offset' => $request->request->getInt('start'), - 'limit' => $request->request->getInt('limit', 40), - ]; - - $result = $service->findAll($filter, $options); - - $data = []; - - foreach ($result['data'] as $notification) { - $data[] = $service->format($notification); - } - - return $this->adminJson([ - 'success' => true, - 'total' => $result['total'], - 'data' => $data, - ]); + public function findAllAction( + FindAllNotificationsHandler $findAllNotifications, + Request $request, + NotificationService $service, + ): JsonResponse { + $result = $findAllNotifications( + service: $service, + request: $request, + offset: $request->request->getInt('start'), + limit: $request->request->getInt('limit', 40), + ); + + return $this->adminJson(ApiResponse::ok(['total' => $result->total, 'data' => $result->data])); } + #[IsGranted(CorePermission::Notifications->value)] #[Route('/find-last-unread', name: 'opendxp_admin_notification_findlastunread', methods: ['GET'])] - public function findLastUnreadAction(Request $request, NotificationService $service): JsonResponse - { - $this->checkPermission('notifications'); - - $user = $this->getAdminUser(); - $lastUpdate = $request->query->getInt('lastUpdate', time()); - $result = $service->findLastUnread((int) $user->getId(), $lastUpdate); - $unread = $service->countAllUnread((int) $user->getId()); - - $data = []; - - foreach ($result['data'] as $notification) { - $data[] = $service->format($notification); - } - - return $this->adminJson([ - 'success' => true, - 'total' => $result['total'], - 'data' => $data, - 'unread' => $unread, - ]); + public function findLastUnreadAction( + FindLastUnreadNotificationsHandler $findLastUnread, + NotificationService $service, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $lastUpdate = null, + ): JsonResponse { + $result = $findLastUnread( + service: $service, + lastUpdate: $lastUpdate ?? time(), + ); + + return $this->adminJson(ApiResponse::ok(['total' => $result->total, 'data' => $result->data, 'unread' => $result->unread])); } + #[IsGranted(CorePermission::Notifications->value)] #[Route('/mark-as-read', name: 'opendxp_admin_notification_markasread', methods: ['PUT'])] - public function markAsReadAction(Request $request, NotificationService $service): JsonResponse - { - $this->checkPermission('notifications'); - - $id = $request->query->getInt('id'); - $service->findAndMarkAsRead($id, $this->getAdminUser()->getId()); - - return $this->adminJson(['success' => true]); + public function markAsReadAction( + MarkAsReadNotificationHandler $handler, + NotificationService $service, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { + $handler($service, $id); + + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Notifications->value)] #[Route('/delete', name: 'opendxp_admin_notification_delete', methods: ['DELETE'])] - public function deleteAction(Request $request, NotificationService $service): JsonResponse - { - $this->checkPermission('notifications'); - - $id = $request->query->getInt('id'); - $service->delete($id, $this->getAdminUser()->getId()); - - return $this->adminJson(['success' => true]); + public function deleteAction( + DeleteNotificationHandler $handler, + NotificationService $service, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { + $handler($service, $id); + + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Notifications->value)] #[Route('/delete-all', name: 'opendxp_admin_notification_deleteall', methods: ['DELETE'])] - public function deleteAllAction(NotificationService $service): JsonResponse + public function deleteAllAction(DeleteAllNotificationsHandler $deleteAllNotifications, NotificationService $service): JsonResponse { - $this->checkPermission('notifications'); - - $user = $this->getAdminUser(); - $service->deleteAll((int) $user->getId()); + $deleteAllNotifications($service); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } } diff --git a/src/Controller/Admin/PortalController.php b/src/Controller/Admin/PortalController.php index 78a6ede9..92343f14 100644 --- a/src/Controller/Admin/PortalController.php +++ b/src/Controller/Admin/PortalController.php @@ -1,5 +1,4 @@ dashboardHelper->getAllDashboards(); + $result = $getDashboardList(); - $data = []; - foreach (array_keys($dashboards) as $key) { - if ($key !== 'welcome') { - $data[] = $key; - } - } - - return $this->adminJson($data); + return $this->adminJson($result->dashboards); } #[Route('/create-dashboard', name: 'opendxp_admin_portal_createdashboard', methods: ['POST'])] - public function createDashboardAction(Request $request): JsonResponse + public function createDashboardAction(Request $request, CreateDashboardHandler $createDashboard): JsonResponse { - $dashboards = $this->dashboardHelper->getAllDashboards(); $key = trim($request->request->get('key', '')); - if (isset($dashboards[$key])) { - return $this->adminJson(['success' => false, 'message' => 'name_already_in_use']); - } - if (!empty($key)) { - $this->dashboardHelper->saveDashboard($key); - - return $this->adminJson(['success' => true]); + try { + $createDashboard($key); + } catch (\InvalidArgumentException $e) { + return $this->adminJson(ApiResponse::error($e->getMessage())); } - return $this->adminJson(['success' => false, 'message' => 'empty']); + return $this->adminJson(ApiResponse::ok()); } #[Route('/delete-dashboard', name: 'opendxp_admin_portal_deletedashboard', methods: ['DELETE'])] - public function deleteDashboardAction(Request $request): JsonResponse + public function deleteDashboardAction(Request $request, DeleteDashboardHandler $deleteDashboard): JsonResponse { - $key = $request->request->get('key'); - $this->dashboardHelper->deleteDashboard($key); + $deleteDashboard((string) $request->request->get('key')); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/get-configuration', name: 'opendxp_admin_portal_getconfiguration', methods: ['GET'])] - public function getConfigurationAction(Request $request): JsonResponse - { - $config = $this->dashboardHelper->getDashboard($request->query->get('key')); + public function getConfigurationAction( + GetDashboardConfigurationHandler $getDashboardConfiguration, + #[MapQueryParameter] ?string $key = null, + ): JsonResponse { + $result = $getDashboardConfiguration($key); - return $this->adminJson($config); + return $this->adminJson($result->config); } #[Route('/remove-widget', name: 'opendxp_admin_portal_removewidget', methods: ['DELETE'])] - public function removeWidgetAction(Request $request): JsonResponse + public function removeWidgetAction(Request $request, RemoveWidgetHandler $removeWidget): JsonResponse { - $dashboardId = $request->request->get('key'); - $config = $this->dashboardHelper->getDashboard($dashboardId); - - $newConfig = [[], []]; - $colCount = 0; - - $currentId = $request->request->has('id') ? (int) $request->request->get('id') : null; - - foreach ($config['positions'] as $col) { - foreach ($col as $row) { - if ($row['id'] !== $currentId) { - $newConfig[$colCount][] = $row; - } - } - $colCount++; - } - - $config['positions'] = $newConfig; + $widgetId = $request->request->has('id') ? (int) $request->request->get('id') : null; + $removeWidget( + dashboardId: (string) $request->request->get('key'), + widgetId: $widgetId, + ); - $this->dashboardHelper->saveDashboard($dashboardId, $config); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/add-widget', name: 'opendxp_admin_portal_addwidget', methods: ['POST'])] - public function addWidgetAction(Request $request): JsonResponse + public function addWidgetAction(Request $request, AddWidgetHandler $addWidget): JsonResponse { - $dashboardId = $request->request->get('key'); - $config = $this->dashboardHelper->getDashboard($dashboardId); - - $nextId = 0; - foreach ($config['positions'] as $col) { - foreach ($col as $row) { - $nextId = ($row['id'] > $nextId ? $row['id'] : $nextId); - } - } - - $nextId += 1; - $config['positions'][0][] = [ - 'id' => $nextId, - 'type' => $request->request->get('type'), - 'config' => null, - ]; + $result = $addWidget( + dashboardId: (string) $request->request->get('key'), + type: (string) $request->request->get('type'), + ); - $this->dashboardHelper->saveDashboard($dashboardId, $config); - - return $this->adminJson(['success' => true, 'id' => $nextId]); + return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } #[Route('/reorder-widget', name: 'opendxp_admin_portal_reorderwidget', methods: ['PUT'])] - public function reorderWidgetAction(Request $request): JsonResponse + public function reorderWidgetAction(Request $request, ReorderWidgetHandler $reorderWidget): JsonResponse { - $dashboardId = $request->request->get('key'); - $config = $this->dashboardHelper->getDashboard($dashboardId); - - $newConfig = [[], []]; - $colCount = 0; - $toMove = null; - - $currentId = $request->request->has('id') ? (int) $request->request->get('id') : null; - - foreach ($config['positions'] as $col) { - foreach ($col as $row) { - if ($row['id'] !== $currentId) { - $newConfig[$colCount][] = $row; - } else { - $toMove = $row; - } - } - $colCount++; - } - - array_splice($newConfig[$request->request->get('column')], $request->request->getInt('row'), 0, [$toMove]); - - $config['positions'] = $newConfig; - - $this->dashboardHelper->saveDashboard($dashboardId, $config); - - return $this->adminJson(['success' => true]); + $widgetId = $request->request->has('id') ? (int) $request->request->get('id') : null; + $reorderWidget( + dashboardId: (string) $request->request->get('key'), + widgetId: $widgetId, + column: $request->request->getInt('column'), + row: $request->request->getInt('row'), + ); + + return $this->adminJson(ApiResponse::ok()); } #[Route('/update-portlet-config', name: 'opendxp_admin_portal_updateportletconfig', methods: ['PUT'])] - public function updatePortletConfigAction(Request $request): JsonResponse + public function updatePortletConfigAction(Request $request, UpdatePortletConfigHandler $updatePortletConfig): JsonResponse { - $key = $request->request->get('key'); - - $currentId = $request->request->has('id') ? (int) $request->request->get('id') : null; - $configuration = $request->request->get('config'); - - $dashboard = $this->dashboardHelper->getDashboard($key); - foreach ($dashboard['positions'] as &$col) { - foreach ($col as &$portlet) { - if ($portlet['id'] === $currentId) { - $portlet['config'] = $configuration; - - break; - } - } - } - - $this->dashboardHelper->saveDashboard($key, $dashboard); - - return $this->adminJson(['success' => true]); + $portletId = $request->request->has('id') ? (int) $request->request->get('id') : null; + $updatePortletConfig( + dashboardKey: (string) $request->request->get('key'), + portletId: $portletId, + configuration: $request->request->get('config'), + ); + + return $this->adminJson(ApiResponse::ok()); } #[Route('/portlet-modified-documents', name: 'opendxp_admin_portal_portletmodifieddocuments', methods: ['GET'])] - public function portletModifiedDocumentsAction(Request $request): JsonResponse + public function portletModifiedDocumentsAction(GetModifiedDocumentsHandler $getModifiedDocuments): JsonResponse { - $list = Document::getList([ - 'limit' => 10, - 'order' => 'DESC', - 'orderKey' => 'modificationDate', - 'condition' => "userModification = '".$this->getAdminUser()->getId()."'", - ]); - - $response = []; - $response['documents'] = []; - - foreach ($list as $doc) { - if ($doc->isAllowed('view')) { - $response['documents'][] = [ - 'id' => $doc->getId(), - 'type' => $doc->getType(), - 'path' => $doc->getRealFullPath(), - 'date' => $doc->getModificationDate(), - ]; - } - } + $result = $getModifiedDocuments(); - return $this->adminJson($response); + return $this->adminJson(['documents' => $result->documents]); } #[Route('/portlet-modified-assets', name: 'opendxp_admin_portal_portletmodifiedassets', methods: ['GET'])] - public function portletModifiedAssetsAction(Request $request): JsonResponse + public function portletModifiedAssetsAction(GetModifiedAssetsHandler $getModifiedAssets): JsonResponse { - $list = Asset::getList([ - 'limit' => 10, - 'order' => 'DESC', - 'orderKey' => 'modificationDate', - 'condition' => "userModification = '".$this->getAdminUser()->getId()."'", - ]); - - $response = []; - $response['assets'] = []; - - foreach ($list as $doc) { - /** - * @var Asset $doc - */ - if ($doc->isAllowed('view')) { - $response['assets'][] = [ - 'id' => $doc->getId(), - 'type' => $doc->getType(), - 'path' => $doc->getRealFullPath(), - 'date' => $doc->getModificationDate(), - ]; - } - } + $result = $getModifiedAssets(); - return $this->adminJson($response); + return $this->adminJson(['assets' => $result->assets]); } #[Route('/portlet-modified-objects', name: 'opendxp_admin_portal_portletmodifiedobjects', methods: ['GET'])] - public function portletModifiedObjectsAction(Request $request): JsonResponse + public function portletModifiedObjectsAction(GetModifiedObjectsHandler $getModifiedObjects): JsonResponse { - $list = DataObject::getList([ - 'limit' => 10, - 'order' => 'DESC', - 'orderKey' => 'modificationDate', - 'condition' => "userModification = '".$this->getAdminUser()->getId()."'", - ]); - - $response = []; - $response['objects'] = []; - - foreach ($list as $object) { - if ($object->isAllowed('view')) { - $response['objects'][] = [ - 'id' => $object->getId(), - 'type' => $object->getType(), - 'path' => $object->getRealFullPath(), - 'date' => $object->getModificationDate(), - ]; - } - } + $result = $getModifiedObjects(); - return $this->adminJson($response); + return $this->adminJson(['objects' => $result->objects]); } #[Route('/portlet-modification-statistics', name: 'opendxp_admin_portal_portletmodificationstatistics', methods: ['GET'])] - public function portletModificationStatisticsAction(Request $request): JsonResponse + public function portletModificationStatisticsAction(GetModificationStatisticsHandler $getModificationStatistics): JsonResponse { - $db = \OpenDxp\Db::get(); - - $days = 31; - $startDate = mktime(23, 59, 59, (int) date('m'), (int) date('d'), (int) date('Y')); - - $data = []; - - for ($i = 0; $i < $days; $i++) { - // documents - $end = $startDate - ($i * 86400); - $start = $end - 86399; - - $o = $db->fetchOne( - 'SELECT COUNT(*) AS count FROM objects WHERE modificationDate > ? AND modificationDate < ?', - [$start, $end] - ); - $a = $db->fetchOne( - 'SELECT COUNT(*) AS count FROM assets WHERE modificationDate > ? AND modificationDate < ?', - [$start, $end] - ); - $d = $db->fetchOne( - 'SELECT COUNT(*) AS count FROM documents WHERE modificationDate > ? AND modificationDate < ?', - [$start, $end] - ); - - $date = new DateTime(); - $date->setTimestamp($start); - - $data[] = [ - 'timestamp' => $start, - 'datetext' => $date->format('Y-m-d'), - 'objects' => (int) $o, - 'documents' => (int) $d, - 'assets' => (int) $a, - ]; - } - - $data = array_reverse($data); - - return $this->adminJson(['data' => $data]); - } - - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } + $result = $getModificationStatistics(); - $this->dashboardHelper = new Dashboard($this->getAdminUser()); + return $this->adminJson(['data' => $result->data]); } } diff --git a/src/Controller/Admin/RecyclebinController.php b/src/Controller/Admin/RecyclebinController.php index 82c1d57c..b9dd7e25 100644 --- a/src/Controller/Admin/RecyclebinController.php +++ b/src/Controller/Admin/RecyclebinController.php @@ -1,5 +1,4 @@ value)] #[Route('/recyclebin/list', name: 'opendxp_admin_recyclebin_list', methods: ['POST'])] - public function listAction(Request $request): JsonResponse - { - if ($request->query->get('xaction') === 'destroy') { - $item = Recyclebin\Item::getById(\OpenDxp\Bundle\AdminBundle\Helper\QueryParams::getRecordIdForGridRequest($request->request->get('data'))); - - if ($item) { - $item->delete(); - } - - return $this->adminJson(['success' => true, 'data' => []]); - } - - $db = \OpenDxp\Db::get(); - - $list = new Recyclebin\Item\Listing(); - $list->setLimit((int) $request->request->get('limit', 50)); - $list->setOffset((int) $request->request->get('start', 0)); - - $list->setOrderKey('date'); - $list->setOrder('DESC'); - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings($request->request->all()); - if ($sortingSettings['orderKey']) { - $list->setOrderKey($sortingSettings['orderKey']); - $list->setOrder($sortingSettings['order']); - } - - $conditionFilters = []; - - if ($request->request->get('filterFullText')) { - $conditionFilters[] = '`path` LIKE ' . $list->quote('%'. $list->escapeLike($request->request->get('filterFullText')) .'%'); - } - - $filters = $request->request->get('filter'); - if ($filters) { - $filters = $this->decodeJson($filters); - - foreach ($filters as $filter) { - $operator = '='; - - $filterField = $filter['property']; - $filterOperator = $filter['operator']; - - if ($filter['type'] === 'string') { - $operator = 'LIKE'; - } elseif ($filter['type'] === 'numeric') { - if ($filterOperator === 'lt') { - $operator = '<'; - } elseif ($filterOperator === 'gt') { - $operator = '>'; - } elseif ($filterOperator === 'eq') { - $operator = '='; - } - } elseif ($filter['type'] === 'date') { - if ($filterOperator === 'lt') { - $operator = '<'; - } elseif ($filterOperator === 'gt') { - $operator = '>'; - } elseif ($filterOperator === 'eq') { - $operator = '='; - } - $filter['value'] = strtotime($filter['value']); - } elseif ($filter['type'] === 'list') { - $operator = '='; - } elseif ($filter['type'] === 'boolean') { - $operator = '='; - $filter['value'] = (int) $filter['value']; - } - // system field - $value = ($filter['value'] ?? ''); - if ($operator === 'LIKE') { - $value = '%' . $value . '%'; - } - - $field = $db->quoteIdentifier($filterField); - if (($filter['field'] ?? false) === 'fullpath') { - $field = 'CONCAT(`path`,filename)'; - } - - if ($filter['type'] === 'date' && $operator === '=') { - $maxTime = $value + (86400 - 1); //specifies the top point of the range used in the condition - $condition = $field . ' BETWEEN ' . $db->quote($value) . ' AND ' . $db->quote($maxTime); - $conditionFilters[] = $condition; - } else { - $conditionFilters[] = $field . $operator . ' ' . $db->quote($value); - } - } + public function listAction( + ListRecyclebinHandler $listRecyclebin, + DeleteRecyclebinItemHandler $deleteRecyclebinItem, + Request $request, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { + if ($xaction === 'destroy') { + $deleteRecyclebinItem(QueryParams::getRecordIdForGridRequest($request->request->get('data'))); + + return $this->adminJson(ApiResponse::ok(['data' => []])); } - if ($conditionFilters !== []) { - $condition = implode(' AND ', $conditionFilters); - $list->setCondition($condition); - } + $sortingSettings = QueryParams::extractSortingSettings($request->request->all()); + $orderKey = $sortingSettings['orderKey'] ?: 'date'; + $order = $sortingSettings['orderKey'] ? $sortingSettings['order'] : 'DESC'; - $items = $list->load(); - $data = []; - foreach ($items as $item) { - $data[] = $item->getObjectVars(); - } + $parsedFilters = $this->decodeJson($request->request->get('filter') ?? '[]'); - return $this->adminJson(['data' => $data, 'success' => true, 'total' => $list->getTotalCount()]); + $result = $listRecyclebin( + limit: (int) $request->request->get('limit', 50), + offset: (int) $request->request->get('start', 0), + orderKey: $orderKey, + order: $order, + filterFullText: $request->request->get('filterFullText') ?: null, + filters: $parsedFilters, + ); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } + #[IsGranted(CorePermission::Recyclebin->value)] #[Route('/recyclebin/restore', name: 'opendxp_admin_recyclebin_restore', methods: ['POST'])] - public function restoreAction(Request $request): JsonResponse - { - $item = Recyclebin\Item::getById((int) $request->request->get('id')); - if (!$item) { - throw $this->createNotFoundException(); - } - $item->restore(); + public function restoreAction( + RestoreRecyclebinItemHandler $restoreRecyclebinItem, + Request $request, + ): JsonResponse { + $restoreRecyclebinItem((int) $request->request->get('id')); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Recyclebin->value)] #[Route('/recyclebin/flush', name: 'opendxp_admin_recyclebin_flush', methods: ['DELETE'])] public function flushAction(): JsonResponse { $bin = new Element\Recyclebin(); $bin->flush(); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/recyclebin/add', name: 'opendxp_admin_recyclebin_add', methods: ['POST'])] - public function addAction(Request $request): JsonResponse - { - try { - $element = Element\Service::getElementById($request->request->get('type'), $request->request->getInt('id')); - - if ($element) { - $list = $element::getList(['unpublished' => true]); - $list->setCondition('`path` LIKE ' . $list->quote($list->escapeLike($element->getRealFullPath()) . '/%')); - $children = $list->getTotalCount(); - - if ($children <= 100) { - Recyclebin\Item::create($element, $this->getAdminUser()); - } - } - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - - return $this->adminJson(['success' => true]); + public function addAction( + AddToRecyclebinHandler $addToRecyclebin, + Request $request, + ): JsonResponse { + $addToRecyclebin( + type: $request->request->get('type'), + id: $request->request->getInt('id'), + ); + + return $this->adminJson(ApiResponse::ok()); } public function onKernelControllerEvent(ControllerEvent $event): void @@ -185,12 +111,8 @@ public function onKernelControllerEvent(ControllerEvent $event): void return; } - // recyclebin actions might take some time (save & restore) $timeout = 600; // 10 minutes @ini_set('max_execution_time', (string) $timeout); set_time_limit($timeout); - - // check permissions - $this->checkActionPermission($event, 'recyclebin', ['addAction']); } } diff --git a/src/Controller/Admin/Settings/ThumbnailController.php b/src/Controller/Admin/Settings/ThumbnailController.php new file mode 100644 index 00000000..ea2afa00 --- /dev/null +++ b/src/Controller/Admin/Settings/ThumbnailController.php @@ -0,0 +1,94 @@ +value)] +class ThumbnailController extends AdminAbstractController +{ + #[Route('/settings/thumbnail-tree', name: 'opendxp_admin_settings_thumbnailtree', methods: ['GET', 'POST'])] + public function thumbnailTreeAction(GetThumbnailTreeHandler $getThumbnailTree): JsonResponse + { + $result = $getThumbnailTree(); + + return $this->adminJson($result->nodes); + } + + #[Route('/settings/thumbnail-downloadable', name: 'opendxp_admin_settings_thumbnaildownloadable', methods: ['GET'])] + public function thumbnailDownloadableAction(GetDownloadableThumbnailsHandler $getDownloadableThumbnails): JsonResponse + { + $result = $getDownloadableThumbnails(); + + return $this->adminJson($result->thumbnails); + } + + #[Route('/settings/thumbnail-add', name: 'opendxp_admin_settings_thumbnailadd', methods: ['POST'])] + public function thumbnailAddAction(Request $request, AddThumbnailHandler $addThumbnail): JsonResponse + { + $result = $addThumbnail($request->request->get('name')); + + return $this->adminJson(ApiResponse::fromBool($result->created, ['id' => $result->id])); + } + + #[Route('/settings/thumbnail-delete', name: 'opendxp_admin_settings_thumbnaildelete', methods: ['DELETE'])] + public function thumbnailDeleteAction(Request $request, DeleteThumbnailHandler $deleteThumbnail): JsonResponse + { + $deleteThumbnail($request->request->get('name')); + + return $this->adminJson(ApiResponse::ok()); + } + + #[Route('/settings/thumbnail-get', name: 'opendxp_admin_settings_thumbnailget', methods: ['GET'])] + public function thumbnailGetAction( + #[MapQueryParameter] string $name, + GetThumbnailHandler $getThumbnail, + ): JsonResponse { + $result = $getThumbnail($name); + + return $this->adminJson($result->data); + } + + #[Route('/settings/thumbnail-update', name: 'opendxp_admin_settings_thumbnailupdate', methods: ['PUT'])] + public function thumbnailUpdateAction(Request $request, UpdateThumbnailHandler $updateThumbnail): JsonResponse + { + $updateThumbnail( + name: $request->request->get('name'), + settingsData: $this->decodeJson($request->request->get('settings')), + mediaData: $this->decodeJson($request->request->get('medias')), + mediaOrder: $this->decodeJson($request->request->get('mediaOrder')), + ); + + return $this->adminJson(ApiResponse::ok()); + } +} diff --git a/src/Controller/Admin/Settings/VideoThumbnailController.php b/src/Controller/Admin/Settings/VideoThumbnailController.php new file mode 100644 index 00000000..e2fcbab6 --- /dev/null +++ b/src/Controller/Admin/Settings/VideoThumbnailController.php @@ -0,0 +1,110 @@ +value)] +class VideoThumbnailController extends AdminAbstractController +{ + #[Route('/settings/video-thumbnail-adapter-check', name: 'opendxp_admin_settings_videothumbnailadaptercheck', methods: ['GET'])] + public function videoThumbnailAdapterCheckAction(TranslatorInterface $translator): Response + { + $content = ''; + + if (!\OpenDxp\Video::isAvailable()) { + $content = '' . + $translator->trans('php_cli_binary_and_or_ffmpeg_binary_setting_is_missing', [], 'admin') . + ''; + } + + return new Response($content); + } + + #[Route('/settings/video-thumbnail-tree', name: 'opendxp_admin_settings_videothumbnailtree', methods: ['GET', 'POST'])] + public function videoThumbnailTreeAction(GetVideoThumbnailTreeHandler $getVideoThumbnailTree): JsonResponse + { + $result = $getVideoThumbnailTree(); + + return $this->adminJson($result->nodes); + } + + #[Route('/settings/video-thumbnail-list', name: 'opendxp_admin_settings_videothumbnail_list', methods: ['GET'])] + public function videoThumbnailListAction(GetVideoThumbnailListHandler $getVideoThumbnailList): JsonResponse + { + $result = $getVideoThumbnailList(); + + return $this->adminJson($result->thumbnails); + } + + #[Route('/settings/video-thumbnail-add', name: 'opendxp_admin_settings_videothumbnailadd', methods: ['POST'])] + public function videoThumbnailAddAction(Request $request, AddVideoThumbnailHandler $addVideoThumbnail): JsonResponse + { + $result = $addVideoThumbnail($request->request->get('name')); + + return $this->adminJson(ApiResponse::fromBool($result->created, ['id' => $result->id])); + } + + #[Route('/settings/video-thumbnail-delete', name: 'opendxp_admin_settings_videothumbnaildelete', methods: ['DELETE'])] + public function videoThumbnailDeleteAction(Request $request, DeleteVideoThumbnailHandler $deleteVideoThumbnail): JsonResponse + { + $deleteVideoThumbnail($request->request->get('name')); + + return $this->adminJson(ApiResponse::ok()); + } + + #[Route('/settings/video-thumbnail-get', name: 'opendxp_admin_settings_videothumbnailget', methods: ['GET'])] + public function videoThumbnailGetAction( + #[MapQueryParameter] string $name, + GetVideoThumbnailHandler $getVideoThumbnail, + ): JsonResponse { + $result = $getVideoThumbnail($name); + + return $this->adminJson($result->data); + } + + #[Route('/settings/video-thumbnail-update', name: 'opendxp_admin_settings_videothumbnailupdate', methods: ['PUT'])] + public function videoThumbnailUpdateAction(Request $request, UpdateVideoThumbnailHandler $updateVideoThumbnail): JsonResponse + { + $updateVideoThumbnail( + name: $request->request->get('name'), + settingsData: $this->decodeJson($request->request->get('settings')), + mediaData: $this->decodeJson($request->request->get('medias')), + mediaOrder: $this->decodeJson($request->request->get('mediaOrder')), + ); + + return $this->adminJson(ApiResponse::ok()); + } +} diff --git a/src/Controller/Admin/SettingsController.php b/src/Controller/Admin/SettingsController.php index 1195d12a..ac8c0142 100644 --- a/src/Controller/Admin/SettingsController.php +++ b/src/Controller/Admin/SettingsController.php @@ -1,4 +1,5 @@ query->has('white')) { - $logo = OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/logo-claim-white.svg'; - } else { - $logo = OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/logo-claim-gray.svg'; - } - - $stream = fopen($logo, 'rb'); + $result = $handler(white: $request->query->has('white')); - $storage = Tool\Storage::get('admin'); - if ($storage->fileExists(self::CUSTOM_LOGO_PATH)) { - try { - $mime = $storage->mimeType(self::CUSTOM_LOGO_PATH); - $stream = $storage->readStream(self::CUSTOM_LOGO_PATH); - } catch (Exception) { - // do nothing - } - } - - return new StreamedResponse(function () use ($stream): void { - fpassthru($stream); + return new StreamedResponse(static function () use ($result): void { + fpassthru($result->stream); }, 200, [ - 'Content-Type' => $mime, + 'Content-Type' => $result->mime, 'Content-Security-Policy' => "script-src 'none'", ]); } - /** - * @throws Exception - */ #[Route('/upload-custom-logo', name: 'opendxp_admin_settings_uploadcustomlogo', methods: ['POST'])] - public function uploadCustomLogoAction(Request $request): JsonResponse + public function uploadCustomLogoAction(Request $request, UploadCustomLogoHandler $handler): JsonResponse { + /** @var UploadedFile $logoFile */ $logoFile = $request->files->get('Filedata'); - if (!$logoFile instanceof UploadedFile - || !in_array($logoFile->guessExtension(), ['svg', 'png', 'jpg']) - ) { - throw new Exception('Unsupported file format.'); + if (!$logoFile instanceof UploadedFile) { + throw new BadRequestHttpException('No file uploaded.'); } - $storage = Tool\Storage::get('admin'); - $storage->writeStream(self::CUSTOM_LOGO_PATH, fopen($logoFile->getPathname(), 'rb')); - - // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in - // Ext.form.Action.Submit and mark the submission as failed + $handler($logoFile->getPathname(), $logoFile->guessExtension() ?? ''); - $response = $this->adminJson(['success' => true]); + $response = $this->adminJson(ApiResponse::ok()); $response->headers->set('Content-Type', 'text/html'); return $response; } #[Route('/delete-custom-logo', name: 'opendxp_admin_settings_deletecustomlogo', methods: ['DELETE'])] - public function deleteCustomLogoAction(Request $request): JsonResponse + public function deleteCustomLogoAction(DeleteCustomLogoHandler $handler): JsonResponse { - if (Tool\Storage::get('admin')->fileExists(self::CUSTOM_LOGO_PATH)) { - Tool\Storage::get('admin')->delete(self::CUSTOM_LOGO_PATH); - } + $handler(); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } - /** - * Used by the predefined metadata grid - */ + #[IsGranted(CorePermission::AssetMetadata->value)] #[Route('/predefined-metadata', name: 'opendxp_admin_settings_metadata', methods: ['POST'])] - public function metadataAction(Request $request): JsonResponse - { - $this->checkPermission('asset_metadata'); - + public function metadataAction( + Request $request, + GetPredefinedMetadataListHandler $getPredefinedMetadataList, + CreatePredefinedMetadataHandler $createPredefinedMetadata, + UpdatePredefinedMetadataHandler $updatePredefinedMetadata, + DeletePredefinedMetadataHandler $deletePredefinedMetadata, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { if ($request->request->has('data')) { - if ($request->query->get('xaction') === 'destroy') { - $data = $this->decodeJson($request->request->get('data')); - $id = $data['id']; - $metadata = Metadata\Predefined::getById($id); - if (!$metadata->isWriteable()) { - throw new ConfigWriteException(); - } - $metadata->delete(); + $data = $this->decodeJson($request->request->get('data')); - return $this->adminJson(['success' => true, 'data' => []]); + if ($xaction === 'destroy') { + $deletePredefinedMetadata((string) $data['id']); + + return $this->adminJson(ApiResponse::ok(['data' => []])); } - if ($request->query->get('xaction') === 'update') { - $data = $this->decodeJson($request->request->get('data')); - // save type - $metadata = Metadata\Predefined::getById($data['id']); - if (!$metadata->isWriteable()) { - throw new ConfigWriteException(); - } - $metadata->setValues($data); - $existingItem = Metadata\Predefined\Listing::getByKeyAndLanguage($metadata->getName(), $metadata->getLanguage(), $metadata->getTargetSubtype()); - if ($existingItem && $existingItem->getId() !== $metadata->getId()) { - return $this->adminJson(['message' => 'predefined_metadata_definitions_error_name_exists_msg', 'success' => false]); - } - $metadata->minimize(); - $metadata->save(); - $metadata->expand(); - $responseData = $metadata->getObjectVars(); - $responseData['writeable'] = $metadata->isWriteable(); + if ($xaction === 'update') { + $result = $updatePredefinedMetadata($data); - return $this->adminJson(['data' => $responseData, 'success' => true]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } - if ($request->query->get('xaction') === 'create') { - if (!(new Metadata\Predefined())->isWriteable()) { - throw new ConfigWriteException(); - } - $data = $this->decodeJson($request->request->get('data')); + if ($xaction === 'create') { unset($data['id']); - // save type - $metadata = Metadata\Predefined::create(); - $metadata->setValues($data); - $existingItem = Metadata\Predefined\Listing::getByKeyAndLanguage($metadata->getName(), $metadata->getLanguage(), $metadata->getTargetSubtype()); - if ($existingItem) { - return $this->adminJson(['message' => 'rule_violation', 'success' => false]); - } - $metadata->save(); - $responseData = $metadata->getObjectVars(); - $responseData['writeable'] = $metadata->isWriteable(); + $result = $createPredefinedMetadata($data); - return $this->adminJson(['data' => $responseData, 'success' => true]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } } else { - // get list of types - $list = new Metadata\Predefined\Listing(); - - if ($filter = $request->request->get('filter')) { - $list->setFilter(function (Metadata\Predefined $predefined) use ($filter) { - foreach ($predefined->getObjectVars() as $value) { - if (stripos((string)$value, (string) $filter) !== false) { - return true; - } - } + $result = $getPredefinedMetadataList($request->request->get('filter')); - return false; - }); - } - - $properties = []; - foreach ($list->getDefinitions() as $metadata) { - $metadata->expand(); - $data = $metadata->getObjectVars(); - $data['writeable'] = $metadata->isWriteable(); - $properties[] = $data; - } - - return $this->adminJson(['data' => $properties, 'success' => true, 'total' => $list->getTotalCount()]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - return $this->adminJson(['success' => false]); + throw new BadRequestHttpException(); } #[Route('/get-predefined-metadata', name: 'opendxp_admin_settings_getpredefinedmetadata', methods: ['GET'])] - public function getPredefinedMetadataAction(Request $request): JsonResponse - { - $type = $request->query->get('type'); - $subType = $request->query->get('subType'); - $group = $request->query->get('group'); - $list = Metadata\Predefined\Listing::getByTargetType($type, [$subType]); - $result = []; - foreach ($list as $item) { - $itemGroup = $item->getGroup() ?? ''; - if ($group === 'default' || $group === $itemGroup) { - $item->expand(); - $data = $item->getObjectVars(); - $data['writeable'] = $item->isWriteable(); - $result[] = $data; - } - } - - return $this->adminJson(['data' => $result, 'success' => true]); + public function getPredefinedMetadataAction( + GetFilteredPredefinedMetadataHandler $handler, + #[MapQueryParameter] ?string $type = null, + #[MapQueryParameter] ?string $subType = null, + #[MapQueryParameter] ?string $group = null, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $handler($type, $subType, $group)->data])); } + #[IsGranted(CorePermission::PredefinedProperties->value)] #[Route('/properties', name: 'opendxp_admin_settings_properties', methods: ['POST'])] - public function propertiesAction(Request $request): JsonResponse - { - $this->checkPermission('predefined_properties'); - + public function propertiesAction( + Request $request, + GetPredefinedPropertiesListHandler $getPredefinedPropertiesList, + CreatePredefinedPropertyHandler $createPredefinedProperty, + UpdatePredefinedPropertyHandler $updatePredefinedProperty, + DeletePredefinedPropertyHandler $deletePredefinedProperty, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { if ($request->request->has('data')) { - if ($request->query->get('xaction') === 'destroy') { - $data = $this->decodeJson($request->request->get('data')); - $id = $data['id']; - $property = Property\Predefined::getById($id); - if (!$property->isWriteable()) { - throw new ConfigWriteException(); - } - $property->delete(); + $data = $this->decodeJson($request->request->get('data')); - return $this->adminJson(['success' => true, 'data' => []]); + if ($xaction === 'destroy') { + $deletePredefinedProperty((string) $data['id']); + + return $this->adminJson(ApiResponse::ok(['data' => []])); } - if ($request->query->get('xaction') === 'update') { - $data = $this->decodeJson($request->request->get('data')); - // save type - $property = Property\Predefined::getById($data['id']); - if (!$property->isWriteable()) { - throw new ConfigWriteException(); - } - if (is_array($data['ctype'])) { - $data['ctype'] = implode(',', $data['ctype']); - } - $property->setValues($data); - $property->save(); - $responseData = $property->getObjectVars(); - $responseData['writeable'] = $property->isWriteable(); + if ($xaction === 'update') { + $result = $updatePredefinedProperty($data); - return $this->adminJson(['data' => $responseData, 'success' => true]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } - if ($request->query->get('xaction') === 'create') { - if (!(new Property\Predefined())->isWriteable()) { - throw new ConfigWriteException(); - } - $data = $this->decodeJson($request->request->get('data')); + if ($xaction === 'create') { unset($data['id']); - // save type - $property = Property\Predefined::create(); - $property->setValues($data); - $property->save(); - $responseData = $property->getObjectVars(); - $responseData['writeable'] = $property->isWriteable(); - - return $this->adminJson(['data' => $responseData, 'success' => true]); - } - } else { - // get list of types - $list = new Property\Predefined\Listing(); - - if ($filter = $request->request->get('filter')) { - $list->setFilter(function (Property\Predefined $predefined) use ($filter) { - foreach ($predefined->getObjectVars() as $value) { - if ($value) { - $cellValues = is_array($value) ? $value : [$value]; - - foreach ($cellValues as $cellValue) { - if (stripos((string)$cellValue, (string) $filter) !== false) { - return true; - } - } - } - } - - return false; - }); - } + $result = $createPredefinedProperty($data); - $properties = []; - foreach ($list->getProperties() as $property) { - $data = $property->getObjectVars(); - $data['writeable'] = $property->isWriteable(); - $properties[] = $data; + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } + } else { + $result = $getPredefinedPropertiesList($request->request->get('filter')); - return $this->adminJson(['data' => $properties, 'success' => true, 'total' => $list->getTotalCount()]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - return $this->adminJson(['success' => false]); + throw new BadRequestHttpException(); } + #[IsGranted(AdminPermission::SystemAppearance->value)] #[Route('/get-admin-system', name: 'opendxp_appearance_admin_settings_get', methods: ['GET'])] - public function getAppearanceSystemAction(AdminConfig $config): JsonResponse + public function getAppearanceSystemAction(GetAppearanceSettingsHandler $handler): JsonResponse { - $this->checkPermission('system_appearance_settings'); - $config = $config->getAdminSystemSettingsConfig(); - - $response = [ - 'values' => $config, - ]; - - return $this->adminJson($response); + return $this->adminJson(['values' => $handler()->values]); } + #[IsGranted(CorePermission::SystemSettings->value)] #[Route('/get-system', name: 'opendxp_admin_settings_getsystem', methods: ['GET'])] - public function getSystemAction(Request $request, SystemSettingsConfig $config): JsonResponse + public function getSystemAction(GetSystemSettingsHandler $handler): JsonResponse { - $this->checkPermission('system_settings'); - $config = $config->getSystemSettingsConfig(); + $result = $handler(); - // If required languages is empty it's the same as if all langauges are required. Therefore, we - // need to overwrite the value with the valid languages value to have all languages required - if (empty($config['general']['required_languages'])) { - $config['general']['required_languages'] = $config['general']['valid_languages']; - } - - $valueArray = [ - 'general' => $config['general'], - 'documents' => $config['documents'], - 'assets' => $config['assets'], - 'objects' => $config['objects'], - 'email' => $config['email'], - 'writeable' => $config['writeable'], - ]; - - $locales = Tool::getSupportedLocales(); - $languageOptions = []; - $validLanguages = []; - foreach ($locales as $short => $translation) { - if (!empty($short)) { - $languageOptions[] = [ - 'language' => $short, - 'display' => $translation . " ($short)", - ]; - $validLanguages[] = $short; - } - } - - //for "wrong" legacy values - foreach ($valueArray['general']['valid_languages'] as $existingValue) { - if (!in_array($existingValue, $validLanguages)) { - $languageOptions[] = [ - 'language' => $existingValue, - 'display' => $existingValue, - ]; - } - } - - $response = [ - 'values' => $valueArray, - 'config' => [ - 'languages' => $languageOptions, - ], - ]; - - return $this->adminJson($response); + return $this->adminJson([ + 'values' => $result->values, + 'config' => ['languages' => $result->languages], + ]); } + #[IsGranted(AdminPermission::SystemAppearance->value)] #[Route('/set-appearance', name: 'opendxp_admin_settings_appearance_set', methods: ['PUT'])] public function setAppearanceSystemAction( Request $request, KernelInterface $kernel, - EventDispatcherInterface $eventDispatcher, - CoreCacheHandler $cache, - Filesystem $filesystem, - CacheClearer $symfonyCacheClearer, - AdminConfig $config + SaveAppearanceSettingsHandler $handler, ): JsonResponse { - $this->checkPermission('system_appearance_settings'); - - $values = $this->decodeJson($request->request->get('data')); + $handler( + values: $this->decodeJson($request->request->get('data')), + env: $request->request->get('env', $kernel->getEnvironment()), + ); - $config->save($values); - - // clear all caches - $this->clearSymfonyCache($request, $kernel, $eventDispatcher, $symfonyCacheClearer); - $this->stopMessengerWorkers(); - - $eventDispatcher->addListener(KernelEvents::TERMINATE, function (TerminateEvent $event) use ( - $cache, $eventDispatcher, $filesystem - ): void { - // we need to clear the cache with a delay, because the cache is used by messenger:stop-workers - // to send the stop signal to all worker processes - sleep(2); - $this->clearOpenDxpCache($cache, $eventDispatcher, $filesystem); - }); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::SystemSettings->value)] #[Route('/set-system', name: 'opendxp_admin_settings_setsystem', methods: ['PUT'])] public function setSystemAction( Request $request, KernelInterface $kernel, - EventDispatcherInterface $eventDispatcher, - CoreCacheHandler $cache, - Filesystem $filesystem, - CacheClearer $symfonyCacheClearer, - SystemSettingsConfig $config + SaveSystemSettingsHandler $handler, ): JsonResponse { - $this->checkPermission('system_settings'); - - $values = $this->decodeJson($request->request->get('data')); + $handler( + values: $this->decodeJson($request->request->get('data')), + env: $request->request->get('env', $kernel->getEnvironment()), + ); - $config->save($values); - - // clear all caches - $this->clearSymfonyCache($request, $kernel, $eventDispatcher, $symfonyCacheClearer); - $this->stopMessengerWorkers(); - - $eventDispatcher->addListener(KernelEvents::TERMINATE, function (TerminateEvent $event) use ( - $cache, $eventDispatcher, $filesystem - ): void { - // we need to clear the cache with a delay, because the cache is used by messenger:stop-workers - // to send the stop signal to all worker processes - sleep(2); - $this->clearOpenDxpCache($cache, $eventDispatcher, $filesystem); - }); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(new Expression('is_granted("clear_cache") or is_granted("system_settings")'))] #[Route('/clear-cache', name: 'opendxp_admin_settings_clearcache', methods: ['DELETE'])] public function clearCacheAction( Request $request, KernelInterface $kernel, - EventDispatcherInterface $eventDispatcher, - CoreCacheHandler $cache, - Filesystem $filesystem, - CacheClearer $symfonyCacheClearer + ClearOpenDxpCacheHandler $clearOpenDxpCache, + ClearSymfonyCacheHandler $clearSymfonyCache, ): JsonResponse { - $this->checkPermissionsHasOneOf(['clear_cache', 'system_settings']); - - $result = [ - 'success' => true, - ]; - - $clearOpenDxpCache = !(bool)$request->request->get('only_symfony_cache'); - $clearSymfonyCache = !(bool)$request->request->get('only_opendxp_cache'); + $shouldClearOpenDxp = !(bool) $request->request->get('only_symfony_cache'); + $shouldClearSymfony = !(bool) $request->request->get('only_opendxp_cache'); - if ($clearOpenDxpCache) { - $this->clearOpenDxpCache($cache, $eventDispatcher, $filesystem); + if ($shouldClearOpenDxp) { + $clearOpenDxpCache(); } - if ($clearSymfonyCache) { - $this->clearSymfonyCache($request, $kernel, $eventDispatcher, $symfonyCacheClearer); + if ($shouldClearSymfony) { + $clearSymfonyCache($request->request->get('env', $kernel->getEnvironment())); } - $response = new JsonResponse($result); + $response = new JsonResponse(ApiResponse::ok()); - if ($clearSymfonyCache) { - // we send the response directly here and exit to make sure no code depending on the stale container - // is running after this + if ($shouldClearSymfony) { + // send response before exit so the client gets a reply before the process terminates $response->sendHeaders(); $response->sendContent(); exit; @@ -500,552 +280,70 @@ public function clearCacheAction( return $response; } - private function clearOpenDxpCache( - CoreCacheHandler $cache, - EventDispatcherInterface $eventDispatcher, - Filesystem $filesystem, - ): void { - // empty document cache - $cache->clearAll(); - - if ($filesystem->exists(OPENDXP_CACHE_DIRECTORY)) { - $filesystem->remove(OPENDXP_CACHE_DIRECTORY); - } - - $filesystem->dumpFile(OPENDXP_CACHE_DIRECTORY . '/.gitkeep', ''); - - $eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR); - } - - private function clearSymfonyCache( - Request $request, - KernelInterface $kernel, - EventDispatcherInterface $eventDispatcher, - CacheClearer $symfonyCacheClearer, - ): void { - - // if no env is passed it will use the current one - $environment = $request->request->get('env', $kernel->getEnvironment()); - - if ($kernel->getEnvironment() === $environment) { - // remove terminate and exception event listeners for the current env as they break with a - // cleared container - see #2434 - foreach ($eventDispatcher->getListeners(KernelEvents::TERMINATE) as $listener) { - $eventDispatcher->removeListener(KernelEvents::TERMINATE, $listener); - } - - foreach ($eventDispatcher->getListeners(KernelEvents::EXCEPTION) as $listener) { - $eventDispatcher->removeListener(KernelEvents::EXCEPTION, $listener); - } - } - - $symfonyCacheClearer->clear($environment); - } - + #[IsGranted(CorePermission::ClearFullpageCache->value)] #[Route('/clear-output-cache', name: 'opendxp_admin_settings_clearoutputcache', methods: ['DELETE'])] - public function clearOutputCacheAction(EventDispatcherInterface $eventDispatcher): JsonResponse + public function clearOutputCacheAction(ClearOutputCacheHandler $handler): JsonResponse { - $this->checkPermission('clear_fullpage_cache'); - - // remove "output" out of the ignored tags, if a cache lifetime is specified - Cache::removeIgnoredTagOnClear('output'); - - // empty document cache - Cache::clearTags(['output', 'output_lifetime']); + $handler(); - $eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR_FULLPAGE_CACHE); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::ClearTempFiles->value)] #[Route('/clear-temporary-files', name: 'opendxp_admin_settings_cleartemporaryfiles', methods: ['DELETE'])] - public function clearTemporaryFilesAction(EventDispatcherInterface $eventDispatcher): JsonResponse + public function clearTemporaryFilesAction(ClearTemporaryFilesHandler $handler): JsonResponse { - $this->checkPermission('clear_temp_files'); - - // public files - Tool\Storage::get('thumbnail')->deleteDirectory('/'); - Db::get()->executeStatement('TRUNCATE TABLE assets_image_thumbnail_cache'); - - Tool\Storage::get('asset_cache')->deleteDirectory('/'); - - // system files - FileSystemHelper::recursiveDelete(OPENDXP_SYSTEM_TEMP_DIRECTORY, false); + $handler(); - $eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR_TEMPORARY_FILES); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/get-available-admin-languages', name: 'opendxp_admin_settings_getavailableadminlanguages', methods: ['GET'])] - public function getAvailableAdminLanguagesAction(Request $request): JsonResponse + public function getAvailableAdminLanguagesAction(GetAvailableAdminLanguagesHandler $handler): JsonResponse { - $langs = []; - $availableLanguages = Tool\Admin::getLanguages(); - $locales = Tool::getSupportedLocales(); - - foreach ($availableLanguages as $lang) { - if (array_key_exists($lang, $locales)) { - $langs[] = [ - 'language' => $lang, - 'display' => $locales[$lang], - ]; - } - } - - usort($langs, fn ($a, $b) => strcmp($a['display'], $b['display'])); - - return $this->adminJson($langs); + return $this->adminJson($handler()->langs); } #[Route('/get-available-sites', name: 'opendxp_admin_settings_getavailablesites', methods: ['GET'])] - public function getAvailableSitesAction(Request $request): JsonResponse - { + public function getAvailableSitesAction( + GetAvailableSitesHandler $handler, + #[MapQueryParameter] ?string $excludeMainSite = null, + ): JsonResponse { try { - // we need to check documents permission for listing purposes in sites ext model & url-slugs $this->checkPermission('documents'); } catch (AccessDeniedHttpException) { Logger::log('[Startup] Sites are not loaded as "documents" permission is missing'); - //return empty string to avoid error on startup return $this->adminJson([]); } - $excludeMainSite = $request->query->get('excludeMainSite'); - - $sitesList = new Model\Site\Listing(); - $sitesObjects = $sitesList->load(); - $sites = []; - if (!$excludeMainSite) { - $sites[] = [ - 'id' => 0, - 'rootId' => 1, - 'domains' => '', - 'rootPath' => '/', - 'domain' => $this->translator->trans('main_site', [], 'admin'), - ]; - } - - foreach ($sitesObjects as $site) { - if ($site->getRootDocument()) { - if ($site->getMainDomain()) { - $sites[] = [ - 'id' => $site->getId(), - 'rootId' => $site->getRootId(), - 'domains' => implode(',', $site->getDomains()), - 'rootPath' => $site->getRootPath(), - 'domain' => $site->getMainDomain(), - ]; - } - } else { - // site is useless, parent doesn't exist anymore - $site->delete(); - } - } - - return $this->adminJson($sites); + return $this->adminJson($handler(excludeMainSite: (bool) $excludeMainSite)->sites); } #[Route('/get-available-countries', name: 'opendxp_admin_settings_getavailablecountries', methods: ['GET'])] - public function getAvailableCountriesAction(LocaleServiceInterface $localeService): JsonResponse + public function getAvailableCountriesAction(GetAvailableCountriesHandler $handler): JsonResponse { - $countries = $localeService->getDisplayRegions(); - asort($countries); - - $options = []; - - foreach ($countries as $short => $translation) { - if (strlen($short) === 2) { - $options[] = [ - 'key' => $translation . ' (' . $short . ')', - 'value' => $short, - ]; - } - } + $result = $handler(); - $result = ['data' => $options, 'success' => true, 'total' => count($options)]; - - return $this->adminJson($result); + return $this->adminJson(ApiResponse::ok(['data' => $result->options, 'total' => count($result->options)])); } #[Route('/thumbnail-adapter-check', name: 'opendxp_admin_settings_thumbnailadaptercheck', methods: ['GET'])] - public function thumbnailAdapterCheckAction(Request $request, TranslatorInterface $translator): Response - { - $content = ''; - - $instance = \OpenDxp\Image::getInstance(); - if ($instance instanceof \OpenDxp\Image\Adapter\GD) { - $content = '' . - $translator->trans('important_use_imagick_pecl_extensions_for_best_results_gd_is_just_a_fallback_with_less_quality', [], 'admin') . - ''; - } - - return new Response($content); - } - - #[Route('/thumbnail-tree', name: 'opendxp_admin_settings_thumbnailtree', methods: ['GET', 'POST'])] - public function thumbnailTreeAction(): JsonResponse - { - $this->checkPermission('thumbnails'); - - $thumbnails = []; - - $list = new Asset\Image\Thumbnail\Config\Listing(); - - $groups = []; - foreach ($list->getThumbnails() as $item) { - if ($item->getGroup()) { - if (empty($groups[$item->getGroup()])) { - $groups[$item->getGroup()] = [ - 'id' => 'group_' . $item->getName(), - 'text' => htmlspecialchars($item->getGroup()), - 'expandable' => true, - 'leaf' => false, - 'allowChildren' => true, - 'iconCls' => 'opendxp_icon_folder', - 'group' => $item->getGroup(), - 'children' => [], - ]; - } - $groups[$item->getGroup()]['children'][] = - [ - 'id' => $item->getName(), - 'text' => $item->getName(), - 'leaf' => true, - 'iconCls' => 'opendxp_icon_thumbnails', - 'cls' => 'opendxp_treenode_disabled', - 'writeable' => $item->isWriteable(), - ]; - } else { - $thumbnails[] = [ - 'id' => $item->getName(), - 'text' => $item->getName(), - 'leaf' => true, - 'iconCls' => 'opendxp_icon_thumbnails', - 'cls' => 'opendxp_treenode_disabled', - 'writeable' => $item->isWriteable(), - ]; - } - } - - foreach ($groups as $group) { - $thumbnails[] = $group; - } - - return $this->adminJson($thumbnails); - } - - #[Route('/thumbnail-downloadable', name: 'opendxp_admin_settings_thumbnaildownloadable', methods: ['GET'])] - public function thumbnailDownloadableAction(): JsonResponse - { - $thumbnails = []; - - $list = new Asset\Image\Thumbnail\Config\Listing(); - $list->setFilter(fn (Asset\Image\Thumbnail\Config $config) => $config->isDownloadable()); - - foreach ($list->getThumbnails() as $item) { - $thumbnails[] = [ - 'id' => $item->getName(), - 'text' => $item->getName(), - ]; - } - - return $this->adminJson($thumbnails); - } - - #[Route('/thumbnail-add', name: 'opendxp_admin_settings_thumbnailadd', methods: ['POST'])] - public function thumbnailAddAction(Request $request): JsonResponse - { - $this->checkPermission('thumbnails'); - - $success = false; - - $pipe = Asset\Image\Thumbnail\Config::getByName($request->request->get('name')); - - if (!$pipe) { - $pipe = new Asset\Image\Thumbnail\Config(); - if (!$pipe->isWriteable()) { - throw new ConfigWriteException(); - } - $pipe->setName($request->request->get('name')); - $pipe->save(); - $success = true; - } elseif (!$pipe->isWriteable()) { - throw new ConfigWriteException(); - } - - return $this->adminJson(['success' => $success, 'id' => $pipe->getName()]); - } - - #[Route('/thumbnail-delete', name: 'opendxp_admin_settings_thumbnaildelete', methods: ['DELETE'])] - public function thumbnailDeleteAction(Request $request): JsonResponse - { - $this->checkPermission('thumbnails'); - - $pipe = Asset\Image\Thumbnail\Config::getByName($request->request->get('name')); - - if (!$pipe->isWriteable()) { - throw new ConfigWriteException(); - } - - $pipe->delete(); - - return $this->adminJson(['success' => true]); - } - - #[Route('/thumbnail-get', name: 'opendxp_admin_settings_thumbnailget', methods: ['GET'])] - public function thumbnailGetAction(Request $request): JsonResponse - { - $this->checkPermission('thumbnails'); - - $pipe = Asset\Image\Thumbnail\Config::getByName($request->query->get('name')); - $data = $pipe->getObjectVars(); - $data['writeable'] = $pipe->isWriteable(); - - return $this->adminJson($data); - } - - #[Route('/thumbnail-update', name: 'opendxp_admin_settings_thumbnailupdate', methods: ['PUT'])] - public function thumbnailUpdateAction(Request $request): JsonResponse - { - $this->checkPermission('thumbnails'); - - $pipe = Asset\Image\Thumbnail\Config::getByName($request->request->get('name')); - - if (!$pipe->isWriteable()) { - throw new ConfigWriteException(); - } - - $settingsData = $this->decodeJson($request->request->get('settings')); - $mediaData = $this->decodeJson($request->request->get('medias')); - $mediaOrder = $this->decodeJson($request->request->get('mediaOrder')); - - foreach ($settingsData as $key => $value) { - $setter = 'set' . ucfirst($key); - if (method_exists($pipe, $setter)) { - $pipe->$setter($value); - } - } - - $pipe->resetItems(); - - uksort($mediaData, static function ($a, $b) use ($mediaOrder) { - if ($a === 'default') { - return -1; - } - - return ($mediaOrder[$a] < $mediaOrder[$b]) ? -1 : 1; - }); - - foreach ($mediaData as $mediaName => $items) { - if (preg_match('/["<>]/', $mediaName)) { - throw new Exception('Invalid media query name'); - } - - foreach ($items as $item) { - $type = $item['type']; - unset($item['type']); - - $pipe->addItem($type, $item, $mediaName); - } - } - - $pipe->save(); - - return $this->adminJson(['success' => true]); - } - - #[Route('/video-thumbnail-adapter-check', name: 'opendxp_admin_settings_videothumbnailadaptercheck', methods: ['GET'])] - public function videoThumbnailAdapterCheckAction(Request $request, TranslatorInterface $translator): Response - { - $content = ''; - - if (!\OpenDxp\Video::isAvailable()) { - $content = '' . - $translator->trans('php_cli_binary_and_or_ffmpeg_binary_setting_is_missing', [], 'admin') . - ''; - } - - return new Response($content); - } - - #[Route('/video-thumbnail-tree', name: 'opendxp_admin_settings_videothumbnailtree', methods: ['GET', 'POST'])] - public function videoThumbnailTreeAction(): JsonResponse - { - $this->checkPermission('thumbnails'); - - $thumbnails = []; - - $list = new Asset\Video\Thumbnail\Config\Listing(); - - $groups = []; - foreach ($list->getThumbnails() as $item) { - if ($item->getGroup()) { - if (empty($groups[$item->getGroup()])) { - $groups[$item->getGroup()] = [ - 'id' => 'group_' . $item->getName(), - 'text' => htmlspecialchars($item->getGroup()), - 'expandable' => true, - 'leaf' => false, - 'allowChildren' => true, - 'iconCls' => 'opendxp_icon_folder', - 'group' => $item->getGroup(), - 'children' => [], - ]; - } - $groups[$item->getGroup()]['children'][] = - [ - 'id' => $item->getName(), - 'text' => $item->getName(), - 'leaf' => true, - 'iconCls' => 'opendxp_icon_videothumbnails', - 'cls' => 'opendxp_treenode_disabled', - 'writeable' => $item->isWriteable(), - ]; - } else { - $thumbnails[] = [ - 'id' => $item->getName(), - 'text' => $item->getName(), - 'leaf' => true, - 'iconCls' => 'opendxp_icon_videothumbnails', - 'cls' => 'opendxp_treenode_disabled', - 'writeable' => $item->isWriteable(), - ]; - } - } - - foreach ($groups as $group) { - $thumbnails[] = $group; - } - - return $this->adminJson($thumbnails); - } - - #[Route('/video-thumbnail-list', name: 'opendxp_admin_settings_videothumbnail_list', methods: ['GET'])] - public function videoThumbnailListAction(): JsonResponse + public function thumbnailAdapterCheckAction(ThumbnailAdapterCheckHandler $handler): Response { - $thumbnails = [ - ['id' => 'opendxp-system-treepreview', 'text' => 'original'], - ]; - $list = new Asset\Video\Thumbnail\Config\Listing(); - - foreach ($list->getThumbnails() as $item) { - $thumbnails[] = [ - 'id' => $item->getName(), - 'text' => $item->getName(), - ]; - } - - return $this->adminJson($thumbnails); + return new Response($handler()->content); } - #[Route('/video-thumbnail-add', name: 'opendxp_admin_settings_videothumbnailadd', methods: ['POST'])] - public function videoThumbnailAddAction(Request $request): JsonResponse - { - $this->checkPermission('thumbnails'); - - $success = false; - - $pipe = Asset\Video\Thumbnail\Config::getByName($request->request->get('name')); - - if (!$pipe) { - $pipe = new Asset\Video\Thumbnail\Config(); - if (!$pipe->isWriteable()) { - throw new ConfigWriteException(); - } - $pipe->setName($request->request->get('name')); - $pipe->save(); - $success = true; - } elseif (!$pipe->isWriteable()) { - throw new ConfigWriteException(); - } - - return $this->adminJson(['success' => $success, 'id' => $pipe->getName()]); - } - - #[Route('/video-thumbnail-delete', name: 'opendxp_admin_settings_videothumbnaildelete', methods: ['DELETE'])] - public function videoThumbnailDeleteAction(Request $request): JsonResponse - { - $this->checkPermission('thumbnails'); - - $pipe = Asset\Video\Thumbnail\Config::getByName($request->request->get('name')); - - if (!$pipe->isWriteable()) { - throw new ConfigWriteException(); - } - - $pipe->delete(); - - return $this->adminJson(['success' => true]); - } - - #[Route('/video-thumbnail-get', name: 'opendxp_admin_settings_videothumbnailget', methods: ['GET'])] - public function videoThumbnailGetAction(Request $request): JsonResponse - { - $this->checkPermission('thumbnails'); - - $pipe = Asset\Video\Thumbnail\Config::getByName($request->query->get('name')); - - $data = $pipe->getObjectVars(); - $data['writeable'] = $pipe->isWriteable(); - - return $this->adminJson($data); - } - - #[Route('/video-thumbnail-update', name: 'opendxp_admin_settings_videothumbnailupdate', methods: ['PUT'])] - public function videoThumbnailUpdateAction(Request $request): JsonResponse - { - $this->checkPermission('thumbnails'); - - $pipe = Asset\Video\Thumbnail\Config::getByName($request->request->get('name')); - - if (!$pipe->isWriteable()) { - throw new ConfigWriteException(); - } - - $settingsData = $this->decodeJson($request->request->get('settings')); - $mediaData = $this->decodeJson($request->request->get('medias')); - $mediaOrder = $this->decodeJson($request->request->get('mediaOrder')); - - foreach ($settingsData as $key => $value) { - $setter = 'set' . ucfirst($key); - if (method_exists($pipe, $setter)) { - $pipe->$setter($value); - } - } - - $pipe->resetItems(); - - uksort($mediaData, static function ($a, $b) use ($mediaOrder) { - if ($a === 'default') { - return -1; - } - - return ($mediaOrder[$a] < $mediaOrder[$b]) ? -1 : 1; - }); - - foreach ($mediaData as $mediaName => $items) { - foreach ($items as $item) { - $type = $item['type']; - unset($item['type']); - - $pipe->addItem($type, $item, htmlspecialchars($mediaName)); - } - } - - $pipe->save(); - - return $this->adminJson(['success' => true]); - } - - /** - * @throws Exception - */ + #[IsGranted(CorePermission::WebsiteSettings->value)] #[Route('/website-settings', name: 'opendxp_admin_settings_websitesettings', methods: ['POST'])] - public function websiteSettingsAction(Request $request): JsonResponse - { - $this->checkPermission('website_settings'); - + public function websiteSettingsAction( + Request $request, + GetWebsiteSettingsListHandler $getWebsiteSettingsList, + CreateWebsiteSettingHandler $createWebsiteSetting, + UpdateWebsiteSettingHandler $updateWebsiteSetting, + DeleteWebsiteSettingHandler $deleteWebsiteSetting, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { if ($request->request->has('data')) { $data = $this->decodeJson($request->request->get('data')); @@ -1055,156 +353,45 @@ public function websiteSettingsAction(Request $request): JsonResponse $value = trim($value); } } + unset($value); } - if ($request->query->get('xaction') === 'destroy') { - $id = $data['id']; - $setting = WebsiteSetting::getById($id); - if ($setting instanceof WebsiteSetting) { - $setting->delete(); - - return $this->adminJson(['success' => true, 'data' => []]); - } - } elseif ($request->query->get('xaction') === 'update') { - // save routes - $setting = WebsiteSetting::getById($data['id']); - if ($setting instanceof WebsiteSetting) { - switch ($setting->getType()) { - case 'document': - case 'asset': - case 'object': - if (isset($data['data'])) { - $element = Element\Service::getElementByPath($setting->getType(), $data['data']); - $data['data'] = $element; - } - - break; - } - - $setting->setValues($data); - $setting->save(); + if ($xaction === 'destroy') { + $deleteWebsiteSetting((int) $data['id']); - $data = $this->getWebsiteSettingForEditMode($setting); + return $this->adminJson(ApiResponse::ok(['data' => []])); + } elseif ($xaction === 'update') { + $result = $updateWebsiteSetting($data); - return $this->adminJson(['data' => $data, 'success' => true]); - } - } elseif ($request->query->get('xaction') === 'create') { + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); + } elseif ($xaction === 'create') { unset($data['id']); + $result = $createWebsiteSetting($data); - // save route - $setting = new WebsiteSetting(); - $setting->setValues($data); - - $setting->save(); - - return $this->adminJson(['data' => $setting->getObjectVars(), 'success' => true]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } } else { - $list = new WebsiteSetting\Listing(); - - $list->setLimit((int) $request->request->get('limit', 50)); - $list->setOffset((int) $request->request->get('start', 0)); - - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings([...$request->request->all(), ...$request->query->all()]); - if ($sortingSettings['orderKey']) { - $list->setOrderKey($sortingSettings['orderKey']); - $list->setOrder($sortingSettings['order']); - } else { - $list->setOrderKey('name'); - $list->setOrder('asc'); - } + $sortingSettings = QueryParams::extractSortingSettings([...$request->request->all(), ...$request->query->all()]); - if ($request->request->has('filter')) { - $list->setCondition('`name` LIKE ' . $list->quote('%'.$request->request->get('filter').'%')); - } - - $totalCount = $list->getTotalCount(); - $list = $list->load(); - - $settings = []; - foreach ($list as $item) { - $resultItem = $this->getWebsiteSettingForEditMode($item); - $settings[] = $resultItem; - } + $result = $getWebsiteSettingsList( + limit: (int) $request->request->get('limit', 50), + offset: (int) $request->request->get('start', 0), + orderKey: $sortingSettings['orderKey'] ?: null, + order: $sortingSettings['order'] ?? null, + filter: $request->request->has('filter') ? $request->request->get('filter') : null, + ); - return $this->adminJson(['data' => $settings, 'success' => true, 'total' => $totalCount]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - return $this->adminJson(['success' => false]); - } - - /** - * @return array{id: ?int, name: string, language: string, type: string, data: mixed, siteId: ?int, creationDate: ?int, modificationDate: ?int} - */ - private function getWebsiteSettingForEditMode(WebsiteSetting $item): array - { - $resultItem = [ - 'id' => $item->getId(), - 'name' => $item->getName(), - 'language' => $item->getLanguage(), - 'type' => $item->getType(), - 'data' => null, - 'siteId' => $item->getSiteId(), - 'creationDate' => $item->getCreationDate(), - 'modificationDate' => $item->getModificationDate(), - ]; - - switch ($item->getType()) { - case 'document': - case 'asset': - case 'object': - $element = $item->getData(); - if ($element) { - $resultItem['data'] = $element->getRealFullPath(); - } - - break; - default: - $resultItem['data'] = $item->getData(); - - break; - } - - return $resultItem; + return $this->adminJson(ApiResponse::error()); } #[Route('/get-available-algorithms', name: 'opendxp_admin_settings_getavailablealgorithms', methods: ['GET'])] - public function getAvailableAlgorithmsAction(Request $request): JsonResponse + public function getAvailableAlgorithmsAction(GetAvailableAlgorithmsHandler $handler): JsonResponse { - $options = [ - [ - 'key' => 'password_hash', - 'value' => 'password_hash', - ], - ]; - - $algorithms = hash_algos(); - foreach ($algorithms as $algorithm) { - $options[] = [ - 'key' => $algorithm . ' (' . $this->translator->trans('deprecated', [], 'admin') . ')', - 'value' => $algorithm, - ]; - } - - $result = ['data' => $options, 'success' => true, 'total' => count($options)]; - - return $this->adminJson($result); - } + $result = $handler(); - /** - * deleteViews - * delete views for localized fields when languages are removed to - * prevent mysql errors - */ - protected function deleteViews(string $language, string $dbName): void - { - $db = \OpenDxp\Db::get(); - $views = $db->fetchAllAssociative(sprintf('SHOW FULL TABLES IN %s WHERE TABLE_TYPE LIKE "VIEW"', $db->quoteIdentifier($dbName))); - - foreach ($views as $view) { - if (preg_match('/^object_localized_[0-9]+_' . $language . '$/', $view['Tables_in_' . $dbName])) { - $db->executeStatement(sprintf('DROP VIEW %s', $db->quoteIdentifier($view['Tables_in_' . $dbName]))); - } - } + return $this->adminJson(ApiResponse::ok(['data' => $result->options, 'total' => count($result->options)])); } } diff --git a/src/Controller/Admin/TagsController.php b/src/Controller/Admin/TagsController.php index 60ea39c9..857c31d6 100644 --- a/src/Controller/Admin/TagsController.php +++ b/src/Controller/Admin/TagsController.php @@ -1,5 +1,4 @@ value)] #[Route('/add', name: 'opendxp_admin_tags_add', methods: ['POST'])] - public function addAction(Request $request): JsonResponse + public function addAction(Request $request, AddTagHandler $addTag): JsonResponse { - $this->checkPermission('tags_configuration'); + $result = $addTag( + name: strip_tags($request->request->get('text', '')), + parentId: (int)$request->request->get('parentId'), + ); - try { - $tag = new Tag(); - $tag->setName(strip_tags($request->request->get('text', ''))); - $tag->setParentId((int)$request->request->get('parentId')); - $tag->save(); - - return $this->adminJson(['success' => true, 'id' => $tag->getId()]); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } + return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::TagsConfiguration->value)] #[Route('/delete', name: 'opendxp_admin_tags_delete', methods: ['DELETE'])] - public function deleteAction(Request $request): JsonResponse + public function deleteAction(Request $request, DeleteTagHandler $deleteTag): JsonResponse { - $this->checkPermission('tags_configuration'); + $deleteTag(id: (int)$request->request->get('id')); - $tag = Tag::getById((int) $request->request->get('id')); - if ($tag) { - $tag->delete(); - - return $this->adminJson(['success' => true]); - } - - throw $this->createNotFoundException('Tag with ID ' . $request->request->get('id') . ' not found.'); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::TagsConfiguration->value)] #[Route('/update', name: 'opendxp_admin_tags_update', methods: ['PUT'])] - public function updateAction(Request $request): JsonResponse + public function updateAction(Request $request, UpdateTagHandler $updateTag): JsonResponse { - $this->checkPermission('tags_configuration'); - - $tag = Tag::getById((int) $request->request->get('id')); - if ($tag) { - $parentId = $request->request->get('parentId'); - if ($parentId || $parentId === '0') { - $tag->setParentId((int)$parentId); - } - if ($request->request->has('text')) { - $tag->setName(strip_tags($request->request->get('text', ''))); - } - - $tag->save(); - - return $this->adminJson(['success' => true]); - } - - throw $this->createNotFoundException('Tag with ID ' . $request->request->get('id') . ' not found.'); + $parentId = $request->request->get('parentId'); + $updateTag( + id: (int)$request->request->get('id'), + parentId: ($parentId || $parentId === '0') ? (int)$parentId : null, + name: $request->request->has('text') ? strip_tags($request->request->get('text', '')) : null, + ); + + return $this->adminJson(ApiResponse::ok()); } #[Route('/tree-get-children-by-id', name: 'opendxp_admin_tags_treegetchildrenbyid', methods: ['GET'])] - public function treeGetChildrenByIdAction(Request $request): JsonResponse - { - $showSelection = $request->query->get('showSelection') === 'true'; - $assignmentCId = (int)$request->query->get('assignmentCId'); - $assignmentCType = strip_tags($request->query->get('assignmentCType', '')); - - $recursiveChildren = false; - $assignedTagIds = []; - if ($assignmentCId && $assignmentCType) { - $assignedTags = Tag::getTagsForElement($assignmentCType, $assignmentCId); - - foreach ($assignedTags as $assignedTag) { - $assignedTagIds[$assignedTag->getId()] = $assignedTag; - } - } - - $tagList = new Tag\Listing(); - if ($request->query->get('node')) { - $tagList->setCondition('parentId = ?', (int)$request->query->get('node')); - } else { - $tagList->setCondition('ISNULL(parentId) OR parentId = 0'); - } - $tagList->setOrderKey('name'); - - if (!empty($request->query->get('filter'))) { - $filterIds = [0]; - $filterTagList = new Tag\Listing(); - $filterTagList->setCondition('LOWER(`name`) LIKE ?', ['%' . $filterTagList->escapeLike(mb_strtolower($request->query->get('filter'))) . '%']); - foreach ($filterTagList->load() as $filterTag) { - if ($filterTag->getParentId() === 0) { - $filterIds[] = $filterTag->getId(); - } else { - $ids = explode('/', $filterTag->getIdPath()); - if (isset($ids[1])) { - $filterIds[] = (int)$ids[1]; - } - } - } - - $filterIds = array_unique($filterIds); - $tagList->setCondition('id IN('.implode(',', $filterIds).')'); - $recursiveChildren = true; - } - - $tags = []; - foreach ($tagList->load() as $tag) { - $tags[] = $this->convertTagToArray($tag, $showSelection, $assignedTagIds, true, $recursiveChildren); - } - - return $this->adminJson($tags); - } - - protected function convertTagToArray(Tag $tag, bool $showSelection, array $assignedTagIds, bool $loadChildren = false, bool $recursiveChildren = false): array - { - $hasChildren = $tag->hasChildren(); - - $tagArray = [ - 'id' => $tag->getId(), - 'text' => $tag->getName(), - 'path' => $tag->getNamePath(), - 'expandable' => $hasChildren, - 'leaf' => !$hasChildren, - 'iconCls' => 'opendxp_icon_element_tags', - 'qtipCfg' => [ - 'title' => 'ID: ' . $tag->getId(), - ], - ]; - - if ($showSelection) { - $tagArray['checked'] = isset($assignedTagIds[$tag->getId()]); - } - - if ($hasChildren && $loadChildren) { - $children = $tag->getChildren(); - $loadChildren = $recursiveChildren; - foreach ($children as $child) { - $tagArray['children'][] = $this->convertTagToArray($child, $showSelection, $assignedTagIds, $loadChildren, $recursiveChildren); - } - } - - return $tagArray; + public function treeGetChildrenByIdAction( + GetTagTreeChildrenHandler $getTagTreeChildren, + #[MapQueryParameter] ?string $showSelection = null, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $assignmentCId = null, + #[MapQueryParameter] string $assignmentCType = '', + #[MapQueryParameter] ?string $node = null, + #[MapQueryParameter] ?string $filter = null, + ): JsonResponse { + $result = $getTagTreeChildren( + showSelection: $showSelection === 'true', + assignmentCId: $assignmentCId, + assignmentCType: strip_tags($assignmentCType), + node: $node, + filter: $filter, + ); + + return $this->adminJson($result->tags); } #[Route('/load-tags-for-element', name: 'opendxp_admin_tags_loadtagsforelement', methods: ['GET'])] - public function loadTagsForElementAction(Request $request): JsonResponse - { - $assignmentId = (int)$request->query->get('assignmentCId'); - $assignmentType = strip_tags($request->query->get('assignmentCType', '')); - - $assignedTagArray = []; - if ($assignmentId && $assignmentType) { - $assignedTags = Tag::getTagsForElement($assignmentType, $assignmentId); - - foreach ($assignedTags as $assignedTag) { - $assignedTagArray[] = $this->convertTagToArray($assignedTag, false, []); - } + public function loadTagsForElementAction( + GetTagsForElementHandler $getTagsForElement, + #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $assignmentCId = null, + #[MapQueryParameter] string $assignmentCType = '', + ): JsonResponse { + if (!$assignmentCId || !$assignmentCType) { + return $this->adminJson([]); } - return $this->adminJson($assignedTagArray); + $result = $getTagsForElement( + assignmentId: $assignmentCId, + assignmentType: strip_tags($assignmentCType), + ); + + return $this->adminJson($result->tags); } #[Route('/add-tag-to-element', name: 'opendxp_admin_tags_addtagtoelement', methods: ['PUT'])] - public function addTagToElementAction(Request $request): JsonResponse + public function addTagToElementAction(Request $request, AddTagToElementHandler $addTagToElement): JsonResponse { - $assignmentElementId = (int)$request->request->get('assignmentElementId'); - $assignmentElementType = strip_tags($request->request->get('assignmentElementType', '')); - $tagId = (int)$request->request->get('tagId'); - - $tag = Tag::getById($tagId); - if ($tag) { - Tag::addTagToElement($assignmentElementType, $assignmentElementId, $tag); + $result = $addTagToElement( + tagId: (int)$request->request->get('tagId'), + elementType: strip_tags($request->request->get('assignmentElementType', '')), + elementId: (int)$request->request->get('assignmentElementId'), + ); - return $this->adminJson(['success' => true, 'id' => $tag->getId()]); - } - - return $this->adminJson(['success' => false]); + return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } #[Route('/remove-tag-from-element', name: 'opendxp_admin_tags_removetagfromelement', methods: ['DELETE'])] - public function removeTagFromElementAction(Request $request): JsonResponse + public function removeTagFromElementAction(Request $request, RemoveTagFromElementHandler $removeTagFromElement): JsonResponse { - $assignmentElementId = (int)$request->request->get('assignmentElementId'); - $assignmentElementType = strip_tags($request->request->get('assignmentElementType', '')); - $tagId = (int)$request->request->get('tagId'); - - $tag = Tag::getById($tagId); - if ($tag) { - Tag::removeTagFromElement($assignmentElementType, $assignmentElementId, $tag); - - return $this->adminJson(['success' => true, 'id' => $tag->getId()]); - } + $result = $removeTagFromElement( + tagId: (int)$request->request->get('tagId'), + elementType: strip_tags($request->request->get('assignmentElementType', '')), + elementId: (int)$request->request->get('assignmentElementId'), + ); - return $this->adminJson(['success' => false]); + return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } #[Route('/get-batch-assignment-jobs', name: 'opendxp_admin_tags_getbatchassignmentjobs', methods: ['GET'])] - public function getBatchAssignmentJobsAction(Request $request, EventDispatcherInterface $eventDispatcher): JsonResponse - { - $elementId = (int)$request->query->get('elementId'); - $elementType = strip_tags($request->query->get('elementType', '')); - - $idList = []; - switch ($elementType) { - case 'object': - $object = \OpenDxp\Model\DataObject::getById($elementId); - if ($object) { - $idList = $this->getSubObjectIds($object, $eventDispatcher); - } - - break; - case 'asset': - $asset = \OpenDxp\Model\Asset::getById($elementId); - if ($asset) { - $idList = $this->getSubAssetIds($asset, $eventDispatcher); - } - - break; - case 'document': - $document = \OpenDxp\Model\Document::getById($elementId); - if ($document) { - $idList = $this->getSubDocumentIds($document, $eventDispatcher); - } - - break; - } - - $size = 2; - $offset = 0; - $idListParts = []; - while ($offset < count($idList)) { - $idListParts[] = array_slice($idList, $offset, $size); - $offset += $size; - } - - return $this->adminJson(['success' => true, 'idLists' => $idListParts, 'totalCount' => count($idList)]); - } - - /** - * @return int[] - */ - private function getSubObjectIds(\OpenDxp\Model\DataObject\AbstractObject $object, EventDispatcherInterface $eventDispatcher): array - { - $childrenList = new \OpenDxp\Model\DataObject\Listing(); - $condition = '`path` LIKE ?'; - if (!$this->getAdminUser()->isAdmin()) { - $userIds = $this->getAdminUser()->getRoles(); - $userIds[] = $this->getAdminUser()->getId(); - $condition .= ' AND ( - (SELECT `view` FROM users_workspaces_object WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`,`key`),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - OR - (SELECT `view` FROM users_workspaces_object WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`,`key`))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - )'; - } - - $childrenList->setCondition($condition, $childrenList->escapeLike($object->getRealFullPath()) . '/%'); - - $beforeListLoadEvent = new GenericEvent($this, [ - 'list' => $childrenList, - 'context' => [], - ]); - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::OBJECT_LIST_BEFORE_LIST_LOAD); - /** @var \OpenDxp\Model\DataObject\Listing $childrenList */ - $childrenList = $beforeListLoadEvent->getArgument('list'); - - return $childrenList->loadIdList(); - } - - /** - * @return int[] - */ - private function getSubAssetIds(\OpenDxp\Model\Asset $asset, EventDispatcherInterface $eventDispatcher): array - { - $childrenList = new \OpenDxp\Model\Asset\Listing(); - $condition = '`path` LIKE ?'; - if (!$this->getAdminUser()->isAdmin()) { - $userIds = $this->getAdminUser()->getRoles(); - $userIds[] = $this->getAdminUser()->getId(); - $condition .= ' AND ( - (SELECT `view` FROM users_workspaces_asset WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`,filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - OR - (SELECT `view` FROM users_workspaces_asset WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`,filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - )'; - } - - $childrenList->setCondition($condition, $childrenList->escapeLike($asset->getRealFullPath()) . '/%'); - - $beforeListLoadEvent = new GenericEvent($this, [ - 'list' => $childrenList, - 'context' => [], - ]); - - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD); - /** @var \OpenDxp\Model\Asset\Listing $childrenList */ - $childrenList = $beforeListLoadEvent->getArgument('list'); - - return $childrenList->loadIdList(); - } - - /** - * @return int[] - */ - private function getSubDocumentIds(\OpenDxp\Model\Document $document, EventDispatcherInterface $eventDispatcher): array - { - $childrenList = new \OpenDxp\Model\Document\Listing(); - $condition = '`path` LIKE ?'; - if (!$this->getAdminUser()->isAdmin()) { - $userIds = $this->getAdminUser()->getRoles(); - $userIds[] = $this->getAdminUser()->getId(); - $condition .= ' AND ( - (SELECT `view` FROM users_workspaces_document WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`,`key`),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - OR - (SELECT `view` FROM users_workspaces_document WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`,`key`))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 - )'; - } - - $childrenList->setCondition($condition, $childrenList->escapeLike($document->getRealFullPath()) . '/%'); - - $beforeListLoadEvent = new GenericEvent($this, [ - 'list' => $childrenList, - 'context' => [], - ]); - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::DOCUMENT_LIST_BEFORE_LIST_LOAD); - /** @var \OpenDxp\Model\Document\Listing $childrenList */ - $childrenList = $beforeListLoadEvent->getArgument('list'); - - return $childrenList->loadIdList(); + public function getBatchAssignmentJobsAction( + GetBatchAssignmentJobsHandler $getBatchAssignmentJobs, + #[MapQueryParameter] int $elementId = 0, + #[MapQueryParameter] string $elementType = '', + ): JsonResponse { + $result = $getBatchAssignmentJobs( + elementType: strip_tags($elementType), + elementId: $elementId, + ); + + return $this->adminJson(ApiResponse::ok(['idLists' => $result->idListParts, 'totalCount' => $result->totalCount])); } #[Route('/do-batch-assignment', name: 'opendxp_admin_tags_dobatchassignment', methods: ['PUT'])] - public function doBatchAssignmentAction(Request $request): JsonResponse + public function doBatchAssignmentAction(Request $request, DoBatchAssignmentHandler $doBatchAssignment): JsonResponse { - $cType = strip_tags($request->request->get('elementType', '')); - $assignedTags = json_decode($request->request->get('assignedTags')); - $elementIds = json_decode($request->request->get('childrenIds')); - $doCleanupTags = $request->request->get('removeAndApply') === 'true'; - - Tag::batchAssignTagsToElement($cType, $elementIds, $assignedTags, $doCleanupTags); - - return $this->adminJson(['success' => true]); + $doBatchAssignment( + elementType: strip_tags($request->request->get('elementType', '')), + elementIds: json_decode($request->request->get('childrenIds'), true) ?? [], + assignedTags: json_decode($request->request->get('assignedTags'), true) ?? [], + doCleanupTags: $request->request->get('removeAndApply') === 'true', + ); + + return $this->adminJson(ApiResponse::ok()); } } diff --git a/src/Controller/Admin/TranslationController.php b/src/Controller/Admin/TranslationController.php index 69e87ff7..b6ec5de0 100644 --- a/src/Controller/Admin/TranslationController.php +++ b/src/Controller/Admin/TranslationController.php @@ -1,5 +1,4 @@ request->get('domain', Translation::DOMAIN_DEFAULT); - $admin = $domain == Translation::DOMAIN_ADMIN; + $admin = $domain === Translation::DOMAIN_ADMIN; $dialect = $request->request->get('csvSettings'); $session = Session::getSessionBag($request->getSession(), 'opendxp_importconfig'); @@ -62,45 +66,26 @@ public function importAction(Request $request, LocaleServiceInterface $localeSer $this->checkPermission(($admin ? 'admin_' : '') . 'translations'); - $merge = $request->query->get('merge'); $overwrite = !$merge; - $allowedLanguages = $this->getAdminUser()->getAllowedLanguagesForEditingWebsiteTranslations(); - if ($admin) { - $allowedLanguages = Tool\Admin::getLanguages(); - } + $flagUrlTemplate = $this->generateUrl('opendxp_admin_misc_getlanguageflag', ['language' => '{language}']); + $flagUrlTemplate = str_replace('%7Blanguage%7D', '{language}', $flagUrlTemplate); - $delta = Translation::importTranslationsFromFile( - $tmpFile, - $domain, - $overwrite, - $allowedLanguages, - $dialect + $result = $importTranslations( + tmpFile: $tmpFile, + domain: $domain, + overwrite: $overwrite, + dialect: $dialect, + enrichDelta: (bool) $merge, + flagUrlTemplate: $flagUrlTemplate, ); - if (is_file($tmpFile)) { - @unlink($tmpFile); - } - - $result = [ - 'success' => true, - ]; - + $extra = []; if ($merge) { - $enrichedDelta = []; - foreach ($delta as $item) { - $lg = $item['lg']; - $currentLocale = $localeService->findLocale(); - $item['lgname'] = Locale::getDisplayLanguage($lg, $currentLocale); - $item['icon'] = $this->generateUrl('opendxp_admin_misc_getlanguageflag', ['language' => $lg]); - $item['current'] = $item['text']; - $enrichedDelta[] = $item; - } - - $result['delta'] = base64_encode(json_encode($enrichedDelta)); + $extra['delta'] = base64_encode(json_encode($result->delta)); } - $response = $this->adminJson($result); + $response = $this->adminJson(ApiResponse::ok($extra)); // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in // Ext.form.Action.Submit and mark the submission as failed $response->headers->set('Content-Type', 'text/html'); @@ -109,371 +94,123 @@ public function importAction(Request $request, LocaleServiceInterface $localeSer } #[Route('/upload-import', name: 'opendxp_admin_translation_uploadimportfile', methods: ['POST'])] - public function uploadImportFileAction(Request $request, Filesystem $filesystem): JsonResponse - { - /** @var UploadedFile $file */ + public function uploadImportFileAction( + Request $request, + UploadTranslationImportFileHandler $uploadTranslationImportFile, + ): JsonResponse { $file = $request->files->get('Filedata'); - $tmpData = file_get_contents($file->getPathname()); - - //store data for further usage - $filename = uniqid('import_translations-', false); - $importFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . $filename; - $filesystem->dumpFile($importFile, $tmpData); + $result = $uploadTranslationImportFile($file); - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($importFile): void { - $session->set('translation_import_file', $importFile); + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($result): void { + $session->set('translation_import_file', $result->importFile); }, 'opendxp_importconfig'); - // determine csv settings - $dialect = Tool\Admin::determineCsvDialect($importFile); - - //ignore if line terminator is already hex otherwise generate hex for string - if (!empty($dialect->lineterminator) && empty(preg_match('/[a-f0-9]{2}/i', $dialect->lineterminator))) { - $dialect->lineterminator = bin2hex($dialect->lineterminator); - } - - return $this->adminJson([ - 'success' => true, - 'config' => [ - 'csvSettings' => $dialect, - ], - ]); + return $this->adminJson(ApiResponse::ok(['config' => [ + 'csvSettings' => $result->dialect, + ]])); } #[Route('/export', name: 'opendxp_admin_translation_export', methods: ['GET'])] - public function exportAction(Request $request): Response - { - $domain = $request->query->get('domain', Translation::DOMAIN_DEFAULT); - $admin = $domain == Translation::DOMAIN_ADMIN; + public function exportAction( + ExportTranslationsHandler $exportTranslations, + #[MapQueryParameter] ?string $domain = null, + #[MapQueryParameter] ?string $filter = null, + #[MapQueryParameter] ?string $searchString = null, + ): Response { + $admin = $domain === Translation::DOMAIN_ADMIN; $this->checkPermission(($admin ? 'admin_' : '') . 'translations'); - $translation = new Translation(); - $translation->setDomain($domain); - $tableName = $translation->getDao()->getDatabaseTableName(); - - $list = new Translation\Listing(); - $list->setDomain($domain); - - $joins = []; - - $list->setOrder('asc'); - $list->setOrderKey($tableName . '.key', false); - - $filterParameters = [ - 'filter' => $request->query->get('filter'), - 'searchString' => $request->query->get('searchString'), - ]; - - $conditions = $this->getGridFilterCondition($filterParameters, $tableName, false, $admin); - if ($conditions !== []) { - $list->setCondition($conditions['condition'], $conditions['params']); - } - - $filters = $this->getGridFilterCondition($filterParameters, $tableName, true, $admin); - - if ($filters) { - $joins = [...$joins, ...$filters['joins']]; - } - - $this->extendTranslationQuery($joins, $list, $tableName, $filters); - - try { - $list->load(); - } catch (SyntaxErrorException) { - throw new InvalidArgumentException('Check your arguments.'); - } - - $translations = []; - $translationObjects = $list->getTranslations(); - - // fill with one dummy translation if the store is empty - if ($translationObjects === []) { - if ($admin) { - $t = new Translation(); - $t->setDomain(Translation::DOMAIN_ADMIN); - $languages = Tool\Admin::getLanguages(); - } else { - $t = new Translation(); - $languages = $this->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); - } - - foreach ($languages as $language) { - $t->addTranslation($language, ''); - } - - $translationObjects[] = $t; - } - - foreach ($translationObjects as $t) { - $row = $t->getTranslations(); - $row = Element\Service::escapeCsvRecord($row); - $translations[] = ['key' => $t->getKey(), 'creationDate' => $t->getCreationDate(), 'modificationDate' => $t->getModificationDate(), ...$row]; - } - - //header column - $columns = array_keys($translations[0]); - - if ($admin) { - $languages = Tool\Admin::getLanguages(); - } else { - $languages = $this->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); - } - - //add language columns which have no translations yet - foreach ($languages as $l) { - if (!in_array($l, $columns)) { - $columns[] = $l; - } - } - - //remove invalid languages - foreach ($columns as $key => $column) { - if (strtolower(trim($column)) !== 'key' && !in_array($column, $languages)) { - unset($columns[$key]); - } - } - $columns = array_values($columns); - - $headerRow = []; - foreach ($columns as $key => $value) { - $headerRow[] = '"' . $value . '"'; - } - $csv = implode(';', $headerRow) . "\r\n"; - - foreach ($translations as $t) { - $tempRow = []; - foreach ($columns as $key) { - $value = $t[$key] ?? null; - //clean value of evil stuff such as " and linebreaks - if (is_string($value)) { - $value = Tool\Text::removeLineBreaks($value); - $value = str_replace('"', '"', $value); - - $tempRow[$key] = '"' . $value . '"'; - } else { - $tempRow[$key] = $value; - } - } - $csv .= implode(';', $tempRow) . "\r\n"; - } + $result = $exportTranslations( + domain: $domain, + filter: $filter, + searchString: $searchString, + admin: $admin, + ); - $response = new Response("\xEF\xBB\xBF" . $csv); + $response = new Response("\xEF\xBB\xBF" . $result->csv); $response->headers->set('Content-Encoding', 'UTF-8'); $response->headers->set('Content-Type', 'text/csv; charset=UTF-8'); - $response->headers->set('Content-Disposition', 'attachment; filename: "export_' . $domain . '_translations.csv"'); + $response->headers->set('Content-Disposition', 'attachment; filename: "export_' . $result->domain . '_translations.csv"'); ini_set('display_errors', '0'); //to prevent warning messages in csv return $response; } #[Route('/add-admin-translation-keys', name: 'opendxp_admin_translation_addadmintranslationkeys', methods: ['POST'])] - public function addAdminTranslationKeysAction(Request $request): JsonResponse - { + public function addAdminTranslationKeysAction( + Request $request, + AddAdminTranslationKeysHandler $addAdminTranslationKeys, + ): JsonResponse { $keys = $request->request->get('keys'); if ($keys) { - $availableLanguages = Tool\Admin::getLanguages(); $data = $this->decodeJson($keys); - foreach ($data as $translationData) { - $t = null; // reset - - try { - $t = Translation::getByKey($translationData, Translation::DOMAIN_ADMIN); - } catch (Exception $e) { - Logger::log((string) $e); - } - if (!$t instanceof Translation) { - $t = new Translation(); - $t->setDomain(Translation::DOMAIN_ADMIN); - $t->setKey($translationData); - $t->setCreationDate(time()); - $t->setModificationDate(time()); - - foreach ($availableLanguages as $lang) { - $t->addTranslation($lang, ''); - } - - try { - $t->save(); - } catch (Exception $e) { - Logger::log((string) $e); - } - } - } + $addAdminTranslationKeys($data); } - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/translations', name: 'opendxp_admin_translation_translations', methods: ['POST'])] - public function translationsAction(Request $request, TranslatorInterface $translator): JsonResponse - { + public function translationsAction( + Request $request, + DeleteTranslationHandler $deleteTranslation, + UpdateTranslationHandler $updateTranslation, + CreateTranslationHandler $createTranslation, + GetTranslationsHandler $getTranslations, + #[MapQueryParameter] ?string $xaction = null, + ): JsonResponse { $domain = $request->request->get('domain', Translation::DOMAIN_DEFAULT); $admin = $domain === Translation::DOMAIN_ADMIN; - $validLanguages = $admin ? Tool\Admin::getLanguages() : $this->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); $this->checkPermission(($admin ? 'admin_' : '') . 'translations'); - $translation = new Translation(); - $translation->setDomain($domain); - $tableName = $translation->getDao()->getDatabaseTableName(); - if ($request->request->has('data')) { $data = $this->decodeJson($request->request->get('data')); - if ($request->query->get('xaction') === 'destroy') { - $t = Translation::getByKey($data['key'], $domain); - if ($t instanceof Translation) { - $t->delete(); - } + if ($xaction === 'destroy') { + $deleteTranslation($data['key'], $domain); - return $this->adminJson([ - 'success' => true, - 'data' => [], - ]); + return $this->adminJson(ApiResponse::ok(['data' => []])); } - if ($request->query->get('xaction') === 'update') { - $t = Translation::getByKey($data['key'], $domain); - - foreach ($data as $key => $value) { - $key = preg_replace('/^_/', '', $key, 1); - if (!in_array($key, ['key', 'type'])) { - $t->addTranslation($key, $value); - } - } - - if ($data['key']) { - $t->setKey($data['key']); - } - - if ($data['type']) { - $t->setType($data['type']); - } - $t->setModificationDate(time()); - $t->save(); - - return $this->adminJson([ - 'success' => true, - 'data' => [ - 'key' => $t->getKey(), - 'creationDate' => $t->getCreationDate(), - 'modificationDate' => $t->getModificationDate(), - 'type' => $t->getType(), - ...$this->prefixTranslations($t->getTranslations()), - ], - ]); - } + if ($xaction === 'update') { + $result = $updateTranslation($data, $domain); - if ($request->query->get('xaction') === 'create') { - $t = Translation::getByKey($data['key'], $domain); - if ($t) { - return $this->adminJson([ - 'message' => 'identifier_already_exists', - 'success' => false, - ]); - } - - $t = new Translation(); - $t->setDomain($domain); - $t->setKey($data['key']); - $t->setCreationDate(time()); - $t->setModificationDate(time()); - $t->setType($data['type'] ?? null); - - foreach ($validLanguages as $lang) { - $t->addTranslation($lang, ''); - } - - $t->save(); - - return $this->adminJson([ - 'success' => true, - 'data' => [ - 'key' => $t->getKey(), - 'creationDate' => $t->getCreationDate(), - 'modificationDate' => $t->getModificationDate(), - 'type' => $t->getType(), - ...$this->prefixTranslations($t->getTranslations()), - ], - ]); + return $this->adminJson(ApiResponse::ok(['data' => [ + 'key' => $result->key, + 'creationDate' => $result->creationDate, + 'modificationDate' => $result->modificationDate, + 'type' => $result->type, + ...$this->prefixTranslations($result->translations), + ]])); } - } - - // get list of types - $list = new Translation\Listing(); - $list->setDomain($domain); - $list->setOrder('asc'); - $list->setOrderKey($tableName . '.key', false); - $list->setLanguages($validLanguages); - $sortingSettings = \OpenDxp\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings( - [...$request->request->all(), ...$request->query->all()] - ); + if ($xaction === 'create') { + $result = $createTranslation($data, $domain); - $joins = []; - - if ($orderKey = $sortingSettings['orderKey']) { - if (in_array(trim($orderKey, '_'), $validLanguages)) { - $orderKey = trim($orderKey, '_'); - $joins[] = [ - 'language' => $orderKey, - ]; - $list->setOrderKey($orderKey); - } elseif ($list->isValidOrderKey($sortingSettings['orderKey'])) { - $list->setOrderKey($tableName . '.' . $sortingSettings['orderKey'], false); + return $this->adminJson(ApiResponse::ok(['data' => [ + 'key' => $result->key, + 'creationDate' => $result->creationDate, + 'modificationDate' => $result->modificationDate, + 'type' => $result->type, + ...$this->prefixTranslations($result->translations), + ]])); } } - if ($sortingSettings['order']) { - $list->setOrder($sortingSettings['order']); - } - - $list->setLimit((int) $request->request->get('limit', 50)); - $list->setOffset((int) $request->request->get('start', 0)); - - $filterParameters = [ - 'filter' => $request->request->get('filter'), - 'searchString' => $request->request->get('searchString'), - ]; - $conditions = $this->getGridFilterCondition($filterParameters, $tableName, false, $admin); - $filters = $this->getGridFilterCondition($filterParameters, $tableName, true, $admin); - - if ($filters) { - $joins = [...$joins, ...$filters['joins']]; - } - - if ($conditions !== []) { - $list->setCondition($conditions['condition'], $conditions['params']); - } - - $this->extendTranslationQuery($joins, $list, $tableName, $filters); - - $translations = []; - $searchString = $request->request->get('searchString'); - foreach ($list->getTranslations() as $t) { - //Reload translation to get complete data, - //if translation fetched based on the text not key - if ($searchString && !strpos($searchString, (string) $t->getKey()) && !$t = Translation::getByKey($t->getKey(), $domain)) { - continue; - } - - $translations[] = [ - ...$this->prefixTranslations($t->getTranslations()), - 'key' => $t->getKey(), - 'creationDate' => $t->getCreationDate(), - 'modificationDate' => $t->getModificationDate(), - 'type' => $t->getType(), - ]; - } + $result = $getTranslations( + domain: $domain, + requestParams: [...$request->request->all(), ...$request->query->all()], + limit: (int) $request->request->get('limit', 50), + offset: (int) $request->request->get('start', 0), + filter: $request->request->get('filter'), + searchString: $request->request->get('searchString'), + ); - return $this->adminJson([ - 'success' => true, - 'data' => $translations, - 'total' => $list->getTotalCount(), - ]); + return $this->adminJson(ApiResponse::ok(['data' => $result->translations, 'total' => $result->total])); } protected function prefixTranslations(array $translations): array @@ -486,171 +223,16 @@ protected function prefixTranslations(array $translations): array return $prefixedTranslations; } - protected function extendTranslationQuery(array $joins, Translation\Listing $list, string $tableName, array $filters): void - { - if ($joins) { - $list->onCreateQueryBuilder( - function (DoctrineQueryBuilder $select) use ( - $joins, - $tableName, - $filters - ): void { - $db = \OpenDxp\Db::get(); - - $alreadyJoined = []; - - foreach ($joins as $join) { - $fieldname = $join['language']; - - if (isset($alreadyJoined[$fieldname])) { - continue; - } - $alreadyJoined[$fieldname] = 1; - - $select->addSelect($fieldname . '.text AS ' . $fieldname); - $select->leftJoin( - $tableName, - $tableName, - $fieldname, - '(' - . $fieldname . '.key = ' . $tableName . '.key' - . ' and ' . $fieldname . '.language = ' . $db->quote($fieldname) - . ')' - ); - } - - $havings = $filters['conditions']; - if ($havings) { - $havings = implode(' AND ', $havings); - $select->having($havings); - } - } - ); - } - } - - protected function getGridFilterCondition(array $filterParameters, string $tableName, bool $languageMode = false, bool $admin = false): array - { - $placeHolderCount = 0; - $joins = []; - $conditions = []; - $validLanguages = $admin ? Tool\Admin::getLanguages() : $this->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); - - $db = \OpenDxp\Db::get(); - $conditionFilters = []; - - $filterJson = $filterParameters['filter']; - if ($filterJson) { - $propertyField = 'property'; - $operatorField = 'operator'; - - $filters = $this->decodeJson($filterJson); - foreach ($filters as $filter) { - $operator = '='; - $field = null; - $value = null; - - $fieldname = $filter[$propertyField]; - if (in_array(ltrim($fieldname, '_'), $validLanguages)) { - $fieldname = ltrim($fieldname, '_'); - } - $fieldname = str_replace('--', '', $fieldname); - if (!$languageMode && in_array($fieldname, $validLanguages)) { - continue; - } - if ($languageMode && !in_array($fieldname, $validLanguages)) { - continue; - } - - if (!$languageMode) { - $fieldname = $tableName . '.' . $fieldname; - } - - if (!empty($filter['value'])) { - if ($filter['type'] === 'string') { - $operator = 'LIKE'; - $field = $fieldname; - $value = '%' . $filter['value'] . '%'; - } elseif ($filter['type'] === 'date' || - (in_array($fieldname, ['modificationDate', 'creationDate']))) { - if ($filter[$operatorField] === 'lt') { - $operator = '<'; - } elseif ($filter[$operatorField] === 'gt') { - $operator = '>'; - } elseif ($filter[$operatorField] === 'eq') { - $operator = '='; - $fieldname = "UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))"; - } - $filter['value'] = strtotime($filter['value']); - $field = $fieldname; - $value = $filter['value']; - } - } - - if ($field && $value) { - $condition = $db->quoteIdentifier($field) . ' ' . $operator . ' ' . $db->quote($value); - - if ($languageMode) { - $conditions[$fieldname] = $condition; - $joins[] = [ - 'language' => $fieldname, - ]; - } else { - $placeHolderName = self::PLACEHOLDER_NAME . $placeHolderCount; - $placeHolderCount++; - $conditionFilters[] = [ - 'condition' => $field . ' ' . $operator . ' :' . $placeHolderName, - 'field' => $placeHolderName, - 'value' => $value, - ]; - } - } - } - } - - if (!empty($filterParameters['searchString'])) { - $conditionFilters[] = [ - 'condition' => '(lower(' . $tableName . '.key) LIKE :filterTerm OR lower(' . $tableName . '.text) LIKE :filterTerm)', - 'field' => 'filterTerm', - 'value' => '%' . mb_strtolower($filterParameters['searchString']) . '%', - ]; - } - - if ($languageMode) { - return [ - 'joins' => $joins, - 'conditions' => $conditions, - ]; - } - - if ($conditionFilters !== []) { - $conditions = []; - $params = []; - foreach ($conditionFilters as $conditionFilter) { - $conditions[] = $conditionFilter['condition']; - $params[$conditionFilter['field']] = $conditionFilter['value']; - } - - $conditionFilters = [ - 'condition' => implode(' AND ', $conditions), - 'params' => $params, - ]; - } - - return $conditionFilters; - } - #[Route('/cleanup', name: 'opendxp_admin_translation_cleanup', methods: ['DELETE'])] - public function cleanupAction(Request $request): JsonResponse - { + public function cleanupAction( + Request $request, + CleanupTranslationsHandler $cleanupTranslations, + ): JsonResponse { $domain = $request->request->get('domain', Translation::DOMAIN_DEFAULT); - $list = new Translation\Listing(); - $list->setDomain($domain); - $list->cleanup(); - \OpenDxp\Cache::clearTags(['translator', 'translate']); + $cleanupTranslations($domain); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } /** @@ -660,154 +242,60 @@ public function cleanupAction(Request $request): JsonResponse * ----------------------------------------------------------------------------------- */ #[Route('/content-export-jobs', name: 'opendxp_admin_translation_contentexportjobs', methods: ['POST'])] - public function contentExportJobsAction(Request $request): JsonResponse - { + public function contentExportJobsAction( + Request $request, + BuildContentExportJobsHandler $buildContentExportJobs, + ): JsonResponse { $data = $this->decodeJson($request->request->get('data')); - $elements = []; - $jobs = []; - $exportId = uniqid('', false); - $source = $request->request->get('source', ''); - $target = $request->request->get('target', ''); + $source = str_replace('_', '-', $request->request->get('source', '')); + $target = str_replace('_', '-', $request->request->get('target', '')); $type = $request->request->get('type'); $jobUrl = $request->request->get('job_url', $request->getBaseUrl() . '/admin/translation/' . $type . '-export'); - $source = str_replace('_', '-', $source); - $target = str_replace('_', '-', $target); - - if ($data && is_array($data)) { - foreach ($data as $element) { - $elements[$element['type'] . '_' . $element['id']] = [ - 'id' => $element['id'], - 'type' => $element['type'], - ]; - - $el = null; - - if ($element['children']) { - $el = Element\Service::getElementById($element['type'], (int) $element['id']); - $baseClass = ELement\Service::getBaseClassNameForElement($element['type']); - $listClass = '\\OpenDxp\\Model\\' . $baseClass . '\\Listing'; - $list = new $listClass(); - $list->setUnpublished(true); - if ($el instanceof DataObject\AbstractObject) { - // inlcude variants - $list->setObjectTypes( - [DataObject::OBJECT_TYPE_VARIANT, - DataObject::OBJECT_TYPE_OBJECT, - DataObject::OBJECT_TYPE_FOLDER, ] - ); - } - $list->setCondition( - 'path LIKE ?', - [$list->escapeLike($el->getRealFullPath() . ($el->getRealFullPath() !== '/' ? '/' : '')) . '%'] - ); - $children = $list->load(); - - foreach ($children as $child) { - $childId = $child->getId(); - $elements[$element['type'] . '_' . $childId] = [ - 'id' => $childId, - 'type' => $element['type'], - ]; - - if (isset($element['relations']) && $element['relations']) { - $childDependencies = $child->getDependencies()->getRequires(); - foreach ($childDependencies as $cd) { - if ($cd['type'] === 'object' || $cd['type'] === 'document') { - $elements[$cd['type'] . '_' . $cd['id']] = $cd; - } - } - } - } - } - - if (isset($element['relations']) && $element['relations']) { - if (!$el instanceof Element\ElementInterface) { - $el = Element\Service::getElementById($element['type'], (int) $element['id']); - } - - $dependencies = $el->getDependencies()->getRequires(); - foreach ($dependencies as $dependency) { - if ($dependency['type'] === 'object' || $dependency['type'] === 'document') { - $elements[$dependency['type'] . '_' . $dependency['id']] = $dependency; - } - } - } - } - } - - $elements = array_values($elements); - - $elementsPerJob = (int)$request->request->get('elements_per_job', 10); + $elementsPerJob = max(1, (int) $request->request->get('elements_per_job', 10)); - // make sure elements per job is not 0 - if (!$elementsPerJob) { - $elementsPerJob = 1; - } - - // one job = X elements - $elements = array_chunk($elements, $elementsPerJob); - foreach ($elements as $chunk) { - $jobs[] = [[ - 'url' => $jobUrl, - 'method' => 'POST', - 'params' => [ - 'id' => $exportId, - 'source' => $source, - 'target' => $target, - 'data' => $this->encodeJson($chunk), - ], - ]]; - } + $result = $buildContentExportJobs( + data: $data && is_array($data) ? $data : [], + source: $source, + target: $target, + jobUrl: $jobUrl, + elementsPerJob: $elementsPerJob, + ); - return $this->adminJson([ - 'success' => true, - 'jobs' => $jobs, - 'id' => $exportId, - ]); + return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs, 'id' => $result->exportId])); } #[Route('/merge-item', name: 'opendxp_admin_translation_mergeitem', methods: ['PUT'])] - public function mergeItemAction(Request $request): JsonResponse - { + public function mergeItemAction( + Request $request, + MergeTranslationItemsHandler $mergeTranslationItems, + ): JsonResponse { $domain = $request->request->get('domain', Translation::DOMAIN_DEFAULT); - $dataList = json_decode($request->request->get('data'), true); - foreach ($dataList as $data) { - $t = Translation::getByKey($data['key'], $domain, true); - $newValue = htmlspecialchars_decode($data['current']); - $t->addTranslation($data['lg'], $newValue); - $t->setModificationDate(time()); - $t->save(); - } + $mergeTranslationItems($dataList, $domain); - return $this->adminJson([ - 'success' => true, - ]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/get-website-translation-languages', name: 'opendxp_admin_translation_getwebsitetranslationlanguages', methods: ['GET'])] - public function getWebsiteTranslationLanguagesAction(Request $request): JsonResponse + public function getWebsiteTranslationLanguagesAction(GetWebsiteTranslationLanguagesHandler $getWebsiteTranslationLanguages): JsonResponse { + $result = $getWebsiteTranslationLanguages(); + return $this->adminJson([ - 'view' => $this->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(), + 'view' => $result->view, //when no view language is defined, all languages are editable. if one view language is defined, it //may be possible that no edit language is set intentionally - 'edit' => $this->getAdminUser()->getAllowedLanguagesForEditingWebsiteTranslations(), + 'edit' => $result->edit, ]); } #[Route('/get-translation-domains', name: 'opendxp_admin_translation_gettranslationdomains', methods: ['GET'])] - public function getTranslationDomainsAction(Request $request): JsonResponse + public function getTranslationDomainsAction(GetTranslationDomainsHandler $getTranslationDomains): JsonResponse { - $translation = new Translation(); - - $domains = array_map( - static fn ($domain) => ['name' => $domain], - $translation->getDao()->getAvailableDomains(), - ); + $result = $getTranslationDomains(); - return $this->adminJson(['domains' => $domains]); + return $this->adminJson(['domains' => $result->domains]); } } diff --git a/src/Controller/Admin/User/RoleController.php b/src/Controller/Admin/User/RoleController.php new file mode 100644 index 00000000..9ffda71a --- /dev/null +++ b/src/Controller/Admin/User/RoleController.php @@ -0,0 +1,85 @@ +value)] + #[Route('/user/role-tree-get-children-by-id', name: 'opendxp_admin_user_roletreegetchildrenbyid', methods: ['GET'])] + public function roleTreeGetChildrenByIdAction( + GetRoleTreeChildrenHandler $getRoleTreeChildren, + #[MapQueryParameter] int $node, + ): JsonResponse { + return $this->adminJson($getRoleTreeChildren($node)); + } + + #[IsGranted(CorePermission::Users->value)] + #[Route('/user/role-get', name: 'opendxp_admin_user_roleget', methods: ['GET'])] + public function roleGetAction( + GetRoleHandler $getRole, + #[MapQueryParameter] int $id, + ): JsonResponse { + $result = $getRole($id); + + return $this->adminJson(ApiResponse::ok([ + 'role' => $result->role, + 'permissions' => $result->permissions, + 'classes' => $result->classes, + 'docTypes' => $result->docTypes, + 'availablePermissions' => $result->availablePermissions, + 'availablePerspectives' => $result->availablePerspectives, + 'validLanguages' => $result->validLanguages, + ])); + } + + #[IsGranted(CorePermission::Users->value)] + #[Route('/user/get-roles', name: 'opendxp_admin_user_getroles', methods: ['GET'])] + public function getRolesAction( + GetRolesHandler $getRoles, + #[MapQueryParameter] ?string $permission = null, + ): JsonResponse { + $roles = $getRoles($permission); + + return $this->adminJson(ApiResponse::ok(['total' => count($roles), 'data' => $roles])); + } + + #[IsGranted(CorePermission::ShareConfigurations->value)] + #[Route('/user/get-roles-for-sharing', name: 'opendxp_admin_user_getrolesforsharing', methods: ['GET'])] + public function getRolesForSharingAction( + GetRolesHandler $getRoles, + #[MapQueryParameter] ?string $permission = null, + ): JsonResponse { + $roles = $getRoles($permission); + + return $this->adminJson(ApiResponse::ok(['total' => count($roles), 'data' => $roles])); + } +} diff --git a/src/Controller/Admin/User/UserProfileController.php b/src/Controller/Admin/User/UserProfileController.php new file mode 100644 index 00000000..8c818817 --- /dev/null +++ b/src/Controller/Admin/User/UserProfileController.php @@ -0,0 +1,108 @@ +files->get('Filedata'); + + $uploadUserImage($id, $avatarFile); + + $response = $this->adminJson(ApiResponse::ok()); + $response->headers->set('Content-Type', 'text/html'); + + return $response; + } + + #[Route('/user/update-current-user', name: 'opendxp_admin_user_updatecurrentuser', methods: ['PUT'])] + public function updateCurrentUserAction(Request $request, UpdateCurrentUserHandler $updateCurrentUser): JsonResponse + { + if (!$request->request->has('id')) { + return $this->adminJson(false); + } + + $isPasswordReset = \OpenDxp\Tool\Session::useBag($request->getSession(), static fn (AttributeBagInterface $adminSession) => (bool) $adminSession->get('password_reset')); + + $values = $this->decodeJson($request->request->get('data'), true); + + $keyBindingsJson = $request->request->has('keyBindings') + ? $request->request->get('keyBindings') + : null; + + $updateCurrentUser( + requestedUserId: (int) $request->request->get('id'), + values: $values, + isPasswordReset: $isPasswordReset, + keyBindingsJson: $keyBindingsJson, + ); + + return $this->adminJson(ApiResponse::ok()); + } + + #[Route('/user/get-current-user', name: 'opendxp_admin_user_getcurrentuser', methods: ['GET'])] + public function getCurrentUserAction(Request $request, GetCurrentUserHandler $getCurrentUser): Response + { + $isPasswordReset = (bool) \OpenDxp\Tool\Session::useBag($request->getSession(), fn (AttributeBagInterface $adminSession) => $adminSession->get('password_reset')); + + $result = $getCurrentUser($isPasswordReset); + + $response = new Response('opendxp.currentuser = ' . $this->encodeJson($result->userData)); + $response->headers->set('Content-Type', 'text/javascript'); + + return $response; + } + + #[Route('/user/reset-my-2fa-secret', name: 'opendxp_admin_user_reset_my_2fa_secret', methods: ['PUT'])] + public function resetMy2FaSecretAction(ResetMy2FaSecretHandler $resetMy2FaSecret): JsonResponse + { + $resetMy2FaSecret(); + + return $this->adminJson(ApiResponse::ok()); + } + + #[Route('/user/get-default-key-bindings', name: 'opendxp_admin_user_getdefaultkeybindings', methods: ['GET'])] + public function getDefaultKeyBindingsAction(): JsonResponse + { + return $this->adminJson(ApiResponse::ok(['data' => UserHelper::getDefaultKeyBindings()])); + } +} diff --git a/src/Controller/Admin/UserController.php b/src/Controller/Admin/UserController.php index bdee9899..1997452e 100644 --- a/src/Controller/Admin/UserController.php +++ b/src/Controller/Admin/UserController.php @@ -1,5 +1,4 @@ value)] #[Route('/user/tree-get-children-by-id', name: 'opendxp_admin_user_treegetchildrenbyid', methods: ['GET'])] - public function treeGetChildrenByIdAction(Request $request): JsonResponse - { - $list = new User\Listing(); - $list->setCondition('parentId = ?', (int)$request->query->get('node')); - $list->setOrder('ASC'); - $list->setOrderKey('name'); - $list->load(); - - $users = []; - foreach ($list->getUsers() as $user) { - if ($user->getId() && $user->getName() !== 'system') { - $users[] = $this->getTreeNodeConfig($user); - } - } - - return $this->adminJson($users); - } - - protected function getTreeNodeConfig(User|User\Folder $user): array - { - $tmpUser = [ - 'id' => $user->getId(), - 'text' => $user->getName(), - 'elementType' => 'user', - 'type' => $user->getType(), - 'qtipCfg' => [ - 'title' => 'ID: ' . $user->getId(), - ], - ]; - - // set type specific settings - if ($user instanceof User\Folder) { - $tmpUser['leaf'] = false; - $tmpUser['iconCls'] = 'opendxp_icon_folder'; - $tmpUser['expanded'] = true; - $tmpUser['allowChildren'] = true; - - if ($user->hasChildren()) { - $tmpUser['expanded'] = false; - } else { - $tmpUser['loaded'] = true; - } - } else { - $tmpUser['leaf'] = true; - $tmpUser['iconCls'] = 'opendxp_icon_user'; - if (!$user->getActive()) { - $tmpUser['cls'] = ' opendxp_unpublished'; - } - $tmpUser['allowChildren'] = false; - $tmpUser['admin'] = $user->isAdmin(); - } - - return $tmpUser; + public function treeGetChildrenByIdAction( + GetUserTreeChildrenHandler $getUserTreeChildren, + #[MapQueryParameter] int $node = 0, + ): JsonResponse { + return $this->adminJson($getUserTreeChildren($node)); } + #[IsGranted(CorePermission::Users->value)] #[Route('/user/add', name: 'opendxp_admin_user_add', methods: ['POST'])] - public function addAction(Request $request): JsonResponse - { - try { - $type = $request->request->get('type'); - - $className = User\Service::getClassNameForType($type); - $user = $className::create([ - 'parentId' => $request->request->getInt('parentId'), - 'name' => trim($request->request->get('name', '')), - 'password' => '', - 'active' => $request->request->getBoolean('active'), - ]); - - if ($request->request->has('rid')) { - $rid = (int)$request->request->get('rid'); - $rObject = $className::getById($rid); - if ($rObject && ($type === 'user' || $type === 'role')) { - $user->setParentId($rObject->getParentId()); - if ($rObject->getClasses()) { - $user->setClasses(implode(',', $rObject->getClasses())); - } - if ($rObject->getDocTypes()) { - $user->setDocTypes(implode(',', $rObject->getDocTypes())); - } - $keys = ['asset', 'document', 'object']; - foreach ($keys as $key) { - $getter = 'getWorkspaces' . ucfirst($key); - $setter = 'setWorkspaces' . ucfirst($key); - $workspaces = $rObject->$getter(); - $clonedWorkspaces = []; - if (is_array($workspaces)) { - /** @var User\Workspace\AbstractWorkspace $workspace */ - foreach ($workspaces as $workspace) { - $vars = $workspace->getObjectVars(); - if ($key === 'object') { - $workspaceClass = \OpenDxp\Model\User\Workspace\DataObject::class; - } else { - $workspaceClass = '\\OpenDxp\\Model\\User\\Workspace\\' . ucfirst($key); - } - $newWorkspace = new $workspaceClass(); - foreach ($vars as $varKey => $varValue) { - $newWorkspace->setObjectVar($varKey, $varValue); - } - $newWorkspace->setUserId($user->getId()); - $clonedWorkspaces[] = $newWorkspace; - } - } - - $user->$setter($clonedWorkspaces); - } - $user->setPerspectives($rObject->getPerspectives()); - $user->setPermissions($rObject->getPermissions()); - if ($type === 'user') { - $user->setAdmin(false); - if ($this->getAdminUser()->isAdmin()) { - $user->setAdmin($rObject->getAdmin()); - } - $user->setActive($rObject->getActive()); - $user->setRoles($rObject->getRoles()); - $user->setWelcomeScreen($rObject->getWelcomescreen()); - $user->setMemorizeTabs($rObject->getMemorizeTabs()); - $user->setCloseWarning($rObject->getCloseWarning()); - } - $user->setWebsiteTranslationLanguagesView($rObject->getWebsiteTranslationLanguagesView()); - $user->setWebsiteTranslationLanguagesEdit($rObject->getWebsiteTranslationLanguagesEdit()); - $user->save(); - } - } - - return $this->adminJson([ - 'success' => true, - 'id' => $user->getId(), - ]); - } catch (Exception $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - /** - * @throws Exception - */ - protected function populateChildNodes(User\AbstractUser $node, array &$currentList, bool $roleMode): array + public function addAction(Request $request, AddUserHandler $addUser): JsonResponse { - $currentUser = \OpenDxp\Tool\Admin::getCurrentUser(); + $referenceId = $request->request->has('rid') ? (int) $request->request->get('rid') : null; - $list = $roleMode ? new User\Role\Listing() : new User\Listing(); - $list->setCondition('parentId = ?', $node->getId()); - $list->setOrder('ASC'); - $list->setOrderKey('name'); - $list->load(); + $result = $addUser( + type: $request->request->get('type'), + parentId: $request->request->getInt('parentId'), + name: trim($request->request->get('name', '')), + active: $request->request->getBoolean('active'), + referenceId: $referenceId, + ); - $childList = $roleMode ? $list->getRoles() : $list->getUsers(); - - foreach ($childList as $user) { - if ($user->getId() === $currentUser->getId()) { - throw new Exception('Cannot delete current user'); - } - if ($user->getId() && $currentUser->getId() && $user->getName() !== 'system') { - $currentList[] = $user; - $this->populateChildNodes($user, $currentList, $roleMode); - } - } - - return $currentList; + return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Users->value)] #[Route('/user/delete', name: 'opendxp_admin_user_delete', methods: ['DELETE'])] - public function deleteAction(Request $request): JsonResponse + public function deleteAction(Request $request, DeleteUserHandler $deleteUser): JsonResponse { - $user = User\AbstractUser::getById((int)$request->request->get('id')); - - // only admins are allowed to delete admins and folders - // because a folder might contain an admin user, so it is simply not allowed for users with the "users" permission - if (($user instanceof User\Folder && !$this->getAdminUser()->isAdmin()) || ($user instanceof User && $user->isAdmin() && !$this->getAdminUser()->isAdmin())) { - throw new Exception('You are not allowed to delete this user'); - } - - if ($user instanceof User\Role\Folder) { - $list = [$user]; - $this->populateChildNodes($user, $list, true); - $listCount = count($list); - for ($i = $listCount - 1; $i >= 0; $i--) { - // iterate over the list from the so that nothing can get "lost" - $user = $list[$i]; - $user->delete(); - } - } elseif ($user->getId()) { - $user->delete(); - } + $deleteUser($request->request->getInt('id')); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Users->value)] #[Route('/user/update', name: 'opendxp_admin_user_update', methods: ['PUT'])] - public function updateAction(Request $request, TranslatorInterface $translator): JsonResponse + public function updateAction(Request $request, UpdateUserHandler $updateUser): JsonResponse { - /** @var User|User\Role|null $user */ - $user = User\UserRole::getById($request->request->getInt('id')); - $currentUserIsAdmin = $this->getAdminUser()->isAdmin(); - - if (!$user) { - throw $this->createNotFoundException(); - } - - if ($user instanceof User && $user->isAdmin() && !$currentUserIsAdmin) { - throw $this->createAccessDeniedHttpException('Only admin users are allowed to modify admin users'); - } - - if ($request->request->has('data')) { - $values = $this->decodeJson($request->request->get('data'), true); - - if (!empty($values['password'])) { - if (strlen($values['password']) < 10) { - throw new Exception('Passwords have to be at least 10 characters long'); - } - $values['password'] = Tool\Authentication::getPasswordHash($user->getName(), $values['password']); - } - - // check if there are permissions transmitted, if so reset them all to false (they will be set later) - foreach ($values as $key => $value) { - if (str_starts_with($key, 'permission_')) { - $user->setAllAclToFalse(); - - break; - } - } - - if ($user instanceof User && isset($values['2fa_required'])) { - $user->setTwoFactorAuthentication('required', (bool) $values['2fa_required']); - } - - $user->setValues($values); - - // only admins are allowed to create admin users - // if the logged in user isn't an admin, set admin always to false - if ($user instanceof User && !$currentUserIsAdmin) { - $user->setAdmin(false); - } - - // check for permissions - $availableUserPermissionsList = new User\Permission\Definition\Listing(); - $availableUserPermissions = $availableUserPermissionsList->load(); + $values = $request->request->has('data') + ? $this->decodeJson($request->request->get('data'), true) + : null; - foreach ($availableUserPermissions as $permission) { - if (isset($values['permission_' . $permission->getKey()])) { - $user->setPermission($permission->getKey(), (bool) $values['permission_' . $permission->getKey()]); - } - } + $workspaces = $request->request->has('workspaces') + ? $this->decodeJson($request->request->get('workspaces'), true) + : null; - // check for workspaces - if ($request->request->has('workspaces')) { - $processedPaths = ['object' => [], 'asset' => [], 'document' => []]; //array to find if there are multiple entries for a path - $workspaces = $this->decodeJson($request->request->get('workspaces'), true); - foreach ($workspaces as $type => $spaces) { - $newWorkspaces = []; - foreach ($spaces as $space) { - if (in_array($space['path'], $processedPaths[$type])) { - throw new Exception('Error saving workspaces as multiple entries found for path "' . $space['path'] .'" in '.$translator->trans((string)$type, [], 'admin') . 's'); - } + $keyBindingsJson = $request->request->has('keyBindings') + ? $request->request->get('keyBindings') + : null; - $element = Element\Service::getElementByPath($type, $space['path']); - if ($element) { - $className = '\\OpenDxp\\Model\\User\\Workspace\\' . Element\Service::getBaseClassNameForElement($type); - $workspace = new $className(); - $workspace->setValues($space); + $updateUser($request->request->getInt('id'), $values, $workspaces, $keyBindingsJson); - $workspace->setCid($element->getId()); - $workspace->setCpath($element->getRealFullPath()); - $workspace->setUserId($user->getId()); - - $newWorkspaces[] = $workspace; - $processedPaths[$type][] = $space['path']; - } - } - $user->{'setWorkspaces' . ucfirst($type)}($newWorkspaces); - } - } - } - - if ($user instanceof User && $request->request->has('keyBindings')) { - $keyBindings = json_decode($request->request->get('keyBindings'), true); - $tmpArray = []; - foreach ($keyBindings as $item) { - $tmpArray[] = json_decode($item, true); - } - $tmpArray = array_values(array_filter($tmpArray)); - $tmpArray = json_encode($tmpArray); - - $user->setKeyBindings($tmpArray); - } - - $user->save(); - - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Users->value)] #[Route('/user/get', name: 'opendxp_admin_user_get', methods: ['GET'])] - public function getAction(Request $request): JsonResponse - { - $userId = (int)$request->query->get('id'); - if ($userId < 1) { - throw $this->createNotFoundException(); - } - - $user = User::getById($userId); - - if (!$user) { - throw $this->createNotFoundException(); - } - - if ($user->isAdmin() && !$this->getAdminUser()->isAdmin()) { - throw $this->createAccessDeniedHttpException('Only admin users are allowed to modify admin users'); - } - - // workspaces - $types = ['asset', 'document', 'object']; - foreach ($types as $type) { - /** @var Workspace\Document[]|Workspace\Asset[]|Workspace\DataObject[] $workspaces */ - $workspaces = $user->{'getWorkspaces' . ucfirst($type)}(); - foreach ($workspaces as $wKey => $workspace) { - $el = Element\Service::getElementById($type, $workspace->getCid()); - if ($el) { - $workspaceVars = $workspace->getObjectVars(); - $workspaceVars['path'] = $el->getRealFullPath(); - $workspaces[$wKey] = $workspaceVars; - } - } - $user->{'setWorkspaces' . ucfirst($type)}($workspaces); - } - - // object <=> user dependencies - $userObjects = DataObject\Service::getObjectsReferencingUser($user->getId()); - $userObjectData = []; - $hasHidden = false; - - foreach ($userObjects as $o) { - if ($o->isAllowed('list')) { - $userObjectData[] = [ - 'path' => $o->getRealFullPath(), - 'id' => $o->getId(), - 'subtype' => $o->getClass()->getName(), - ]; - } else { - $hasHidden = true; - } - } - - // get available permissions - $availableUserPermissionsList = new User\Permission\Definition\Listing(); - $availableUserPermissionsList->setOrderKey('category'); - $availableUserPermissions = $availableUserPermissionsList->load(); - - $availableUserPermissionsData = []; - foreach ($availableUserPermissions as $availableUserPermission) { - $availableUserPermissionsData[] = $availableUserPermission->getObjectVars(); - } - - // get available roles - $list = new User\Role\Listing(); - $list->setCondition('`type` = ?', ['role']); - $list->load(); - - $roles = []; - foreach ($list->getItems() as $role) { - $roles[] = [$role->getId(), $role->getName()]; - } - - // unset confidential informations - $userData = $user->getObjectVars(); - $userData['roles'] = $user->getRoles(); - $userData['docTypes'] = $user->getDocTypes(); - $contentLanguages = Tool\Admin::reorderWebsiteLanguages($user, Tool::getValidLanguages()); - $userData['contentLanguages'] = $contentLanguages; - $userData['twoFactorAuthentication']['isActive'] = ($user->getTwoFactorAuthentication('enabled') || $user->getTwoFactorAuthentication('secret')); - unset($userData['password'], $userData['twoFactorAuthentication']['secret']); - $userData['hasImage'] = $user->hasImage(); - - $availablePerspectives = Config::getAvailablePerspectives(null); - - return $this->adminJson([ - 'success' => true, - 'user' => $userData, - 'roles' => $roles, - 'permissions' => $user->generatePermissionList(), - 'availablePermissions' => $availableUserPermissionsData, - 'availablePerspectives' => $availablePerspectives, - 'validLanguages' => Tool::getValidLanguages(), - 'validLocales' => Tool::getSupportedJSLocales(), - 'objectDependencies' => [ - 'hasHidden' => $hasHidden, - 'dependencies' => $userObjectData, - ], - ]); + public function getAction( + GetUserHandler $getUser, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { + $result = $getUser($id); + + return $this->adminJson(ApiResponse::ok([ + 'user' => $result->userData, + 'roles' => $result->roles, + 'permissions' => $result->permissions, + 'availablePermissions' => $result->availablePermissions, + 'availablePerspectives' => $result->availablePerspectives, + 'validLanguages' => $result->validLanguages, + 'validLocales' => $result->validLocales, + 'objectDependencies' => $result->objectDependencies, + ])); } #[Route('/user/get-minimal', name: 'opendxp_admin_user_getminimal', methods: ['GET'])] - public function getMinimalAction(Request $request): JsonResponse - { - $user = User::getById((int)$request->query->get('id')); - - if (!$user) { - throw $this->createNotFoundException(); - } - - $minimalUserData['id'] = $user->getId(); - $minimalUserData['admin'] = $user->isAdmin(); - $minimalUserData['active'] = $user->isActive(); - $minimalUserData['permissionInfo']['assets'] = $user->isAllowed('assets'); - $minimalUserData['permissionInfo']['documents'] = $user->isAllowed('documents'); - $minimalUserData['permissionInfo']['objects'] = $user->isAllowed('objects'); - - return $this->adminJson($minimalUserData); - } - - #[Route('/user/upload-current-user-image', name: 'opendxp_admin_user_uploadcurrentuserimage', methods: ['POST'])] - public function uploadCurrentUserImageAction(Request $request): JsonResponse - { - $user = $this->getAdminUser(); - - if ($user !== null) { - - if ($user->getId() === (int)$request->query->get('id')) { - return $this->uploadImageAction($request); - } - - Logger::warn('prevented save current user, because ids do not match. '); - - return $this->adminJson(false); - } - - return $this->adminJson(false); - } - - #[Route('/user/update-current-user', name: 'opendxp_admin_user_updatecurrentuser', methods: ['PUT'])] - public function updateCurrentUserAction(Request $request, ValidatorInterface $validator): JsonResponse - { - //TODO Can be completely validated with Symfony Validator - $user = $this->getAdminUser(); - - $isPasswordReset = Tool\Session::useBag($request->getSession(), static fn (AttributeBagInterface $adminSession) => (bool) $adminSession->get('password_reset')); - - if ($user !== null && $request->request->has('id')) { - if ($user->getId() === (int) $request->request->get('id')) { - $values = $this->decodeJson($request->request->get('data'), true); - - unset($values['name'], $values['id'], $values['admin'], $values['permissions'], $values['roles'], $values['active']); - - if (!empty($values['new_password'])) { - $oldPasswordCheck = false; - - if ($isPasswordReset) { - // if the user want to reset the password, the old password isn't required - $oldPasswordCheck = true; - } elseif (!empty($values['old_password'])) { - $errors = $validator->validate($values['old_password'], [new UserPassword()]); - - if (count($errors) === 0) { - $oldPasswordCheck = true; - } - } - - if (strlen($values['new_password']) < 10) { - throw new Exception('Passwords have to be at least 10 characters long'); - } - - if ($oldPasswordCheck && $values['new_password'] == $values['retype_password']) { - - if (Tool\Authentication::verifyPassword($user, $values['new_password'])) { - throw new Exception('The new password cannot be the same as the old one'); - } - - $values['password'] = Tool\Authentication::getPasswordHash($user->getName(), $values['new_password']); - } else { - if (!$oldPasswordCheck) { - return $this->adminJson(['success' => false, 'message' => 'incorrect_password']); - } - - return $this->adminJson(['success' => false, 'message' => 'password_cannot_be_changed']); - } - } - - $user->setValues($values); - - if ($request->request->has('keyBindings')) { - $keyBindings = json_decode($request->request->get('keyBindings'), true); - $tmpArray = []; - foreach ($keyBindings as $item) { - $tmpArray[] = json_decode($item, true); - } - $tmpArray = array_values(array_filter($tmpArray)); - $tmpArray = json_encode($tmpArray); - - $user->setKeyBindings($tmpArray); - } - - $user->save(); - - return $this->adminJson(['success' => true]); - } - Logger::warn('prevented save current user, because ids do not match. '); - - return $this->adminJson(false); - } - - return $this->adminJson(false); - } - - #[Route('/user/get-current-user', name: 'opendxp_admin_user_getcurrentuser', methods: ['GET'])] - public function getCurrentUserAction(Request $request): Response - { - $user = $this->getAdminUser(); - - $list = new User\Permission\Definition\Listing(); - $definitions = $list->load(); - - foreach ($definitions as $definition) { - $user->setPermission($definition->getKey(), $user->isAllowed($definition->getKey())); - } - - // unset confidential informations - $userData = $user->getObjectVars(); - $contentLanguages = Tool\Admin::reorderWebsiteLanguages($user, Tool::getValidLanguages()); - $userData['contentLanguages'] = $contentLanguages; - $userData['keyBindings'] = UserHelper::getDefaultKeyBindings($user); - - unset($userData['password']); - $userData['twoFactorAuthentication'] = $user->getTwoFactorAuthentication(); - unset($userData['twoFactorAuthentication']['secret']); - $userData['twoFactorAuthentication']['isActive'] = $user->getTwoFactorAuthentication('enabled') && $user->getTwoFactorAuthentication('secret'); - $userData['hasImage'] = $user->hasImage(); - - $userData['isPasswordReset'] = Tool\Session::useBag($request->getSession(), fn (AttributeBagInterface $adminSession) => $adminSession->get('password_reset')); - - $userData['validLocales'] = Tool::getSupportedJSLocales(); - - $response = new Response('opendxp.currentuser = ' . $this->encodeJson($userData)); - $response->headers->set('Content-Type', 'text/javascript'); - - return $response; - } - - // ROLES - - #[Route('/user/role-tree-get-children-by-id', name: 'opendxp_admin_user_roletreegetchildrenbyid', methods: ['GET'])] - public function roleTreeGetChildrenByIdAction(Request $request): JsonResponse - { - $list = new User\Role\Listing(); - $list->setCondition('parentId = ?', (int)$request->query->get('node')); - $list->load(); - - $roles = []; - foreach ($list->getItems() as $role) { - $roles[] = $this->getRoleTreeNodeConfig($role); - } - - return $this->adminJson($roles); - } - - protected function getRoleTreeNodeConfig(User\Role|User\Role\Folder $role): array - { - $tmpUser = [ - 'id' => $role->getId(), - 'text' => $role->getName(), - 'elementType' => 'role', - 'qtipCfg' => [ - 'title' => 'ID: ' . $role->getId(), - ], - ]; - - // set type specific settings - if ($role instanceof User\Role\Folder) { - $tmpUser['leaf'] = false; - $tmpUser['iconCls'] = 'opendxp_icon_folder'; - $tmpUser['expanded'] = true; - $tmpUser['allowChildren'] = true; - - if ($role->hasChildren()) { - $tmpUser['expanded'] = false; - } else { - $tmpUser['loaded'] = true; - } - } else { - $tmpUser['leaf'] = true; - $tmpUser['iconCls'] = 'opendxp_icon_roles'; - $tmpUser['allowChildren'] = false; - } - - return $tmpUser; - } - - #[Route('/user/role-get', name: 'opendxp_admin_user_roleget', methods: ['GET'])] - public function roleGetAction(Request $request): JsonResponse - { - $role = User\Role::getById((int)$request->query->get('id')); - - if (!$role) { - throw $this->createNotFoundException(); - } - - // workspaces - $types = ['asset', 'document', 'object']; - foreach ($types as $type) { - /** @var Workspace\Document[]|Workspace\Asset[]|Workspace\DataObject[] $workspaces */ - $workspaces = $role->{'getWorkspaces' . ucfirst($type)}(); - foreach ($workspaces as $wKey => $workspace) { - $el = Element\Service::getElementById($type, $workspace->getCid()); - if ($el) { - $workspaceVars = $workspace->getObjectVars(); - $workspaceVars['path'] = $el->getRealFullPath(); - $workspaces[$wKey] = $workspaceVars; - } - } - $role->{'setWorkspaces' . ucfirst($type)}($workspaces); - } - - $replaceFn = (static fn ($value) => $value->getObjectVars()); - - // get available permissions - $availableUserPermissionsList = new User\Permission\Definition\Listing(); - $availableUserPermissionsList->setOrderKey('category'); - $availableUserPermissions = $availableUserPermissionsList->load(); - $availableUserPermissions = array_map($replaceFn, $availableUserPermissions); - - $availablePerspectives = Config::getAvailablePerspectives(null); + public function getMinimalAction( + GetMinimalUserHandler $getMinimalUser, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { + $result = $getMinimalUser($id); return $this->adminJson([ - 'success' => true, - 'role' => $role->getObjectVars(), - 'permissions' => $role->generatePermissionList(), - 'classes' => $role->getClasses(), - 'docTypes' => $role->getDocTypes(), - 'availablePermissions' => $availableUserPermissions, - 'availablePerspectives' => $availablePerspectives, - 'validLanguages' => Tool::getValidLanguages(), + 'id' => $result->id, + 'admin' => $result->admin, + 'active' => $result->active, + 'permissionInfo' => $result->permissionInfo, ]); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Users->value)] #[Route('/user/upload-image', name: 'opendxp_admin_user_uploadimage', methods: ['POST'])] - public function uploadImageAction(Request $request): JsonResponse - { - $requestUserId = $request->query->has('id') ? (int)$request->query->get('id') : null; - $userObj = User::getById($this->getUserId($requestUserId)); - - if (!$userObj) { - throw $this->createNotFoundException(); - } - - if ($userObj->isAdmin() && !$this->getAdminUser()->isAdmin()) { - throw $this->createAccessDeniedHttpException('Only admin users are allowed to modify admin users'); - } + public function uploadImageAction( + Request $request, + UploadUserImageHandler $uploadUserImage, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { + $targetUserId = $request->query->has('id') ? $id : null; - //Check if uploaded file is an image + /** @var UploadedFile $avatarFile */ $avatarFile = $request->files->get('Filedata'); - $assetType = Asset::getTypeFromMimeMapping($avatarFile->getMimeType(), $avatarFile->getFileName()); - - if (!$avatarFile instanceof UploadedFile || $assetType !== 'image') { - throw new Exception('Unsupported file format.'); - } - - $userObj->setImage($avatarFile->getPathname()); + $uploadUserImage($targetUserId, $avatarFile); // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in // Ext.form.Action.Submit and mark the submission as failed - - $response = $this->adminJson(['success' => true]); + $response = $this->adminJson(ApiResponse::ok()); $response->headers->set('Content-Type', 'text/html'); return $response; } - /** - * @throws Exception - */ #[Route('/user/delete-image', name: 'opendxp_admin_user_deleteimage', methods: ['DELETE'])] - public function deleteImageAction(Request $request): JsonResponse - { - $requestUserId = $request->query->has('id') ? (int)$request->query->get('id') : null; - $userObj = User::getById($this->getUserId($requestUserId)); - - if (!$userObj) { - throw $this->createNotFoundException(); - } - - $adminUser = $this->getAdminUser(); - - if (!$adminUser->isAdmin()) { - if ($userObj->isAdmin()) { - throw $this->createAccessDeniedHttpException('Only admin users are allowed to modify admin users'); - } - - if ($adminUser->getId() !== $userObj->getId()) { - throw $this->createAccessDeniedHttpException('Only admin users are allowed to modify users other than themselves'); - } - } - - $userObj->setImage(null); + public function deleteImageAction( + Request $request, + DeleteUserImageHandler $deleteUserImage, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { + $targetUserId = $request->query->has('id') ? $id : null; + $deleteUserImage($targetUserId); - return $this->adminJson(['success' => true]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/user/disable-2fa', name: 'opendxp_admin_user_disable2fasecret', methods: ['DELETE'])] - public function disable2FaSecretAction(Request $request): JsonResponse + public function disable2FaSecretAction(Disable2FaHandler $disable2Fa): JsonResponse { - $user = $this->getAdminUser(); - $success = false; - - if (!$user->getTwoFactorAuthentication('required')) { - $user->setTwoFactorAuthentication([]); - $user->save(); - - $success = true; + try { + $disable2Fa(); + } catch (\Throwable $e) { + return $this->adminJson(ApiResponse::error($e->getMessage())); } - return $this->adminJson([ - 'success' => $success, - ]); + return $this->adminJson(ApiResponse::ok()); } + #[IsGranted(CorePermission::Users->value)] #[Route('/user/reset-2fa-secret', name: 'opendxp_admin_user_reset2fasecret', methods: ['PUT'])] - public function reset2FaSecretAction(Request $request): JsonResponse + public function reset2FaSecretAction(Request $request, Reset2FaSecretHandler $reset2FaSecret): JsonResponse { - $user = User::getById((int)$request->request->get('id')); + $reset2FaSecret($request->request->getInt('id')); - if (!$user) { - throw $this->createNotFoundException(); - } - - $user->setTwoFactorAuthentication('enabled', false); - $user->setTwoFactorAuthentication('secret', ''); - $user->save(); - - return $this->adminJson([ - 'success' => true, - ]); - } - - #[Route('/user/reset-my-2fa-secret', name: 'opendxp_admin_user_reset_my_2fa_secret', methods: ['PUT'])] - public function resetMy2FaSecretAction(Request $request): JsonResponse - { - $user = $this->getAdminUser(); - $user->setTwoFactorAuthentication('required', true); - $user->setTwoFactorAuthentication('enabled', false); - $user->setTwoFactorAuthentication('secret', ''); - $user->save(); - - return $this->adminJson([ - 'success' => true, - ]); + return $this->adminJson(ApiResponse::ok()); } #[Route('/user/get-image', name: 'opendxp_admin_user_getimage', methods: ['GET'])] - public function getImageAction(Request $request): StreamedResponse - { - $requestUserId = $request->query->has('id') ? (int)$request->query->get('id') : null; - $userObj = User::getById($this->getUserId($requestUserId)); - - if (!$userObj) { - throw $this->createNotFoundException(); - } - $stream = $userObj->getImage(); + public function getImageAction( + Request $request, + GetUserImageHandler $getUserImage, + #[MapQueryParameter] int $id = 0, + ): StreamedResponse { + $targetUserId = $request->query->has('id') ? $id : null; + $stream = $getUserImage($targetUserId); return new StreamedResponse(function () use ($stream): void { fpassthru($stream); @@ -826,269 +211,66 @@ public function getImageAction(Request $request): StreamedResponse ]); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Users->value)] #[Route('/user/get-token-login-link', name: 'opendxp_admin_user_gettokenloginlink', methods: ['GET'])] - public function getTokenLoginLinkAction(Request $request, TranslatorInterface $translator): JsonResponse - { - $user = User::getById((int) $request->query->get('id')); - - if (!$user) { - return $this->adminJson([ - 'success' => false, - 'message' => $translator->trans('login_token_invalid_user_error', [], 'admin'), - ], Response::HTTP_NOT_FOUND); - } - - if ($user->isAdmin() && !$this->getAdminUser()->isAdmin()) { - return $this->adminJson([ - 'success' => false, - 'message' => $translator->trans('login_token_as_admin_non_admin_user_error', [], 'admin'), - ], Response::HTTP_FORBIDDEN); - } - - if (empty($user->getPassword())) { - return $this->adminJson([ - 'success' => false, - 'message' => $translator->trans('login_token_no_password_error', [], 'admin'), - ], Response::HTTP_FORBIDDEN); + public function getTokenLoginLinkAction( + GetTokenLoginLinkHandler $getTokenLoginLink, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { + try { + $result = $getTokenLoginLink($id); + } catch (\Throwable $e) { + return $this->adminJson(ApiResponse::error($e->getMessage())); } - $token = Tool\Authentication::generateTokenByUser($user); - $link = $this->generateCustomUrl([ - 'token' => $token, - ]); - - return $this->adminJson([ - 'success' => true, - 'link' => $link, - ]); + return $this->adminJson(ApiResponse::ok(['link' => $result->link])); } + #[IsGranted(CorePermission::Users->value)] #[Route('/user/search', name: 'opendxp_admin_user_search', methods: ['GET'])] - public function searchAction(Request $request): JsonResponse - { - $q = '%' . $request->query->get('query') . '%'; - - $list = new User\Listing(); - $list->setCondition('name LIKE ? OR firstname LIKE ? OR lastname LIKE ? OR email LIKE ? OR id = ?', [$q, $q, $q, $q, (int)$request->query->get('query')]); - $list->setOrder('ASC'); - $list->setOrderKey('name'); - - $users = []; - foreach ($list->getUsers() as $user) { - if ($user->getId() && $user->getName() !== 'system') { - $users[] = [ - 'id' => $user->getId(), - 'name' => $user->getName(), - 'email' => $user->getEmail(), - 'firstname' => $user->getFirstname(), - 'lastname' => $user->getLastname(), - ]; - } - } - - return $this->adminJson([ - 'success' => true, - 'users' => $users, - ]); - } - - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - // check permissions - $unrestrictedActions = [ - 'getCurrentUserAction', 'updateCurrentUserAction', 'getAvailablePermissionsAction', 'getMinimalAction', - 'getImageAction', 'uploadCurrentUserImageAction', 'disable2FaSecretAction', 'renew2FaSecretAction', - 'getUsersForSharingAction', 'getRolesForSharingAction', 'deleteImageAction', - ]; - - $this->checkActionPermission($event, 'users', $unrestrictedActions); + public function searchAction( + SearchUsersHandler $searchUsers, + #[MapQueryParameter] ?string $query = null, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['users' => $searchUsers($query)])); } + #[IsGranted(CorePermission::ShareConfigurations->value)] #[Route('/user/get-users-for-sharing', name: 'opendxp_admin_user_getusersforsharing', methods: ['GET'])] - public function getUsersForSharingAction(Request $request): JsonResponse - { - $this->checkPermission('share_configurations'); - - return $this->getUsersAction($request); - } - - #[Route('/user/get-roles-for-sharing', name: 'opendxp_admin_user_getrolesforsharing', methods: ['GET'])] - public function getRolesForSharingAction(Request $request): JsonResponse - { - $this->checkPermission('share_configurations'); - - return $this->getRolesAction($request); + public function getUsersForSharingAction( + GetUsersHandler $getUsers, + #[MapQueryParameter(name: 'include_current_user')] ?string $includeCurrentUser = null, + #[MapQueryParameter] ?string $permission = null, + ): JsonResponse { + return $this->getUsersAction($getUsers, $includeCurrentUser, $permission); } + #[IsGranted(CorePermission::Users->value)] #[Route('/user/get-users', name: 'opendxp_admin_user_getusers', methods: ['GET'])] - public function getUsersAction(Request $request): JsonResponse - { - $users = []; - - // get available user - $list = new User\Listing(); - - $conditions = [ 'type = "user"' ]; - - if (!$request->query->get('include_current_user')) { - $conditions[] = 'id != ' . $this->getAdminUser()->getId(); - } - - $list->setCondition(implode(' AND ', $conditions)); - - $list->load(); - $userList = $list->getUsers(); - - foreach ($userList as $user) { - if (!$request->query->get('permission') || $user->isAllowed($request->query->get('permission'))) { - $users[] = [ - 'id' => $user->getId(), - 'label' => $user->getUsername(), - ]; - } - } - - return $this->adminJson(['success' => true, 'total' => count($users), 'data' => $users]); - } - - #[Route('/user/get-roles', name: 'opendxp_admin_user_getroles', methods: ['GET'])] - public function getRolesAction(Request $request): JsonResponse - { - $roles = []; - $list = new User\Role\Listing(); - - $list->setCondition('`type` = "role"'); - $list->load(); - $roleList = $list->getRoles(); - - foreach ($roleList as $role) { - if (!$request->query->has('permission') || in_array($request->query->get('permission'), $role->getPermissions())) { - $roles[] = [ - 'id' => $role->getId(), - 'label' => $role->getName(), - ]; - } - } - - return $this->adminJson(['success' => true, 'total' => count($roles), 'data' => $roles]); - } + public function getUsersAction( + GetUsersHandler $getUsers, + #[MapQueryParameter(name: 'include_current_user')] ?string $includeCurrentUser = null, + #[MapQueryParameter] ?string $permission = null, + ): JsonResponse { + $users = $getUsers( + includeCurrentUser: (bool) $includeCurrentUser, + permission: $permission, + ); - #[Route('/user/get-default-key-bindings', name: 'opendxp_admin_user_getdefaultkeybindings', methods: ['GET'])] - public function getDefaultKeyBindingsAction(Request $request): JsonResponse - { - return $this->adminJson(['success' => true, 'data' => UserHelper::getDefaultKeyBindings()]); + return $this->adminJson(ApiResponse::ok(['total' => count($users), 'data' => $users])); } - /** - * @throws Exception - */ + #[IsGranted(CorePermission::Users->value)] #[Route('/user/invitationlink', name: 'opendxp_admin_user_invitationlink', methods: ['POST'])] public function invitationLinkAction( Request $request, - TranslatorInterface $translator, - RouterInterface $router, - GeneralHostResolver $generalHostResolver + SendInvitationLinkHandler $sendInvitationLink, + GeneralHostResolver $generalHostResolver, ): JsonResponse { + $username = (string) $request->request->get('username', ''); + $domain = $generalHostResolver->resolve(['source' => $request]) ?? ''; + $result = $sendInvitationLink($username, $domain); - $success = false; - $message = ''; - - if ($username = $request->request->get('username')) { - $user = User::getByName($username); - if ($user instanceof User) { - if (!$user->isActive()) { - $message .= 'User inactive
'; - } - - if (!$user->getEmail()) { - $message .= 'User has no email address
'; - } - } else { - $message .= 'User unknown
'; - } - - if (empty($message)) { - //generate random password if user has no password - if (!$user->getPassword()) { - $user->setPassword(bin2hex(random_bytes(16))); - $user->save(); - } - - $token = Tool\Authentication::generateTokenByUser($user); - - $domain = $generalHostResolver->resolve(['source' => $request]); - if (!$domain) { - throw new Exception('No main domain set in system settings, unable to generate login invitation link'); - } - - $context = $router->getContext(); - $context->setHost($domain); - - $loginUrl = $this->generateCustomUrl([ - 'token' => $token, - 'reset' => true, - ]); - - try { - $mail = Tool::getMail([$user->getEmail()], 'OpenDXP login invitation for ' . Tool::getHostname()); - $mail->setIgnoreDebugMode(true); - $mail->text("Login to OpenDXP and change your password using the following link. This temporary login link will expire in 24 hours: \r\n\r\n" . $loginUrl); - $mail->send(); - - $success = true; - $message = sprintf($translator->trans('invitation_link_sent', [], 'admin_ext'), $user->getEmail()); - } catch (Exception) { - $message .= 'could not send email'; - } - } - } - - return $this->adminJson([ - 'success' => $success, - 'message' => $message, - ]); - } - - protected function getUserId(?int $requestedUserId): int - { - $currentAdminUserId = $this->getAdminUser()->getId(); - - if ($requestedUserId !== null) { - - if ($currentAdminUserId !== $requestedUserId) { - $this->checkPermission('users'); - } - - return $requestedUserId; - } - - return $currentAdminUserId; - } - - /** - * @param int $referenceType //UrlGeneratorInterface::ABSOLUTE_URL, ABSOLUTE_PATH, RELATIVE_PATH, NETWORK_PATH - * - * @return string The generated URL - */ - private function generateCustomUrl(array $params, string $fallbackUrl = 'opendxp_admin_login_check', int $referenceType = UrlGeneratorInterface::ABSOLUTE_URL): string - { - try { - $adminEntryPointRoute = $this->getParameter('opendxp_admin.custom_admin_route_name'); - - //try to generate invitation link for custom admin point - $loginUrl = $this->generateUrl($adminEntryPointRoute, $params, $referenceType); - } catch (Exception) { - //use default login check for invitation link - $loginUrl = $this->generateUrl($fallbackUrl, $params, $referenceType); - } - - return $loginUrl; + return $this->adminJson(ApiResponse::fromBool($result->success, ['message' => $result->message])); } } diff --git a/src/Controller/Admin/WorkflowController.php b/src/Controller/Admin/WorkflowController.php index ec898955..04dbf003 100644 --- a/src/Controller/Admin/WorkflowController.php +++ b/src/Controller/Admin/WorkflowController.php @@ -1,5 +1,4 @@ getWorkflowIfExists($this->element, (string) $request->request->get('workflowName')); - - if (empty($workflow)) { - $wfConfig = [ - 'message' => 'workflow not found', - ]; - } else { - //this is the default returned workflow data - $wfConfig = [ - 'message' => '', - 'notes_enabled' => false, - 'notes_required' => false, - 'additional_fields' => [], - ]; - - $enabledTransitions = $workflow->getEnabledTransitions($this->element); - $transition = null; - foreach ($enabledTransitions as $_transition) { - if ($_transition->getName() === $request->request->get('transitionName')) { - $transition = $_transition; - } - } + [$ctype, $cid] = $this->resolveCtypeAndCid($request); + $result = $getWorkflowForm( + ctype: $ctype, + cid: $cid, + workflowName: $request->request->getString('workflowName'), + transitionName: $request->request->getString('transitionName'), + ); - if (!$transition instanceof Transition) { - $wfConfig['message'] = sprintf('transition %s currently not allowed', (string) $request->request->get('transitionName')); - } else { - $wfConfig['notes_required'] = $transition->getNotesCommentRequired(); - $wfConfig['additional_fields'] = []; - } - } + $wfConfig = [ + 'message' => $result->message, + 'notes_enabled' => $result->notesEnabled, + 'notes_required' => $result->notesRequired, + 'additional_fields' => $result->additionalFields, + ]; } catch (Exception $e) { - $wfConfig['message'] = $e->getMessage(); + $wfConfig = ['message' => $e->getMessage()]; } return $this->adminJson($wfConfig); } #[Route('/submit-workflow-transition', name: 'opendxp_admin_workflow_submitworkflowtransition', methods: ['POST'])] - public function submitWorkflowTransitionAction(Request $request, Registry $workflowRegistry, Manager $workflowManager): JsonResponse - { - $workflowOptions = $request->request->all('workflow'); - $workflow = $workflowRegistry->get($this->element, $request->request->get('workflowName')); - - if ($workflow->can($this->element, $request->request->get('transition'))) { - try { - $workflowManager->applyWithAdditionalData($workflow, $this->element, $request->request->get('transition'), $workflowOptions, true); - - $data = [ - 'success' => true, - 'callback' => 'reloadObject', - ]; - } catch (ValidationException $e) { - $reason = ''; - if (count($e->getSubItems()) > 0) { - $reason = ''; - } - - $data = [ - 'success' => false, - 'message' => $e->getMessage(), - 'reasons' => [$reason], + public function submitWorkflowTransitionAction( + Request $request, + SubmitWorkflowTransitionHandler $submitWorkflowTransition, + ): JsonResponse { + try { + [$ctype, $cid] = $this->resolveCtypeAndCid($request); + $result = $submitWorkflowTransition( + ctype: $ctype, + cid: $cid, + workflowName: $request->request->getString('workflowName'), + transition: $request->request->getString('transition'), + workflowOptions: $request->request->all('workflow'), + ); - ]; - } catch (Exception $e) { - $data = [ - 'success' => false, - 'message' => 'error performing action on this element', - 'reasons' => [$e->getMessage()], - ]; + if ($result->blocked) { + return $this->adminJson(ApiResponse::error('transition failed', ['reasons' => $result->blockerReasons])); } - } else { - $blockTransitionList = $workflow->buildTransitionBlockerList($this->element, $request->request->get('transition')); - $reasons = array_map(static fn ($blockTransitionItem) => $blockTransitionItem->getMessage(), iterator_to_array($blockTransitionList->getIterator(), true)); + return $this->adminJson(ApiResponse::ok(['callback' => 'reloadObject'])); + } catch (ValidationException $e) { + $reason = ''; + if (count($e->getSubItems()) > 0) { + $reason = ''; + } - $data = [ - 'success' => false, - 'message' => 'transition failed', - 'reasons' => $reasons, - ]; + return $this->adminJson(ApiResponse::error($e->getMessage(), ['reasons' => [$reason]])); + } catch (Exception $e) { + return $this->adminJson(ApiResponse::error('error performing action on this element', ['reasons' => [$e->getMessage()]])); } - - return $this->adminJson($data); } #[Route('/submit-global-action', name: 'opendxp_admin_workflow_submitglobal', methods: ['POST'])] public function submitGlobalAction( Request $request, - Registry $workflowRegistry, - Manager $workflowManager + SubmitGlobalActionHandler $submitGlobalAction, ): JsonResponse { - $workflowOptions = $request->request->all('workflow'); - $workflow = $workflowRegistry->get($this->element, $request->request->get('workflowName')); - - $globalAction = $workflowManager->getGlobalAction( - $request->request->get('workflowName'), - $request->request->get('transition') - ); - $saveSubject = !$globalAction || $globalAction->getSaveSubject(); - try { - $workflowManager->applyGlobalAction( - $workflow, - $this->element, $request->request->get('transition'), - $workflowOptions, $saveSubject + [$ctype, $cid] = $this->resolveCtypeAndCid($request); + $submitGlobalAction( + ctype: $ctype, + cid: $cid, + workflowName: $request->request->getString('workflowName'), + transition: $request->request->getString('transition'), + workflowOptions: $request->request->all('workflow'), ); - $data = [ - 'success' => true, - 'callback' => 'reloadObject', - ]; + return $this->adminJson(ApiResponse::ok(['callback' => 'reloadObject'])); } catch (ValidationException $e) { $reason = ''; if (count($e->getSubItems()) > 0) { $reason = ''; } - $data = [ - 'success' => false, - 'message' => $e->getMessage(), - 'reasons' => [$reason], - - ]; + return $this->adminJson(ApiResponse::error($e->getMessage(), ['reasons' => [$reason]])); } catch (Exception $e) { - $data = [ - 'success' => false, - 'message' => 'error performing action on this element', - 'reasons' => [$e->getMessage()], - ]; + return $this->adminJson(ApiResponse::error('error performing action on this element', ['reasons' => [$e->getMessage()]])); } - - return $this->adminJson($data); } - /** - * Returns the JSON needed by the workflow elements detail tab store - * - * @throws Exception - */ #[Route('/get-workflow-details', name: 'opendxp_admin_workflow_getworkflowdetailsstore')] - public function getWorkflowDetailsStore(Request $request, Manager $workflowManager, StatusInfo $placeStatusInfo, RouterInterface $router, ActionsButtonService $actionsButtonService): JsonResponse - { - $data = []; - - foreach ($workflowManager->getAllWorkflowsForSubject($this->element) as $workflow) { - $workflowConfig = $workflowManager->getWorkflowConfig($workflow->getName()); - - $svg = null; - $msg = ''; - - try { - $svg = $this->getWorkflowSvg($workflow); - } catch (InvalidArgumentException $e) { - $msg = $e->getMessage(); - } - - $url = $router->generate( - 'opendxp_admin_workflow_show_graph', - [ - 'cid' => $request->query->get('cid'), - 'ctype' => $request->query->get('ctype'), - 'workflow' => $workflow->getName(), - ] - ); - - $allowedTransitions = $actionsButtonService->getAllowedTransitions($workflow, $this->element); - $globalActions = $actionsButtonService->getGlobalActions($workflow, $this->element); - - $data[] = [ - 'workflowName' => $this->translator->trans($workflowConfig->getLabel(), [], 'admin'), - 'placeInfo' => $placeStatusInfo->getAllPalacesHtml($this->element, $workflow->getName()), - 'graph' => $msg ?: '
'.$svg.'
', - 'allowedTransitions' => $allowedTransitions, - 'globalActions' => $globalActions, - ]; - } + public function getWorkflowDetailsStore( + GetWorkflowDetailsHandler $getWorkflowDetails, + #[MapQueryParameter] string $ctype, + #[MapQueryParameter] int $cid, + ): JsonResponse { + $result = $getWorkflowDetails(ctype: $ctype, cid: $cid); - return $this->adminJson([ - 'data' => $data, - 'success' => true, - 'total' => count($data), - ]); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => count($result->data)])); } - /** - * Returns the JSON needed by the workflow elements detail tab store - * - * @throws Exception - */ #[Route('/show-graph', name: 'opendxp_admin_workflow_show_graph', methods: ['GET'])] - public function showGraph(Request $request, Manager $workflowManager): Response - { - $workflow = $workflowManager->getWorkflowByName($request->query->get('workflow')); - - $response = new Response($this->getWorkflowSvg($workflow)); + public function showGraph( + GetWorkflowSvgHandler $getWorkflowSvg, + #[MapQueryParameter] string $ctype, + #[MapQueryParameter] int $cid, + #[MapQueryParameter] ?string $workflowName = null, + ): Response { + $svg = $getWorkflowSvg(ctype: $ctype, cid: $cid, workflowName: $workflowName); + + $response = new Response($svg); $response->headers->set('Content-Type', 'image/svg+xml'); return $response; } - /** - * Get custom HTML for the workflow transition submit modal, depending whether it is configured or not. - * - * @throws Exception - */ #[Route('/modal-custom-html', name: 'opendxp_admin_workflow_modal_custom_html', methods: ['POST'])] - public function getModalCustomHtml(Request $request, Registry $workflowRegistry, Manager $manager): JsonResponse - { - $workflow = $workflowRegistry->get($this->element, $request->request->get('workflowName')); - - if ($request->request->get('isGlobalAction') === 'true') { - $globalAction = $manager->getGlobalAction($workflow->getName(), $request->request->get('transition')); - if ($globalAction) { - return $this->customHtmlResponse($globalAction->getCustomHtmlService()); - } - } elseif ($workflow->can($this->element, $request->request->get('transition'))) { - $enabledTransitions = $workflow->getEnabledTransitions($this->element); - $transition = null; - foreach ($enabledTransitions as $_transition) { - if ($_transition->getName() === $request->request->get('transition')) { - $transition = $_transition; - } - } - - if ($transition instanceof Transition) { - return $this->customHtmlResponse($transition->getCustomHtmlService()); - } - } - - $data = [ - 'success' => false, - 'message' => 'error validating the action on this element, element cannot peform this action', - ]; - - return new JsonResponse($data); - } - - private function customHtmlResponse(?CustomHtmlServiceInterface $customHtmlService): JsonResponse - { - $data = [ - 'success' => true, - 'customHtml' => [], - ]; - - if ($customHtmlService) { - foreach (['top', 'center', 'bottom'] as $position) { - $data['customHtml'][$position] = $customHtmlService->renderHtmlForRequestedPosition($this->element, $position); - } - } - - return new JsonResponse($data); - } - - /** - * @throws Exception - */ - private function getWorkflowSvg(WorkflowInterface $workflow): string - { - $marking = $workflow->getMarking($this->element); - - $php = Console::getExecutable('php'); - $dot = Console::getExecutable('dot'); - - if (!$php) { - throw new InvalidArgumentException($this->translator->trans('workflow_cmd_not_found', ['php'], 'admin')); - } - - if (!$dot) { - throw new InvalidArgumentException($this->translator->trans('workflow_cmd_not_found', ['dot'], 'admin')); - } - - $cmd = $php . ' ' . OPENDXP_PROJECT_ROOT . '/bin/console opendxp:workflow:dump ${WNAME} ${WPLACES} | ${DOT} -Tsvg'; - $params = [ - 'WNAME' => $workflow->getName(), - 'WPLACES' => implode(' ', array_keys($marking->getPlaces())), - 'DOT' => $dot, - ]; - - Console::addLowProcessPriority($cmd); - $process = Process::fromShellCommandline($cmd); - $process->run(null, $params); - - return $process->getOutput(); - } - - /** - * @template T of Document|Asset|DataObject - * - * @param T $element - * - * @return T - */ - protected function getLatestVersion(mixed $element): mixed + public function getModalCustomHtml(Request $request, GetModalCustomHtmlHandler $getModalCustomHtml): JsonResponse { - if ( - $element instanceof Document\Folder - || $element instanceof Asset\Folder - || $element instanceof DataObject\Folder - || $element instanceof Document\Hardlink - || $element instanceof Document\Link - ) { - return $element; - } - - //TODO move this maybe to a service method, since this is also used in DataObjectController and DocumentControllers - if ($element instanceof Document\PageSnippet) { - $latestVersion = $element->getLatestVersion(); - if ($latestVersion) { - $latestDoc = $latestVersion->loadData(); - if ($latestDoc instanceof Document\PageSnippet) { - $element = $latestDoc; - } - } - } + try { + [$ctype, $cid] = $this->resolveCtypeAndCid($request); + $result = $getModalCustomHtml( + ctype: $ctype, + cid: $cid, + workflowName: $request->request->getString('workflowName'), + transition: $request->request->getString('transition'), + isGlobalAction: $request->request->getString('isGlobalAction') === 'true', + ); - if ($element instanceof DataObject\Concrete) { - $latestVersion = $element->getLatestVersion(); - if ($latestVersion) { - $latestObj = $latestVersion->loadData(); - if ($latestObj instanceof ConcreteObject) { - $element = $latestObj; - } - } + return $this->adminJson(ApiResponse::ok(['customHtml' => $result->customHtml])); + } catch (Exception $e) { + return $this->adminJson(ApiResponse::error($e->getMessage())); } - - return $element; } - /** - * @throws Exception - */ - public function onKernelControllerEvent(ControllerEvent $event): void + /** @return array{string, int} */ + private function resolveCtypeAndCid(Request $request): array { - if (!$event->isMainRequest()) { - return; - } - - $request = $event->getRequest(); - $ctype = $request->request->get('ctype') ?? $request->query->get('ctype'); - $cid = $request->request->get('cid') ?? $request->query->get('cid'); - - $this->element = match ($ctype) { - 'document' => Document::getById((int) $cid), - 'asset' => Asset::getById((int) $cid), - 'object' => ConcreteObject::getById((int) $cid), - default => null, - }; - - if ($this->element === null) { - throw new Exception('Cannot load element' . $cid . ' of type \'' . $ctype . '\''); - } + $cid = (int) ($request->request->get('cid') ?? $request->query->get('cid')); - //get the latest available version of the element - - $this->element = $this->getLatestVersion($this->element); - $this->element->setUserModification($this->getAdminUser()->getId()); + return [$ctype, $cid]; } } diff --git a/src/Controller/AdminAbstractController.php b/src/Controller/AdminAbstractController.php index 2808a29f..82302267 100644 --- a/src/Controller/AdminAbstractController.php +++ b/src/Controller/AdminAbstractController.php @@ -15,6 +15,7 @@ namespace OpenDxp\Bundle\AdminBundle\Controller; +use JsonSerializable; use OpenDxp\Controller\Traits\JsonHelperTrait; use OpenDxp\Controller\UserAwareController; use OpenDxp\Model\User; @@ -33,6 +34,10 @@ abstract class AdminAbstractController extends UserAwareController */ protected function adminJson(mixed $data, int $status = 200, array $headers = [], array $context = [], bool $useAdminSerializer = true): JsonResponse { + if ($data instanceof JsonSerializable) { + $data = $data->jsonSerialize(); + } + return $this->jsonResponse($data, $status, $headers, $context, $useAdminSerializer); } diff --git a/src/Controller/GDPR/AdminController.php b/src/Controller/GDPR/AdminController.php index 7b87f545..1a5d750b 100644 --- a/src/Controller/GDPR/AdminController.php +++ b/src/Controller/GDPR/AdminController.php @@ -19,15 +19,16 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\GDPR\DataProvider\Manager; -use OpenDxp\Controller\KernelControllerEventInterface; +use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal */ -class AdminController extends AdminAbstractController implements KernelControllerEventInterface +#[IsGranted(AdminPermission::GdprDataExtractor->value)] +class AdminController extends AdminAbstractController { #[Route('/get-data-providers', name: 'opendxp_admin_gdpr_admin_getdataproviders', methods: ['GET'])] public function getDataProvidersAction(Manager $manager): JsonResponse @@ -42,13 +43,4 @@ public function getDataProvidersAction(Manager $manager): JsonResponse return $this->adminJson($response); } - - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - $this->checkActionPermission($event, 'gdpr_data_extractor'); - } } diff --git a/src/Controller/GDPR/AssetController.php b/src/Controller/GDPR/AssetController.php index b3f84d01..a4b28be1 100644 --- a/src/Controller/GDPR/AssetController.php +++ b/src/Controller/GDPR/AssetController.php @@ -19,29 +19,22 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\GDPR\DataProvider\Assets; -use OpenDxp\Controller\KernelControllerEventInterface; +use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use OpenDxp\Model\Asset; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Event\ControllerEvent; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal */ #[Route('/asset')] -class AssetController extends AdminAbstractController implements KernelControllerEventInterface +#[IsGranted(AdminPermission::GdprDataExtractor->value)] +class AssetController extends AdminAbstractController { - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - $this->checkActionPermission($event, 'gdpr_data_extractor'); - } - #[Route('/search-assets', name: 'opendxp_admin_gdpr_asset_searchasset', methods: ['GET'])] public function searchAssetAction(Request $request, Assets $service): JsonResponse { @@ -64,9 +57,12 @@ public function searchAssetAction(Request $request, Assets $service): JsonRespon * @throws Exception */ #[Route('/export', name: 'opendxp_admin_gdpr_asset_exportassets', methods: ['GET'])] - public function exportAssetsAction(Request $request, Assets $service): Response + public function exportAssetsAction( + Assets $service, + #[MapQueryParameter] int $id = 0, + ): Response { - $asset = Asset::getById((int) $request->query->get('id')); + $asset = Asset::getById($id); if (!$asset) { throw $this->createNotFoundException('Asset not found'); } diff --git a/src/Controller/GDPR/DataObjectController.php b/src/Controller/GDPR/DataObjectController.php index 8a32dc2d..aba03054 100644 --- a/src/Controller/GDPR/DataObjectController.php +++ b/src/Controller/GDPR/DataObjectController.php @@ -19,28 +19,21 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\GDPR\DataProvider\DataObjects; -use OpenDxp\Controller\KernelControllerEventInterface; +use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use OpenDxp\Model\DataObject; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Event\ControllerEvent; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal */ #[Route('/data-object')] -class DataObjectController extends AdminAbstractController implements KernelControllerEventInterface +#[IsGranted(AdminPermission::GdprDataExtractor->value)] +class DataObjectController extends AdminAbstractController { - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - $this->checkActionPermission($event, 'gdpr_data_extractor'); - } - #[Route('/search-data-objects', name: 'opendxp_admin_gdpr_dataobject_searchdataobjects', methods: ['GET'])] public function searchDataObjectsAction(Request $request, DataObjects $service): JsonResponse { @@ -63,9 +56,12 @@ public function searchDataObjectsAction(Request $request, DataObjects $service): * @throws Exception */ #[Route('/export', name: 'opendxp_admin_gdpr_dataobject_exportdataobject', methods: ['GET'])] - public function exportDataObjectAction(Request $request, DataObjects $service): JsonResponse + public function exportDataObjectAction( + DataObjects $service, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { - $object = DataObject::getById((int) $request->query->get('id')); + $object = DataObject::getById($id); if (!$object) { throw $this->createNotFoundException('Object not found'); diff --git a/src/Controller/GDPR/OpenDxpUsersController.php b/src/Controller/GDPR/OpenDxpUsersController.php index 4a8d1a61..90748c95 100644 --- a/src/Controller/GDPR/OpenDxpUsersController.php +++ b/src/Controller/GDPR/OpenDxpUsersController.php @@ -18,11 +18,12 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\GDPR\DataProvider\OpenDxpUsers; -use OpenDxp\Controller\KernelControllerEventInterface; +use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Event\ControllerEvent; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * Class OpenDxpController @@ -30,17 +31,9 @@ * @internal */ #[Route('/opendxp-users')] -class OpenDxpUsersController extends AdminAbstractController implements KernelControllerEventInterface +#[IsGranted(AdminPermission::GdprDataExtractor->value)] +class OpenDxpUsersController extends AdminAbstractController { - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - $this->checkActionPermission($event, 'gdpr_data_extractor'); - } - #[Route('/search-users', name: 'opendxp_admin_gdpr_opendxpusers_searchusers', methods: ['GET'])] public function searchUsersAction(Request $request, OpenDxpUsers $openDxpUsers): JsonResponse { @@ -60,10 +53,13 @@ public function searchUsersAction(Request $request, OpenDxpUsers $openDxpUsers): } #[Route('/export-user-data', name: 'opendxp_admin_gdpr_opendxpusers_exportuserdata', methods: ['GET'])] - public function exportUserDataAction(Request $request, OpenDxpUsers $openDxpUsers): JsonResponse + public function exportUserDataAction( + OpenDxpUsers $openDxpUsers, + #[MapQueryParameter] int $id = 0, + ): JsonResponse { $this->checkPermission('users'); - $userData = $openDxpUsers->getExportData((int)$request->query->get('id')); + $userData = $openDxpUsers->getExportData($id); $json = $this->encodeJson($userData, [], JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_PRETTY_PRINT); diff --git a/src/Controller/GDPR/SentMailController.php b/src/Controller/GDPR/SentMailController.php index ed04216e..4c6cb1c8 100644 --- a/src/Controller/GDPR/SentMailController.php +++ b/src/Controller/GDPR/SentMailController.php @@ -17,34 +17,28 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\GDPR; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Controller\KernelControllerEventInterface; +use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use OpenDxp\Model\Tool\Email\Log; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Event\ControllerEvent; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; /** * @internal */ #[Route('/sent-mail')] -class SentMailController extends AdminAbstractController implements KernelControllerEventInterface +#[IsGranted(AdminPermission::GdprDataExtractor->value)] +class SentMailController extends AdminAbstractController { - public function onKernelControllerEvent(ControllerEvent $event): void - { - if (!$event->isMainRequest()) { - return; - } - - $this->checkActionPermission($event, 'gdpr_data_extractor'); - } - #[Route('/export', name: 'opendxp_admin_gdpr_sentmail_exportdataobject', methods: ['GET'])] - public function exportDataObjectAction(Request $request): JsonResponse + public function exportDataObjectAction( + #[MapQueryParameter] int $id = 0, + ): JsonResponse { $this->checkPermission('emails'); - $sentMail = Log::getById((int) $request->query->get('id')); + $sentMail = Log::getById($id); if (!$sentMail) { throw $this->createNotFoundException(); } diff --git a/src/Controller/Traits/ApplySchedulerDataTrait.php b/src/Controller/Traits/ApplySchedulerDataTrait.php deleted file mode 100644 index b61aa440..00000000 --- a/src/Controller/Traits/ApplySchedulerDataTrait.php +++ /dev/null @@ -1,51 +0,0 @@ -request->has('scheduler')) { - $tasks = []; - $tasksData = $this->decodeJson($request->request->get('scheduler')); - - if (!empty($tasksData)) { - foreach ($tasksData as $taskData) { - $taskData['userId'] = $adminUser?->getId(); - - $task = new Task($taskData); - $tasks[] = $task; - } - } - - if ($element->isAllowed('settings') && method_exists($element, 'setScheduledTasks')) { - $element->setScheduledTasks($tasks); - } - } - } -} diff --git a/src/Controller/Traits/DocumentTreeConfigTrait.php b/src/Controller/Traits/DocumentTreeConfigTrait.php index c78b5157..7c91e3db 100644 --- a/src/Controller/Traits/DocumentTreeConfigTrait.php +++ b/src/Controller/Traits/DocumentTreeConfigTrait.php @@ -16,7 +16,6 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Traits; -use Exception; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Model\Element\ElementInterface; use Symfony\Contracts\Service\Attribute\Required; @@ -28,8 +27,6 @@ */ trait DocumentTreeConfigTrait { - use AdminStyleTrait; - protected ElementServiceInterface $elementService; #[Required] @@ -38,9 +35,6 @@ public function setElementService(ElementServiceInterface $elementService): void $this->elementService = $elementService; } - /** - * @throws Exception - */ public function getTreeNodeConfig(ElementInterface $element): array { return $this->elementService->getElementTreeNodeConfig($element); diff --git a/src/Controller/Traits/UserNameTrait.php b/src/Controller/Traits/UserNameTrait.php deleted file mode 100644 index 065985d6..00000000 --- a/src/Controller/Traits/UserNameTrait.php +++ /dev/null @@ -1,64 +0,0 @@ -translator = $translator; - } - - /** - * @param int|null $userId The User ID. - * - * @return array{userName: string, fullName: string} - */ - protected function getUserName(?int $userId = null): array - { - if ($userId === null) { - return [ - 'userName' => '', - 'fullName' => $this->translator->trans('user_unknown', [], 'admin'), - ]; - } - - $user = User::getById($userId); - - if (empty($user)) { - return [ - 'userName' => '', - 'fullName' => $this->translator->trans('user_unknown', [], 'admin'), - ]; - } - - return [ - 'userName' => $user->getName(), - 'fullName' => (empty($user->getFullName()) ? $user->getName() : $user->getFullName()), - ]; - } -} diff --git a/src/Dto/Admin/AdminSettingsDto.php b/src/Dto/Admin/AdminSettingsDto.php new file mode 100644 index 00000000..f1273332 --- /dev/null +++ b/src/Dto/Admin/AdminSettingsDto.php @@ -0,0 +1,115 @@ + $this->success]; + if ($this->message !== null) { + $data['message'] = $this->message; + } + + return array_merge($data, $this->extra); + } +} diff --git a/src/Event/AdminEvents.php b/src/Event/AdminEvents.php index 49f03276..e361343c 100644 --- a/src/Event/AdminEvents.php +++ b/src/Event/AdminEvents.php @@ -338,11 +338,36 @@ class AdminEvents public const string DOCUMENT_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA = 'opendxp.admin.document.treeGetChildrenById.preSendData'; /** - * Fired before the edit lock is handled. + * Fired before the edit lock is handled for an asset. + * + * Arguments: + * - data | array | editLock behaviour — set data['task'] = 'overwrite' to force-acquire the lock + * - object | Asset | the current asset + * + * @Event("Symfony\Component\EventDispatcher\GenericEvent") + * + * @var string + */ + public const string ASSET_GET_IS_LOCKED = 'opendxp.admin.asset.get.isLocked'; + + /** + * Fired before the edit lock is handled for a document. + * + * Arguments: + * - data | array | editLock behaviour — set data['task'] = 'overwrite' to force-acquire the lock + * - object | Document | the current document + * + * @Event("Symfony\Component\EventDispatcher\GenericEvent") + * + * @var string + */ + public const string DOCUMENT_GET_IS_LOCKED = 'opendxp.admin.document.get.isLocked'; + + /** + * Fired before the edit lock is handled for a data object. * - * Subject: \OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObjectController * Arguments: - * - data | array | editLock behaviour, this can be modified + * - data | array | editLock behaviour — set data['task'] = 'overwrite' to force-acquire the lock * - object | AbstractObject | the current object * * @Event("Symfony\Component\EventDispatcher\GenericEvent") diff --git a/src/Exception/Asset/AssetNotFoundException.php b/src/Exception/Asset/AssetNotFoundException.php new file mode 100644 index 00000000..3ac83203 --- /dev/null +++ b/src/Exception/Asset/AssetNotFoundException.php @@ -0,0 +1,28 @@ +elementId; + } + + public function getElementType(): string + { + return $this->elementType; + } + + public function getEditLock(): Editlock + { + return $this->editLock; + } +} diff --git a/src/Exception/NotFoundException.php b/src/Exception/NotFoundException.php new file mode 100644 index 00000000..513dc8ae --- /dev/null +++ b/src/Exception/NotFoundException.php @@ -0,0 +1,22 @@ +userContext->getAdminUser() ?? throw new \RuntimeException('Admin user not available'); + + return new Dashboard($user); + } +} diff --git a/src/Factory/ElementServiceFactory.php b/src/Factory/ElementServiceFactory.php new file mode 100644 index 00000000..32bf1861 --- /dev/null +++ b/src/Factory/ElementServiceFactory.php @@ -0,0 +1,44 @@ +userContext->getAdminUser()); + } + + public function createDataObjectService(): DataObject\Service + { + return new DataObject\Service($this->userContext->getAdminUser()); + } + + public function createDocumentService(): Document\Service + { + return new Document\Service($this->userContext->getAdminUser()); + } +} diff --git a/src/Factory/LoginPageFactory.php b/src/Factory/LoginPageFactory.php new file mode 100644 index 00000000..49419176 --- /dev/null +++ b/src/Factory/LoginPageFactory.php @@ -0,0 +1,45 @@ +config, + bundleManager: $this->bundleManager, + eventDispatcher: $this->eventDispatcher, + authenticationUtils: $this->authenticationUtils, + request: $request, + ); + } +} diff --git a/src/Handler/Admin/IndexActionHandler.php b/src/Handler/Admin/IndexActionHandler.php new file mode 100644 index 00000000..a602f254 --- /dev/null +++ b/src/Handler/Admin/IndexActionHandler.php @@ -0,0 +1,139 @@ +userContext->getAdminUser(); + $dto = $this->factory->createSettings($request, $user); + + $settings = [ + 'instanceId' => $dto->instanceId, + 'version' => $dto->version, + 'build' => $dto->build, + 'debug' => $dto->debug, + 'devmode' => $dto->devMode, + 'disableMinifyJs' => $dto->disableMinifyJs, + 'environment' => $dto->environment, + 'sessionId' => $dto->sessionId, + + 'language' => $dto->language, + 'websiteLanguages' => $dto->websiteLanguages, + 'requiredLanguages' => $dto->requiredLanguages, + + 'showCloseConfirmation' => true, + 'debug_admin_translations' => $dto->debugAdminTranslations, + 'document_generatepreviews' => $dto->generateDocumentPreviews, + 'asset_disable_tree_preview' => $dto->disableAssetTreePreview, + 'asset_hide_edit' => $dto->hideEditImage, + 'asset_tree_paging_limit' => $dto->assetTreePagingLimit, + 'asset_default_upload_path' => $dto->assetDefaultUploadPath, + 'chromium' => $dto->chromiumAvailable, + 'videoconverter' => $dto->videoConverterAvailable, + 'main_domain' => $dto->mainDomain, + 'custom_admin_entrypoint_url' => $dto->customAdminEntrypointUrl, + 'timezone' => $dto->timezone, + 'tile_layer_url_template' => $dto->tileLayerUrlTemplate, + 'geocoding_url_template' => $dto->geocodingUrlTemplate, + 'reverse_geocoding_url_template' => $dto->reverseGeocodingUrlTemplate, + 'document_tree_paging_limit' => $dto->documentTreePagingLimit, + 'object_tree_paging_limit' => $dto->objectTreePagingLimit, + 'hostname' => $dto->hostname, + 'dependency' => $dto->dependencyEnabled, + 'document_auto_save_interval' => $dto->documentAutoSaveInterval, + 'object_auto_save_interval' => $dto->objectAutoSaveInterval, + + 'perspective' => $dto->perspective, + 'availablePerspectives' => $dto->availablePerspectives, + 'disabledPortlets' => $dto->disabledPortlets, + + 'image-thumbnails-writeable' => $dto->imageThumbnailsWriteable, + 'video-thumbnails-writeable' => $dto->videoThumbnailsWriteable, + 'document-types-writeable' => $dto->documentTypesWriteable, + 'predefined-properties-writeable' => $dto->predefinedPropertiesWriteable, + 'predefined-asset-metadata-writeable' => $dto->predefinedAssetMetadataWriteable, + 'perspectives-writeable' => $dto->perspectivesWriteable, + 'custom-views-writeable' => $dto->customViewsWriteable, + 'class-definition-writeable' => $dto->classDefinitionWriteable, + 'object-custom-layout-writeable' => $dto->objectCustomLayoutWriteable, + 'select-options-writeable' => $dto->selectOptionsWriteable, + + 'asset_search_types' => $dto->assetSearchTypes, + 'document_types_configuration' => $dto->documentTypesConfiguration, + 'document_search_types' => $dto->documentSearchTypes, + 'document_valid_types' => $dto->documentValidTypes, + 'document_email_search_types' => $dto->documentEmailSearchTypes, + 'select_options_provider_class' => $dto->selectOptionsProviderClass, + + 'upload_max_filesize' => $dto->uploadMaxFilesize, + 'session_gc_maxlifetime' => $dto->sessionGcMaxlifetime, + + 'maintenance_active' => $dto->maintenanceActive, + 'maintenance_mode' => $dto->maintenanceMode, + + 'mail' => $dto->mailConfigured, + 'mailDefaultAddress' => $dto->mailDefaultAddress, + + 'customviews' => $dto->customViews, + + 'notifications_enabled' => $dto->notificationsEnabled, + 'checknewnotification_enabled' => $dto->checkNewNotificationEnabled, + 'checknewnotification_interval' => $dto->checkNewNotificationInterval, + + 'csrfToken' => $dto->csrfToken, + ]; + + $event = new IndexActionSettingsEvent($settings); + $this->eventDispatcher->dispatch($event, AdminEvents::INDEX_ACTION_SETTINGS); + + return new SettingsResult( + templateParams: [ + 'config' => $this->config, + 'systemSettings' => SystemSettingsConfig::get(), + 'adminSettings' => AdminConfig::get(), + 'perspectiveConfig' => new PerspectiveConfig(), + 'runtimePerspective' => $dto->perspective, + 'pluginJsPaths' => $this->bundleManager->getJsPaths(), + 'pluginCssPaths' => $this->bundleManager->getCssPaths(), + 'settings' => $event->getSettings(), + ], + template: $event->getTemplate(), + ); + } +} diff --git a/src/Handler/Admin/SettingsResult.php b/src/Handler/Admin/SettingsResult.php new file mode 100644 index 00000000..db9f227e --- /dev/null +++ b/src/Handler/Admin/SettingsResult.php @@ -0,0 +1,25 @@ +factory->createStatistics(); + $data = [ + 'instance_id' => $dto->instanceId, + 'revision' => $dto->revision, + 'version' => $dto->version, + 'major_version' => $dto->majorVersion, + 'php_version' => $dto->phpVersion, + 'db_version' => $dto->dbVersion, + 'bundles' => $dto->bundles, + ]; + } catch (\Throwable) { + $data = []; + } + + try { + $this->httpClient->request( + 'POST', + 'https://metrics.opendxp.io/statistics', + ['json' => $data], + ); + } catch (GuzzleException) { + // fail silently + } + } +} diff --git a/src/Handler/Asset/AssetResult.php b/src/Handler/Asset/AssetResult.php new file mode 100644 index 00000000..b8b67949 --- /dev/null +++ b/src/Handler/Asset/AssetResult.php @@ -0,0 +1,27 @@ +isAllowed('publish')) { + throw new AccessDeniedHttpException('not allowed to publish'); + } + + $asset->clearThumbnails(true); + $asset->save(); + } +} diff --git a/src/Handler/Asset/Copy/ChildIdsResult.php b/src/Handler/Asset/Copy/ChildIdsResult.php new file mode 100644 index 00000000..31a63675 --- /dev/null +++ b/src/Handler/Asset/Copy/ChildIdsResult.php @@ -0,0 +1,26 @@ +getRealFullPath() . '@', $targetParent . '/', $source->getRealPath()); + $target = Asset::getByPath($targetPath); + } else { + $target = Asset::getById($targetId); + } + + if ($target === null) { + throw new NotFoundHttpException('Target not found'); + } + + if (!$target->isAllowed('create')) { + Logger::error('could not execute copy/paste because of missing permissions on target [ ' . $targetId . ' ]'); + throw new AccessDeniedHttpException(); + } + + $assetService = $this->serviceFactory->createAssetService(); + + if ($type === 'child') { + $newAsset = $assetService->copyAsChild($target, $source); + + return new CopyAssetResult($newAsset); + } + + if ($type === 'replace') { + $assetService->copyContents($target, $source); + } + + return new CopyAssetResult(); + } +} diff --git a/src/Handler/Asset/Copy/CopyAssetResult.php b/src/Handler/Asset/Copy/CopyAssetResult.php new file mode 100644 index 00000000..7023ab69 --- /dev/null +++ b/src/Handler/Asset/Copy/CopyAssetResult.php @@ -0,0 +1,27 @@ +hasChildren()) { + return new ChildIdsResult([]); + } + + $list = new Asset\Listing(); + $list->setCondition('`path` LIKE ?', [$list->escapeLike($asset->getRealFullPath()) . '/%']); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('ASC'); + + return new ChildIdsResult($list->loadIdList()); + } +} diff --git a/src/Handler/Asset/CreateAssetFolderHandler.php b/src/Handler/Asset/CreateAssetFolderHandler.php new file mode 100644 index 00000000..f0ef83a7 --- /dev/null +++ b/src/Handler/Asset/CreateAssetFolderHandler.php @@ -0,0 +1,52 @@ +userContext->getAdminUser(); + $parentAsset = Asset::getById($parentId); + + if (!$parentAsset->isAllowed('create')) { + throw new AccessDeniedHttpException('prevented creating asset because of missing permissions'); + } + + if (Asset::getByPath($parentAsset->getRealFullPath() . '/' . $name)) { + throw new BadRequestHttpException('Asset with same path+key already exists'); + } + + Asset::create($parentId, [ + 'filename' => $name, + 'type' => 'folder', + 'userOwner' => $adminUser->getId(), + 'userModification' => $adminUser->getId(), + ]); + } +} diff --git a/src/Handler/Asset/DeleteAssetHandler.php b/src/Handler/Asset/DeleteAssetHandler.php new file mode 100644 index 00000000..b4a484c3 --- /dev/null +++ b/src/Handler/Asset/DeleteAssetHandler.php @@ -0,0 +1,69 @@ +userContext->getAdminUser(); + if ($type === 'children') { + $parentAsset = Asset::getById($id); + + $list = new Asset\Listing(); + $list->setCondition('`path` LIKE ?', [Helper::escapeLike($parentAsset->getRealFullPath()) . '/%']); + $list->setLimit($amount); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('DESC'); + + $deleted = []; + foreach ($list as $asset) { + $deleted[$asset->getId()] = $asset->getRealFullPath(); + if ($asset->isAllowed('delete') && !$asset->isLocked()) { + $asset->delete(); + } + } + + return new DeleteAssetResult($deleted); + } + + $asset = Asset::getById($id); + if ($asset && $asset->isAllowed('delete')) { + if ($asset->isLocked()) { + throw new BadRequestHttpException('prevented deleting asset, because it is locked: ID: ' . $asset->getId()); + } + + $asset->delete(); + + return new DeleteAssetResult(); + } + + throw new AccessDeniedHttpException(); + } +} diff --git a/src/Handler/Asset/DeleteAssetResult.php b/src/Handler/Asset/DeleteAssetResult.php new file mode 100644 index 00000000..a0279000 --- /dev/null +++ b/src/Handler/Asset/DeleteAssetResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/download-zip-' . $jobId . '.zip'; + $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); + + if (!$asset->isAllowed('view')) { + throw new AccessDeniedHttpException(); + } + + $zip = new ZipArchive(); + $zipState = is_file($zipFile) ? $zip->open($zipFile) : $zip->open($zipFile, ZipArchive::CREATE); + + if ($zipState !== true) { + throw new \RuntimeException('Failed to open zip archive: ' . $zipFile); + } + + $parentPath = $asset->getRealFullPath(); + if ($asset->getId() === 1) { + $parentPath = ''; + } + + $db = \OpenDxp\Db::get(); + $conditionFilters = []; + + if (!empty($selectedIds)) { + $selectedIdList = explode(',', $selectedIds); + $quotedSelectedIds = []; + foreach ($selectedIdList as $selectedId) { + if ($selectedId) { + $quotedSelectedIds[] = $db->quote($selectedId); + } + } + $conditionFilters[] = 'id IN (' . implode(',', $quotedSelectedIds) . ')'; + } + $conditionFilters[] = "`type` != 'folder' AND `path` like " . $db->quote(Helper::escapeLike($parentPath) . '/%'); + if (!$adminUser->isAdmin()) { + $userIds = $adminUser->getRoles(); + $userIds[] = $adminUser->getId(); + $conditionFilters[] = ' ( + (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`, filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + OR + (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`, filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + )'; + } + + $assetList = new Asset\Listing(); + $assetList->setCondition(implode(' AND ', $conditionFilters)); + $assetList->setOrderKey('LENGTH(`path`) ASC, id ASC', false); + $assetList->setOffset($offset); + $assetList->setLimit($limit); + + foreach ($assetList as $a) { + if (!$a->isAllowed('view') || $a instanceof Asset\Folder) { + continue; + } + $zip->addFile($a->getLocalFile(), preg_replace('@^' . preg_quote($asset->getRealPath(), '@') . '@i', '', $a->getRealFullPath())); + } + + $zip->close(); + } +} diff --git a/src/Handler/Asset/Download/DownloadAssetHandler.php b/src/Handler/Asset/Download/DownloadAssetHandler.php new file mode 100644 index 00000000..bce36660 --- /dev/null +++ b/src/Handler/Asset/Download/DownloadAssetHandler.php @@ -0,0 +1,37 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('Not allowed to view asset'); + } + + return new AssetResult($asset); + } +} diff --git a/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php b/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php new file mode 100644 index 00000000..1afa0bcf --- /dev/null +++ b/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php @@ -0,0 +1,96 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('Not allowed to view thumbnail'); + } + + $thumbnail = null; + $thumbnailFile = null; + $deleteThumbnail = true; + + if ($configData) { + $thumbnailConfig = new Asset\Image\Thumbnail\Config(); + $thumbnailConfig->setName('opendxp-download-' . $image->getId() . '-' . md5($config ?? '')); + + if ($configData['resize_mode'] === 'scaleByWidth') { + $thumbnailConfig->addItem('scaleByWidth', ['width' => $configData['width']]); + } elseif ($configData['resize_mode'] === 'scaleByHeight') { + $thumbnailConfig->addItem('scaleByHeight', ['height' => $configData['height']]); + } else { + $thumbnailConfig->addItem('resize', ['width' => $configData['width'], 'height' => $configData['height']]); + } + + if (!empty($configData['quality']) && $configData['quality'] <= 100 && $configData['quality'] > 0) { + $thumbnailConfig->setQuality($configData['quality']); + } + if (!empty($configData['format'])) { + $thumbnailConfig->setFormat($configData['format']); + } + $thumbnailConfig->setRasterizeSVG(true); + + if ($thumbnailConfig->getFormat() === 'JPEG') { + $thumbnailConfig->setPreserveMetaData(true); + if (empty($configData['quality'])) { + $thumbnailConfig->setPreserveColor(true); + } + } + + $thumbnail = $image->getThumbnail($thumbnailConfig); + $thumbnailFile = $thumbnail->getLocalFile(); + + $exiftool = \OpenDxp\Tool\Console::getExecutable('exiftool'); + if ($thumbnailConfig->getFormat() === 'JPEG' && $exiftool && isset($configData['dpi']) && $configData['dpi']) { + $process = new Process([$exiftool, '-overwrite_original', '-xresolution=' . (int)$configData['dpi'], '-yresolution=' . (int)$configData['dpi'], '-resolutionunit=inches', $thumbnailFile]); + $process->run(); + } + } elseif ($thumbnailName) { + $thumbnail = $image->getThumbnail($thumbnailName); + $deleteThumbnail = false; + } + + if ($thumbnail) { + $thumbnailConfig = $thumbnail->getConfig(); + if ($thumbnailConfig->getFormat() === 'SOURCE' && $autoFormatConfigs = $thumbnailConfig->getAutoFormatThumbnailConfigs()) { + $autoFormatConfig = current($autoFormatConfigs); + $thumbnail = $image->getThumbnail($autoFormatConfig); + } + $thumbnailFile = $thumbnailFile ?: $thumbnail->getLocalFile(); + + return new DownloadImageThumbnailResult($image, $thumbnail, $thumbnailFile, $deleteThumbnail); + } + + throw new AssetNotFoundException($id); + } +} diff --git a/src/Handler/Asset/Download/DownloadImageThumbnailResult.php b/src/Handler/Asset/Download/DownloadImageThumbnailResult.php new file mode 100644 index 00000000..758368a1 --- /dev/null +++ b/src/Handler/Asset/Download/DownloadImageThumbnailResult.php @@ -0,0 +1,30 @@ +getFilename() ?: 'assets'; + + return new DownloadZipResult($zipFile, $suggestedFilename); + } +} diff --git a/src/Handler/Asset/Download/DownloadZipResult.php b/src/Handler/Asset/Download/DownloadZipResult.php new file mode 100644 index 00000000..5ddabf0b --- /dev/null +++ b/src/Handler/Asset/Download/DownloadZipResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); + + if (!$asset->isAllowed('view')) { + return new GetDownloadZipJobsResult(jobId: uniqid('', false), jobs: []); + } + + $parentPath = $asset->getRealFullPath(); + if ($asset->getId() == 1) { + $parentPath = ''; + } + + $db = \OpenDxp\Db::get(); + $conditionFilters = []; + $selectedIdList = explode(',', $selectedIds); + $quotedSelectedIds = []; + foreach ($selectedIdList as $selectedId) { + if ($selectedId) { + $quotedSelectedIds[] = $db->quote($selectedId); + } + } + if ($quotedSelectedIds !== []) { + $conditionFilters[] = 'id IN (' . implode(',', $quotedSelectedIds) . ')'; + } + $conditionFilters[] = '`path` LIKE ' . $db->quote(Helper::escapeLike($parentPath) . '/%') . ' AND `type` != ' . $db->quote('folder'); + if (!$adminUser->isAdmin()) { + $userIds = $adminUser->getRoles(); + $userIds[] = $adminUser->getId(); + $conditionFilters[] = ' ( + (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`, filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + OR + (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`, filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + )'; + } + + $assetList = new Asset\Listing(); + $assetList->setCondition(implode(' AND ', $conditionFilters)); + $assetList->setOrderKey('LENGTH(`path`)', false); + $assetList->setOrder('ASC'); + + $totalCount = $assetList->getTotalCount(); + $jobId = uniqid('', false); + $addFilesUrl = $this->router->generate('opendxp_admin_asset_downloadaszipaddfiles'); + $jobAmount = (int) ceil($totalCount / self::FILES_PER_JOB); + $jobs = []; + for ($i = 0; $i < $jobAmount; $i++) { + $jobs[] = [[ + 'url' => $addFilesUrl, + 'method' => 'GET', + 'params' => [ + 'id' => $asset->getId(), + 'selectedIds' => implode(',', $selectedIdList), + 'offset' => $i * self::FILES_PER_JOB, + 'limit' => self::FILES_PER_JOB, + 'jobId' => $jobId, + ], + ]]; + } + + return new GetDownloadZipJobsResult(jobId: $jobId, jobs: $jobs); + } +} diff --git a/src/Handler/Asset/Download/GetDownloadZipJobsResult.php b/src/Handler/Asset/Download/GetDownloadZipJobsResult.php new file mode 100644 index 00000000..b028983d --- /dev/null +++ b/src/Handler/Asset/Download/GetDownloadZipJobsResult.php @@ -0,0 +1,26 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('Not allowed to preview asset'); + } + + return new AssetResult($asset); + } +} diff --git a/src/Handler/Asset/Editor/SaveImageEditorHandler.php b/src/Handler/Asset/Editor/SaveImageEditorHandler.php new file mode 100644 index 00000000..cdbb2b0a --- /dev/null +++ b/src/Handler/Asset/Editor/SaveImageEditorHandler.php @@ -0,0 +1,49 @@ +userContext->getAdminUser()?->getId() ?? 0; + $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); + + if (!$asset->isAllowed('publish')) { + throw new AccessDeniedHttpException('Not allowed to publish asset'); + } + + $data = substr($dataUri, strpos($dataUri, ',')); + $data = base64_decode($data); + $asset->setData($data); + $asset->setUserModification($userId); + $asset->save(); + + return new AssetResult($asset); + } +} diff --git a/src/Handler/Asset/GetAssetChildrenHandler.php b/src/Handler/Asset/GetAssetChildrenHandler.php new file mode 100644 index 00000000..2d2e7860 --- /dev/null +++ b/src/Handler/Asset/GetAssetChildrenHandler.php @@ -0,0 +1,115 @@ +userContext->getAdminUser(); + $assets = []; + $cv = []; + $filteredTotalCount = 0; + + if ($filter !== null) { + if (!str_ends_with($filter, '*')) { + $filter .= '*'; + } + $filter = str_replace('*', '%', $filter); + $limit = 100; + $offset = 0; + } + + if ($asset->hasChildren()) { + if ($customViewId) { + $cv = $this->elementService->getCustomViewById($customViewId); + } + + $childrenList = new Asset\Listing(); + $childrenList->addConditionParam('parentId = ?', [$asset->getId()]); + $childrenList->filterAccessibleByUser($adminUser, $asset); + + if ($filter !== null) { + $childrenList->addConditionParam('CAST(assets.filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ?', [$filter]); + } + + $childrenList->setLimit($limit); + $childrenList->setOffset($offset); + $childrenList->setOrderKey("FIELD(assets.type, 'folder') DESC, CAST(assets.filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci ASC", false); + + Element\Service::addTreeFilterJoins($cv, $childrenList); + + $beforeListLoadEvent = new GenericEvent(null, [ + 'list' => $childrenList, + 'context' => [], + ]); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD); + /** @var Asset\Listing $childrenList */ + $childrenList = $beforeListLoadEvent->getArgument('list'); + + $children = $childrenList->load(); + $filteredTotalCount = $childrenList->getTotalCount(); + + foreach ($children as $childAsset) { + $assetTreeNode = $this->elementService->getElementTreeNodeConfig($childAsset); + if ($assetTreeNode['permissions']['list'] == 1) { + $assets[] = $assetTreeNode; + } + } + } + + $event = new GenericEvent(null, ['assets' => $assets]); + $this->eventDispatcher->dispatch($event, AdminEvents::ASSET_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA); + $assets = $event->getArgument('assets'); + + return new GetAssetChildrenResult( + $assets, + $filteredTotalCount, + $limit, + $offset, + $asset->getChildAmount($adminUser), + $filter, + ); + } +} diff --git a/src/Handler/Asset/GetAssetChildrenResult.php b/src/Handler/Asset/GetAssetChildrenResult.php new file mode 100644 index 00000000..67380708 --- /dev/null +++ b/src/Handler/Asset/GetAssetChildrenResult.php @@ -0,0 +1,30 @@ +userContext->getAdminUser(); + if (!$asset->isAllowed('view')) { + throw new AccessDeniedHttpException(); + } + + if (!$asset instanceof Asset\Folder && ($asset->isAllowed('publish') || $asset->isAllowed('delete'))) { + $this->editLockService->checkAndAcquire($asset->getId(), 'asset', AdminEvents::ASSET_GET_IS_LOCKED, $asset); + } + + $asset = clone $asset; + $asset->setParent(null); + $asset->setStream(null); + + $data = $asset->getObjectVars(); + $data['locked'] = $asset->isLocked(); + + if ($asset instanceof Asset\Text) { + if ($asset->getFileSize() < 2000000) { + $data['data'] = \ForceUTF8\Encoding::toUTF8($asset->getData()); + } else { + $data['data'] = false; + } + } elseif ($asset instanceof Asset\Document) { + $data['pdfPreviewAvailable'] = (bool) $this->getDocumentPreviewPdf($asset); + } elseif ($asset instanceof Asset\Video) { + $videoInfo = []; + + if (\OpenDxp\Video::isAvailable()) { + $config = Asset\Video\Thumbnail\Config::getPreviewConfig(); + $thumbnail = $asset->getThumbnail($config, ['mp4']); + if ($thumbnail && $thumbnail['status'] === 'finished') { + $videoInfo['previewUrl'] = $thumbnail['formats']['mp4']; + $videoInfo['width'] = $asset->getWidth(); + $videoInfo['height'] = $asset->getHeight(); + $metaData = $asset->getSphericalMetaData(); + if (isset($metaData['ProjectionType']) && strtolower($metaData['ProjectionType']) === 'equirectangular') { + $videoInfo['isVrVideo'] = true; + } + } + } + + $data['videoInfo'] = $videoInfo; + } elseif ($asset instanceof Asset\Image) { + $imageInfo = []; + + $previewUrl = $this->urlGenerator->generate('opendxp_admin_asset_getimagethumbnail', [ + 'id' => $asset->getId(), + 'treepreview' => true, + '_dc' => time(), + ]); + + if ($asset->isAnimated()) { + $previewUrl = $this->urlGenerator->generate('opendxp_admin_asset_getasset', [ + 'id' => $asset->getId(), + '_dc' => time(), + ]); + } + + $imageInfo['previewUrl'] = $previewUrl; + + if ($asset->getWidth() && $asset->getHeight()) { + $imageInfo['dimensions'] = [ + 'width' => $asset->getWidth(), + 'height' => $asset->getHeight(), + ]; + } + + $imageInfo['exiftoolAvailable'] = (bool) \OpenDxp\Tool\Console::getExecutable('exiftool'); + + if (!$asset->getEmbeddedMetaData(false)) { + $asset->getEmbeddedMetaData(true, false); + } + + $data['imageInfo'] = $imageInfo; + } + + $predefinedMetaData = Metadata\Predefined\Listing::getByTargetType('asset', [$asset->getType()]); + $predefinedMetaDataGroups = []; + foreach ($predefinedMetaData as $item) { + if ($item->getGroup()) { + $predefinedMetaDataGroups[$item->getGroup()] = true; + } + } + $data['predefinedMetaDataGroups'] = array_keys($predefinedMetaDataGroups); + $data['properties'] = Element\Service::minimizePropertiesForEditmode($asset->getProperties()); + $data['metadata'] = Asset\Service::expandMetadataForEditmode($asset->getMetadata()); + $data['versionDate'] = $asset->getModificationDate(); + $data['filesizeFormatted'] = $asset->getFileSize(true); + $data['filesize'] = $asset->getFileSize(); + $data['fileExtension'] = pathinfo($asset->getFilename(), PATHINFO_EXTENSION); + $data['idPath'] = Element\Service::getIdPath($asset); + $data['userPermissions'] = $asset->getUserPermissions($adminUser); + + $frontendPath = $asset->getFrontendFullPath(); + $data['url'] = preg_match('/^http(s)?:\\/\\/.+/', $frontendPath) + ? $frontendPath + : $requestSchemeAndHost . $frontendPath; + + $data['scheduledTasks'] = array_map( + static fn (Task $task) => $task->getObjectVars(), + $asset->getScheduledTasks() + ); + + $this->elementResponseNormalizer->normalize($asset, $data, static::class); + + $this->applyAdminStyle($asset, ElementAdminStyleEvent::CONTEXT_EDITOR, $data); + + $data['php'] = [ + 'classes' => [$asset::class, ...array_values(class_parents($asset))], + 'interfaces' => array_values(class_implements($asset)), + ]; + + $event = new GenericEvent(null, [ + 'data' => $data, + 'asset' => $asset, + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::ASSET_GET_PRE_SEND_DATA); + $data = $event->getArgument('data'); + + return new GetAssetDataResult($data); + } + + private function getDocumentPreviewPdf(Asset\Document $asset): mixed + { + $stream = null; + + if ($asset->getMimeType() == self::PDF_MIMETYPE) { + $stream = $asset->getStream(); + } + + if ( + !$stream && + $asset->getPageCount() && + \OpenDxp\Document::isAvailable() && + \OpenDxp\Document::isFileTypeSupported($asset->getFilename()) + ) { + try { + $stream = \OpenDxp\Document::getInstance()->getPdf($asset); + } catch (Exception) { + // nothing to do + } + } + + return $stream; + } + + private function applyAdminStyle(Asset $asset, int $context, array &$data): void + { + $event = new ElementAdminStyleEvent($asset, new AdminStyle($asset), $context); + OpenDxp::getEventDispatcher()->dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); + $adminStyle = $event->getAdminStyle(); + + $data['iconCls'] = $adminStyle->getElementIconClass() !== false ? $adminStyle->getElementIconClass() : null; + if (!$data['iconCls']) { + $data['icon'] = $adminStyle->getElementIcon() !== false ? $adminStyle->getElementIcon() : null; + } else { + $data['icon'] = null; + } + if ($adminStyle->getElementCssClass() !== false) { + if (!isset($data['cls'])) { + $data['cls'] = ''; + } + $data['cls'] .= $adminStyle->getElementCssClass() . ' '; + } + $data['qtipCfg'] = $adminStyle->getElementQtipConfig(); + + $elementText = $adminStyle->getElementText(); + if ($elementText !== null) { + $data['text'] = $elementText; + } + } + +} + diff --git a/src/Handler/Asset/GetAssetDataResult.php b/src/Handler/Asset/GetAssetDataResult.php new file mode 100644 index 00000000..ee8aab36 --- /dev/null +++ b/src/Handler/Asset/GetAssetDataResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $gridConfig = GridConfig::getById($gridConfigId); + if (!$gridConfig) { + throw new NotFoundHttpException('Grid config not found: ' . $gridConfigId); + } + + if ($gridConfig->getOwnerId() !== $adminUser->getId()) { + throw new BadRequestHttpException("don't mess with someone elses grid config"); + } + + $gridConfig->delete(); + } +} diff --git a/src/Handler/Asset/Helper/DoAssetExportHandler.php b/src/Handler/Asset/Helper/DoAssetExportHandler.php new file mode 100644 index 00000000..1ae21ad9 --- /dev/null +++ b/src/Handler/Asset/Helper/DoAssetExportHandler.php @@ -0,0 +1,138 @@ +quote($id); + } + + $list->setCondition('id IN (' . implode(',', $quotedIds) . ')'); + $list->setOrderKey(' FIELD(id, ' . implode(',', $quotedIds) . ')', false); + + $csv = $this->buildCsvData($language, $list, $fields, $header, $addTitles); + + $temp = tmpfile(); + + try { + $storage = Storage::get('temp'); + $csvFile = $this->gridExportService->getCsvFile($fileHandle); + + $fileStream = $storage->readStream($csvFile); + stream_copy_to_stream($fileStream, $temp, null, 0); + + $firstLine = !($addTitles && $header === 'no_header'); + + foreach ($csv as $line) { + if ($addTitles && $firstLine) { + $firstLine = false; + fwrite($temp, implode($delimiter, $line) . "\r\n"); + } else { + fwrite($temp, implode($delimiter, array_map($this->gridColumnConfigService->encode(...), $line)) . "\r\n"); + } + } + + $storage->writeStream($csvFile, $temp); + } catch (UnableToReadFile $exception) { + Logger::err($exception->getMessage()); + throw new BadRequestHttpException(sprintf('export file not found: %s', $fileHandle)); + } finally { + if (is_resource($temp)) { + fclose($temp); + } + } + } + + private function buildCsvData( + string $language, + Asset\Listing $list, + array $fields, + string $header, + bool $addTitles, + ): array { + $csv = []; + $unsupportedFields = ['preview~system', 'size~system']; + $fields = array_filter($fields, fn ($field) => !in_array($field['key'], $unsupportedFields)); + + if ($addTitles && $header !== 'no_header') { + $columns = $fields; + $titleIdx = $header === 'name' ? 'key' : 'label'; + foreach ($columns as $columnIdx => $columnKeys) { + $columns[$columnIdx] = '"' . $columnKeys[$titleIdx] . '"'; + } + $csv[] = $columns; + } + + foreach ($list->load() as $asset) { + if ($fields) { + $dataRows = []; + foreach ($fields as $field) { + $fieldDef = explode('~', $field['key']); + $getter = 'get' . ucfirst($fieldDef[0]); + + if (isset($fieldDef[1])) { + if ($fieldDef[1] === 'system' && method_exists($asset, $getter)) { + $data = $asset->$getter($language); + } else { + $fieldDef[1] = str_replace('none', '', $fieldDef[1]); + $data = $asset->getMetadata($fieldDef[0], $fieldDef[1], true); + } + } else { + $data = $asset->getMetadata($field['key'], $language, true); + } + + if ($data instanceof Element\ElementInterface) { + $data = $data->getRealFullPath(); + } + $dataRows[] = $data; + } + $dataRows = Element\Service::escapeCsvRecord($dataRows); + $csv[] = $dataRows; + } + } + + return $csv; + } +} diff --git a/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php b/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php new file mode 100644 index 00000000..75b7678e --- /dev/null +++ b/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php @@ -0,0 +1,38 @@ +userContext->getAdminUser(); + // Returns false when there is no asset to update (job already completed) — not an error. + // Throws on permission denied or save failure. + $this->gridBatchService->executeAssetBatch($data, $adminUser); + } +} diff --git a/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php b/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php new file mode 100644 index 00000000..6cab2f70 --- /dev/null +++ b/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php @@ -0,0 +1,38 @@ +userContext->getAdminUser(); + return new GetAssetBatchJobsResult( + $this->gridBatchService->getAssetBatchJobIds($allParams, $adminUser), + ); + } +} diff --git a/src/Handler/Asset/Helper/GetAssetBatchJobsResult.php b/src/Handler/Asset/Helper/GetAssetBatchJobsResult.php new file mode 100644 index 00000000..9c51a001 --- /dev/null +++ b/src/Handler/Asset/Helper/GetAssetBatchJobsResult.php @@ -0,0 +1,25 @@ + $defaultMetadata, 'name' => $defaultMetadata, 'datatype' => 'data', 'fieldtype' => 'input']; + } + $result['defaultColumns']['nodeLabel'] = 'default_metadata'; + $result['defaultColumns']['nodeType'] = 'image'; + $result['defaultColumns']['children'] = $defaultColumns; + + //predefined metadata + $list = Metadata\Predefined\Listing::getByTargetType('asset'); + $metadataItems = []; + $tmp = []; + foreach ($list as $item) { + //only allow unique metadata columns with subtypes + $uniqueKey = $item->getName() . '_' . $item->getTargetSubtype(); + if (!in_array($uniqueKey, $tmp) && !in_array($item->getName(), $defaultMetadataNames)) { + $tmp[] = $uniqueKey; + $item->expand(); + $name = SecurityHelper::convertHtmlSpecialChars($item->getName()); + $metadataItems[] = [ + 'title' => $name, + 'name' => $name, + 'subtype' => $item->getTargetSubtype(), + 'datatype' => 'data', + 'fieldtype' => $item->getType(), + 'config' => $item->getConfig(), + ]; + } + } + + $result['metadataColumns']['children'] = $metadataItems; + $result['metadataColumns']['nodeLabel'] = 'predefined_metadata'; + $result['metadataColumns']['nodeType'] = 'metadata'; + + //system columns + $systemColumnNames = Asset\Service::GRID_SYSTEM_COLUMNS; + $systemColumns = []; + foreach ($systemColumnNames as $systemColumn) { + $systemColumns[] = ['title' => $systemColumn, 'name' => $systemColumn, 'datatype' => 'data', 'fieldtype' => 'system']; + } + $result['systemColumns']['nodeLabel'] = 'system_columns'; + $result['systemColumns']['nodeType'] = 'system'; + $result['systemColumns']['children'] = $systemColumns; + + return new GetAssetMetadataForColumnConfigResult($result); + } +} diff --git a/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigResult.php b/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigResult.php new file mode 100644 index 00000000..2ca93512 --- /dev/null +++ b/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $list = $this->gridHelperService->prepareAssetListingForGrid($allParams, $adminUser); + + if (empty($ids = $allParams['ids'] ?? '')) { + $ids = $list->loadIdList(); + } + + $jobs = array_chunk($ids, 20); + + $fileHandle = uniqid('asset-export-', false); + $storage = Storage::get('temp'); + $storage->write($this->gridExportService->getCsvFile($fileHandle), ''); + + return new GetExportJobsResult($jobs, $fileHandle); + } +} diff --git a/src/Handler/Asset/Helper/GetExportJobsResult.php b/src/Handler/Asset/Helper/GetExportJobsResult.php new file mode 100644 index 00000000..e55abd49 --- /dev/null +++ b/src/Handler/Asset/Helper/GetExportJobsResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + $asset = Asset::getById(is_numeric($classId) ? (int) $classId : 0); + + if (!$asset || !$asset->isAllowed('list')) { + throw new AccessDeniedHttpException(); + } + + $favourite = new GridConfigFavourite(); + $favourite->setOwnerId($adminUser->getId()); + $favourite->setClassId($classId); + $favourite->setSearchType($searchType); + $favourite->setType($type); + + try { + if ($gridConfigId !== 0) { + $gridConfig = GridConfig::getById($gridConfigId); + $favourite->setGridConfigId($gridConfig->getId()); + } + + $favourite->setObjectId(0); + $favourite->save(); + } catch (Exception) { + $favourite->delete(); + } + + return new MarkGridConfigFavouriteResult(false); + } +} diff --git a/src/Handler/Asset/Helper/MarkGridConfigFavouriteResult.php b/src/Handler/Asset/Helper/MarkGridConfigFavouriteResult.php new file mode 100644 index 00000000..8673eab6 --- /dev/null +++ b/src/Handler/Asset/Helper/MarkGridConfigFavouriteResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $asset = Asset::getById($assetId); + if (!$asset) { + throw new NotFoundHttpException(); + } + + if (!$asset->isAllowed('list')) { + throw new AccessDeniedHttpException(); + } + + $gridConfigData['opendxp_version'] = Version::getVersion(); + $gridConfigData['opendxp_revision'] = Version::getRevision(); + $gridConfigData['context'] = $context; + unset($gridConfigData['settings']['isShared']); + + $gridConfigId = $metadata['gridConfigId'] ?? null; + $gridConfig = null; + if ($gridConfigId) { + $gridConfig = GridConfig::getById($gridConfigId); + } + + if ($gridConfig && $gridConfig->getOwnerId() !== $adminUser->getId()) { + throw new BadRequestHttpException("don't mess around with somebody else's configuration"); + } + + $this->gridColumnConfigService->updateGridConfigShares($gridConfig, $metadata ?? [], $adminUser); + + if (!$gridConfig) { + $gridConfig = new GridConfig(); + $gridConfig->setName(date('c')); + $gridConfig->setClassId($classId); + $gridConfig->setSearchType($searchType); + $gridConfig->setType($type); + $gridConfig->setOwnerId($adminUser->getId()); + } + + if ($metadata) { + $gridConfig->setName($metadata['gridConfigName']); + $gridConfig->setDescription($metadata['gridConfigDescription']); + $gridConfig->setShareGlobally($metadata['shareGlobally'] && $adminUser->isAdmin()); + $gridConfig->setSetAsFavourite($metadata['setAsFavourite'] && $adminUser->isAdmin()); + } + + $gridConfig->setConfig(json_encode($gridConfigData)); + $gridConfig->save(); + + if (!empty($metadata['setAsFavourite']) && $adminUser->isAdmin()) { + $this->gridColumnConfigService->updateGridConfigFavourites($gridConfig, $metadata, $adminUser); + } + + $availableConfigs = $this->gridColumnConfigService->getMyOwnColumnConfigs($adminUser->getId(), $classId ?? '', $searchType); + $sharedConfigs = $this->gridColumnConfigService->getSharedColumnConfigs($adminUser, $classId ?? '', $searchType); + + $settings = $this->gridColumnConfigService->getShareSettings($gridConfig->getId()); + $settings['gridConfigId'] = (int) $gridConfig->getId(); + $settings['gridConfigName'] = $gridConfig->getName(); + $settings['gridConfigDescription'] = $gridConfig->getDescription(); + $settings['shareGlobally'] = $gridConfig->isShareGlobally(); + $settings['setAsFavourite'] = $gridConfig->isSetAsFavourite(); + $settings['isShared'] = $gridConfig->getOwnerId() !== $adminUser->getId(); + + return new SaveGridColumnConfigResult($settings, $availableConfigs, $sharedConfigs); + } +} diff --git a/src/Handler/Asset/Helper/SaveGridColumnConfigResult.php b/src/Handler/Asset/Helper/SaveGridColumnConfigResult.php new file mode 100644 index 00000000..b531dcfa --- /dev/null +++ b/src/Handler/Asset/Helper/SaveGridColumnConfigResult.php @@ -0,0 +1,27 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('not allowed to view'); + } + + $text = null; + if ($asset instanceof Asset\Document) { + $text = $asset->getText($page); + } + + return new AssetTextResult($text); + } +} diff --git a/src/Handler/Asset/Media/GetDocumentPreviewHandler.php b/src/Handler/Asset/Media/GetDocumentPreviewHandler.php new file mode 100644 index 00000000..fe674287 --- /dev/null +++ b/src/Handler/Asset/Media/GetDocumentPreviewHandler.php @@ -0,0 +1,103 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('Access to asset ' . $asset->getId() . ' denied'); + } + + $scanStatus = null; + $thumbnailPath = null; + $stream = null; + + if ($asset->getMimeType() === self::PDF_MIMETYPE) { + $scanStatus = $this->getScanStatus($asset); + $openPdfConfig = Config::getSystemConfiguration('assets')['document']['open_pdf_in_new_tab']; + + $openInNewTab = $openPdfConfig === 'all-pdfs' + || ($openPdfConfig === 'only-unsafe' && $scanStatus === PdfScanStatus::UNSAFE); + + if ($openInNewTab) { + $thumbnail = $asset->getImageThumbnail(Asset\Image\Thumbnail\Config::getPreviewConfig()); + $thumbnailPath = $thumbnail->getPath(); + + return new PreviewDocumentResult($asset, $scanStatus, $thumbnailPath, $asset->getRealFullPath(), null); + } + } + + if ($scanStatus === null || ($scanStatus !== PdfScanStatus::IN_PROGRESS && $scanStatus !== PdfScanStatus::UNSAFE)) { + $stream = $this->getPreviewPdf($asset); + } + + return new PreviewDocumentResult($asset, $scanStatus, null, $asset->getRealFullPath(), $stream); + } + + private function getScanStatus(Asset\Document $asset): ?PdfScanStatus + { + if (!Config::getSystemConfiguration('assets')['document']['scan_pdf']) { + return null; + } + + $scanStatus = $asset->getScanStatus(); + if (!$scanStatus instanceof PdfScanStatus) { + $asset->addToUpdateTaskQueue(); + + return PdfScanStatus::IN_PROGRESS; + } + + return $scanStatus; + } + + private function getPreviewPdf(Asset\Document $asset): mixed + { + $stream = null; + + if ($asset->getMimeType() === self::PDF_MIMETYPE) { + $stream = $asset->getStream(); + } + + if (!$stream && $asset->getPageCount() && \OpenDxp\Document::isAvailable() && \OpenDxp\Document::isFileTypeSupported($asset->getFilename())) { + try { + $document = \OpenDxp\Document::getInstance(); + $stream = $document->getPdf($asset); + } catch (Exception) { + // nothing to do + } + } + + return $stream; + } +} diff --git a/src/Handler/Asset/Media/GetVideoPreviewHandler.php b/src/Handler/Asset/Media/GetVideoPreviewHandler.php new file mode 100644 index 00000000..84c01568 --- /dev/null +++ b/src/Handler/Asset/Media/GetVideoPreviewHandler.php @@ -0,0 +1,44 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('not allowed to preview'); + } + + $config = Asset\Video\Thumbnail\Config::getByName($configName); + if (!$config instanceof Asset\Video\Thumbnail\Config) { + $config = Asset\Video\Thumbnail\Config::getPreviewConfig(); + } + + $thumbnail = $asset->getThumbnail($config, ['mp4']); + $isFinished = $thumbnail && $thumbnail['status'] === 'finished'; + + return new PreviewVideoResult($asset, $thumbnail, $config->getName(), $isFinished); + } +} diff --git a/src/Handler/Asset/Media/PreviewDocumentResult.php b/src/Handler/Asset/Media/PreviewDocumentResult.php new file mode 100644 index 00000000..6d7bc223 --- /dev/null +++ b/src/Handler/Asset/Media/PreviewDocumentResult.php @@ -0,0 +1,34 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('not allowed to preview'); + } + + $config = Asset\Video\Thumbnail\Config::getByName($configName); + if (!$config instanceof Asset\Video\Thumbnail\Config) { + $config = Asset\Video\Thumbnail\Config::getPreviewConfig(); + } + + $thumbnail = $asset->getThumbnail($config, ['mp4']); + $storagePath = $asset->getRealPath() . '/' . preg_replace('@^' . preg_quote($asset->getPath(), '@') . '@', '', urldecode($thumbnail['formats']['mp4'])); + + $storage = Tool\Storage::get('thumbnail'); + if (!$storage->fileExists($storagePath)) { + throw new NotFoundHttpException('Video thumbnail not found'); + } + + return new ServeVideoPreviewResult( + $storage->readStream($storagePath), + $storage->fileSize($storagePath), + ); + } +} diff --git a/src/Handler/Asset/Media/ServeVideoPreviewResult.php b/src/Handler/Asset/Media/ServeVideoPreviewResult.php new file mode 100644 index 00000000..b386ecba --- /dev/null +++ b/src/Handler/Asset/Media/ServeVideoPreviewResult.php @@ -0,0 +1,26 @@ +isAllowed('publish')) { + throw new AccessDeniedHttpException(); + } + + $this->payloadMapper->applyPayload($payload, $asset); + + return $this->coordinator->save($asset, $payload->task); + } +} diff --git a/src/Handler/Asset/SaveAssetResult.php b/src/Handler/Asset/SaveAssetResult.php new file mode 100644 index 00000000..8dc766b3 --- /dev/null +++ b/src/Handler/Asset/SaveAssetResult.php @@ -0,0 +1,27 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('not allowed to view thumbnail'); + } + + $thumbnail = Asset\Image\Thumbnail\Config::getByAutoDetect($queryAll); + + $format = strtolower($thumbnail->getFormat()); + if ($format === 'source') { + $thumbnail->setFormat('jpeg'); + } + + if ($hasThumbnailPreview) { + $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig(); + } + + $thumb = $document->getImageThumbnail($thumbnail, $page ?? 1); + + if ($origin === 'treeNode' && !$thumb->exists()) { + $this->messageBus->dispatch(new AssetPreviewImageMessage($document->getId())); + throw new NotFoundHttpException(sprintf('Tree preview thumbnail not available for asset %s', $document->getId())); + } + + return new GetDocumentThumbnailResult($thumb->getStream(), $thumb->getFileExtension()); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetDocumentThumbnailResult.php b/src/Handler/Asset/Thumbnail/GetDocumentThumbnailResult.php new file mode 100644 index 00000000..5cd2702e --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetDocumentThumbnailResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + $filterPrepareEvent = new GenericEvent(null, ['requestParams' => $requestParams]); + $this->eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE); + $requestParams = $filterPrepareEvent->getArgument('requestParams'); + + $folder = Asset::getById((int) $requestParams['id']); + + $start = (int) ($requestParams['start'] ?? 0); + $limit = (int) ($requestParams['limit'] ?? 10); + + $conditionFilters = []; + $list = new Asset\Listing(); + $conditionFilters[] = '`path` LIKE ' . ($folder->getRealFullPath() === '/' ? "'/%'" : $list->quote(Helper::escapeLike($folder->getRealFullPath()) . '/%')) . " AND `type` != 'folder'"; + + if (!$adminUser->isAdmin()) { + $conditionFilters[] = $this->gridHelperService->getPermittedPathsByUser('asset', $adminUser); + } + + $list->setCondition(implode(' AND ', $conditionFilters)); + $list->setLimit($limit); + $list->setOffset($start); + $list->setOrderKey('CAST(filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci ASC', false); + + $beforeListLoadEvent = new GenericEvent(null, ['list' => $list, 'context' => $requestParams]); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD); + /** @var Asset\Listing $list */ + $list = $beforeListLoadEvent->getArgument('list'); + + $list->load(); + + $assets = []; + foreach ($list as $asset) { + if (!$asset->isAllowed('list')) { + continue; + } + + $filenameDisplay = $asset->getFilename(); + if (strlen($filenameDisplay) > 32) { + $filenameDisplay = substr($filenameDisplay, 0, 25) . '...' . pathinfo($filenameDisplay, PATHINFO_EXTENSION); + } + + $assets[] = [ + 'id' => $asset->getId(), + 'type' => $asset->getType(), + 'filename' => $asset->getFilename(), + 'filenameDisplay' => htmlspecialchars($filenameDisplay ?? ''), + 'url' => $this->elementService->getThumbnailUrl($asset, ['origin' => 'folderPreview']), + 'idPath' => Element\Service::getIdPath($asset), + ]; + } + + $result = ['data' => $assets, 'total' => $list->getTotalCount()]; + + $afterListLoadEvent = new GenericEvent(null, ['list' => $result, 'context' => $requestParams]); + $this->eventDispatcher->dispatch($afterListLoadEvent, AdminEvents::ASSET_LIST_AFTER_LIST_LOAD); + $result = $afterListLoadEvent->getArgument('list'); + + return new GetFolderContentPreviewResult($result['data'], $result['total']); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetFolderContentPreviewResult.php b/src/Handler/Asset/Thumbnail/GetFolderContentPreviewResult.php new file mode 100644 index 00000000..0a988540 --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetFolderContentPreviewResult.php @@ -0,0 +1,26 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('not allowed to view thumbnail'); + } + + $stream = $folder->getPreviewImage(); + if (!$stream) { + throw new NotFoundHttpException(sprintf('Tree preview thumbnail not available for asset %s', $folder->getId())); + } + + return new FolderThumbnailResult($stream); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetImageThumbnailHandler.php b/src/Handler/Asset/Thumbnail/GetImageThumbnailHandler.php new file mode 100644 index 00000000..71957390 --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetImageThumbnailHandler.php @@ -0,0 +1,95 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('not allowed to view thumbnail'); + } + + $thumbnailConfig = null; + + if ($thumbnailParam) { + $thumbnailConfig = $image->getThumbnail($thumbnailParam)->getConfig(); + } + if (!$thumbnailConfig) { + if ($configDecoded) { + $thumbnailConfig = $image->getThumbnail($configDecoded)->getConfig(); + } else { + $thumbnailConfig = $image->getThumbnail($queryAll)->getConfig(); + } + } else { + $thumbnailConfig->setHighResolution(1); + } + + $format = strtolower($thumbnailConfig->getFormat()); + if ($format === 'source' || $format === 'print') { + $thumbnailConfig->setFormat('PNG'); + $thumbnailConfig->setRasterizeSVG(true); + } + + if ($hasThumbnailPreview) { + $thumbnailConfig = Asset\Image\Thumbnail\Config::getPreviewConfig(); + if (!$image->getThumbnail($thumbnailConfig)->exists()) { + $this->messageBus->dispatch(new AssetPreviewImageMessage($image->getId())); + + return new GetImageThumbnailResult($image, null, $origin === 'folderPreview', false); + } + } + + if ($hasCropPercent) { + $thumbnailConfig->addItemAt(0, 'cropPercent', [ + 'width' => $cropWidth, + 'height' => $cropHeight, + 'y' => $cropTop, + 'x' => $cropLeft, + ]); + + $thumbnailConfig->generateAutoName(); + } + + $thumbnailResult = $image->getThumbnail($thumbnailConfig); + + return new GetImageThumbnailResult($image, $thumbnailResult, false, $hasFileinfo); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetImageThumbnailResult.php b/src/Handler/Asset/Thumbnail/GetImageThumbnailResult.php new file mode 100644 index 00000000..aa4029ae --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetImageThumbnailResult.php @@ -0,0 +1,30 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException('not allowed to view thumbnail'); + } + + $thumbnailConfig = $queryAll; + if ($hasThumbnailPreview) { + $thumbnailConfig = Asset\Image\Thumbnail\Config::getPreviewConfig(); + } + + $timeInt = is_numeric($time) ? (int) $time : null; + + if ($hasSetTime) { + $video->removeCustomSetting('image_thumbnail_asset'); + $video->setCustomSetting('image_thumbnail_time', $timeInt); + $video->save(); + } + + $image = null; + if ($hasImage) { + $image = Asset\Image::getById($imageId) ?? throw new AssetNotFoundException($imageId); + } + + if ($hasSetImage && $image) { + $video->removeCustomSetting('image_thumbnail_time'); + $video->setCustomSetting('image_thumbnail_asset', $image->getId()); + $video->save(); + } + + $thumb = $video->getImageThumbnail($thumbnailConfig, $timeInt, $image); + + if ($origin === 'treeNode' && !$thumb->exists()) { + $this->messageBus->dispatch(new AssetPreviewImageMessage($video->getId())); + throw new NotFoundHttpException(sprintf('Tree preview thumbnail not available for asset %s', $video->getId())); + } + + $stream = $thumb->getStream(); + if (!$stream) { + throw new NotFoundHttpException('Unable to get video thumbnail for video ' . $video->getId()); + } + + return new GetVideoThumbnailResult($stream, $thumb->getFileExtension()); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetVideoThumbnailResult.php b/src/Handler/Asset/Thumbnail/GetVideoThumbnailResult.php new file mode 100644 index 00000000..f1c35028 --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetVideoThumbnailResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + $asset = Asset::getById($assetId); + $allowUpdate = true; + + if ($asset->isAllowed('settings')) { + $asset->setUserModification($adminUser->getId()); + + if (isset($updateData['parentId']) && $updateData['parentId']) { + $parentAsset = Asset::getById((int) $updateData['parentId']); + + if ($asset->getParentId() !== $parentAsset->getId()) { + if (!$parentAsset->isAllowed('create')) { + throw new RuntimeException('Prevented moving asset - no create permission on new parent.'); + } + + $intendedPath = $parentAsset->getRealPath(); + $pKey = $parentAsset->getKey(); + if (!empty($pKey)) { + $intendedPath .= $parentAsset->getKey() . '/'; + } + + if (Asset::getByPath($intendedPath . $asset->getKey()) != null) { + $allowUpdate = false; + } + + if ($asset->isLocked()) { + $allowUpdate = false; + } + } + } + + if ($allowUpdate) { + if (isset($updateData['filename']) && $updateData['filename'] != $asset->getFilename() && !$asset->isAllowed('rename')) { + unset($updateData['filename']); + Logger::debug('prevented renaming asset because of missing permissions.'); + } + + $asset->setValues($updateData); + $asset->save(); + + return new UpdateAssetResult($this->elementService->getElementTreeNodeConfig($asset)); + } + + $msg = 'prevented moving asset, asset with same path+key already exists at target location or the asset is locked. ID: ' . $asset->getId(); + Logger::debug($msg); + throw new BadRequestHttpException($msg); + } + + if ($asset->isAllowed('rename') && isset($updateData['filename'])) { + $asset->setFilename($updateData['filename']); + $asset->save(); + + return new UpdateAssetResult($this->elementService->getElementTreeNodeConfig($asset)); + } + + Logger::debug('prevented update asset because of missing permissions'); + throw new AccessDeniedHttpException('prevented update asset because of missing permissions'); + } +} diff --git a/src/Handler/Asset/UpdateAssetResult.php b/src/Handler/Asset/UpdateAssetResult.php new file mode 100644 index 00000000..5607a58c --- /dev/null +++ b/src/Handler/Asset/UpdateAssetResult.php @@ -0,0 +1,25 @@ +getRealFullPath() . $dir . '/' . $filename); + } +} \ No newline at end of file diff --git a/src/Handler/Asset/Upload/ImportZipFilesHandler.php b/src/Handler/Asset/Upload/ImportZipFilesHandler.php new file mode 100644 index 00000000..00756a80 --- /dev/null +++ b/src/Handler/Asset/Upload/ImportZipFilesHandler.php @@ -0,0 +1,112 @@ +userContext->getAdminUser()?->getId() ?? 0; + $importAsset = Asset::getById($parentId); + $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . $jobId . '.zip'; + $tmpDir = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/zip-import'; + + if (!is_dir($tmpDir)) { + $this->filesystem->mkdir($tmpDir); + } + + $zip = new ZipArchive(); + if ($zip->open($zipFile) !== true) { + return; + } + + for ($i = $offset; $i < ($offset + $limit); $i++) { + $path = $zip->getNameIndex($i); + if (str_starts_with($path, '__MACOSX/')) { + continue; + } + if (str_ends_with($path, '/Thumbs.db')) { + continue; + } + if (str_ends_with($path, '/.DS_Store')) { + continue; + } + + if ($path !== false && $zip->extractTo($tmpDir . '/', $path)) { + $tmpFile = $tmpDir . '/' . preg_replace('@^/@', '', $path); + $filename = Element\Service::getValidKey(basename($path), 'asset'); + $relativePath = ''; + if (dirname($path) !== '.') { + $relativePath = dirname($path); + } + $parentPath = $importAsset->getRealFullPath() . '/' . preg_replace('@^/@', '', $relativePath); + $parent = Asset\Service::createFolderByPath($parentPath); + + if (!$allowOverwrite) { + $filename = $this->assetUploadService->getSafeFilename($parent->getRealFullPath(), $filename); + } + + if ($parent->isAllowed('create')) { + if ($allowOverwrite && Asset\Service::pathExists($parent->getRealFullPath() . '/' . $filename)) { + $asset = Asset::getByPath($parent->getRealFullPath() . '/' . $filename); + $asset->setStream(fopen($tmpFile, 'rb', false, File::getContext())); + $asset->save(); + } else { + Asset::create($parent->getId(), [ + 'filename' => $filename, + 'sourcePath' => $tmpFile, + 'userOwner' => $userId, + 'userModification' => $userId, + ]); + } + + @unlink($tmpFile); + } else { + Logger::debug('prevented creating asset because of missing permissions'); + } + } + } + + $zip->close(); + + if ($isLast) { + unlink($zipFile); + } + } +} diff --git a/src/Handler/Asset/Upload/ImportZipHandler.php b/src/Handler/Asset/Upload/ImportZipHandler.php new file mode 100644 index 00000000..d05e7818 --- /dev/null +++ b/src/Handler/Asset/Upload/ImportZipHandler.php @@ -0,0 +1,77 @@ +isAllowed('create')) { + throw new AccessDeniedHttpException('not allowed to create'); + } + + $jobId = uniqid('', false); + $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . $jobId . '.zip'; + copy($uploadedFilePath, $zipFile); + + $zip = new ZipArchive(); + if ($zip->open($zipFile) !== true) { + throw new BadRequestHttpException($this->translator->trans('could_not_open_zip_file', [], 'admin')); + } + + $numFiles = $zip->numFiles; + $zip->close(); + + $importZipFilesUrl = $this->router->generate('opendxp_admin_asset_importzipfiles'); + $jobAmount = (int) ceil($numFiles / self::FILES_PER_JOB); + $jobs = []; + for ($i = 0; $i < $jobAmount; $i++) { + $jobs[] = [[ + 'url' => $importZipFilesUrl, + 'method' => 'POST', + 'params' => [ + 'parentId' => $asset->getId(), + 'offset' => $i * self::FILES_PER_JOB, + 'limit' => self::FILES_PER_JOB, + 'jobId' => $jobId, + 'last' => (($i + 1) >= $jobAmount) ? 'true' : '', + 'allowOverwrite' => $allowOverwrite ?: 'false', + ], + ]]; + } + + return new ImportZipResult(jobId: $jobId, jobs: $jobs); + } +} \ No newline at end of file diff --git a/src/Handler/Asset/Upload/ImportZipResult.php b/src/Handler/Asset/Upload/ImportZipResult.php new file mode 100644 index 00000000..698effb2 --- /dev/null +++ b/src/Handler/Asset/Upload/ImportZipResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser()?->getId() ?? 0; + $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); + + $newFilename = Element\Service::getValidKey($originalFilename, 'asset'); + $mimetype = MimeTypes::getDefault()->guessMimeType($filePath); + $newType = Asset::getTypeFromMimeMapping($mimetype, $newFilename); + + if ($newType !== $asset->getType()) { + throw new BadRequestHttpException(sprintf( + $this->translator->trans('asset_type_change_not_allowed', [], 'admin'), + $newType, + $asset->getType(), + )); + } + + $stream = fopen($filePath, 'rb+'); + $asset->setStream($stream); + $asset->setCustomSetting('thumbnails', null); + + if (method_exists($asset, 'getEmbeddedMetaData')) { + $asset->getEmbeddedMetaData(true); + } + + $asset->setUserModification($userId); + + $newFileExt = pathinfo($newFilename, PATHINFO_EXTENSION); + $currentFileExt = pathinfo($asset->getFilename(), PATHINFO_EXTENSION); + if ($newFileExt !== $currentFileExt) { + $newFilename = preg_replace('/\.' . $currentFileExt . '$/i', '.' . $newFileExt, $asset->getFilename()); + $newFilename = Element\Service::getSafeCopyName($newFilename, $asset->getParent()); + $asset->setFilename($newFilename); + } + + if (!$asset->isAllowed('publish')) { + throw new AccessDeniedHttpException('missing permission'); + } + + $asset->save(); + + return $asset; + } +} diff --git a/src/Handler/Asset/Version/PublishVersionHandler.php b/src/Handler/Asset/Version/PublishVersionHandler.php new file mode 100644 index 00000000..ae353c12 --- /dev/null +++ b/src/Handler/Asset/Version/PublishVersionHandler.php @@ -0,0 +1,54 @@ +userContext->getAdminUser()?->getId() ?? 0; + $version = Version::getById($versionId) + ?? throw new AssetVersionNotFoundException($versionId); + + $asset = $version->loadData(); + if (!$asset instanceof Asset) { + throw new AssetVersionNotFoundException($versionId); + } + + $currentAsset = Asset::getById($asset->getId()); + if (!$currentAsset?->isAllowed('publish')) { + throw new AccessDeniedHttpException(); + } + + $asset->setUserModification($userId); + $asset->save(); + + return new AssetResult($asset); + } +} diff --git a/src/Handler/Asset/Version/ShowVersionHandler.php b/src/Handler/Asset/Version/ShowVersionHandler.php new file mode 100644 index 00000000..c3de9f2a --- /dev/null +++ b/src/Handler/Asset/Version/ShowVersionHandler.php @@ -0,0 +1,54 @@ +loadData(); + if (!$asset instanceof Asset) { + throw new AssetVersionNotFoundException($versionId); + } + + if (!$asset->isAllowed('versions')) { + throw new AccessDeniedHttpException(); + } + + if ($asset instanceof Asset\Document && $asset->getMimeType() === self::PDF_MIMETYPE) { + return new ShowVersionResult( + asset: $asset, + version: $version, + isPdf: true, + pdfPath: $asset->getRealFullPath(), + ); + } + + return new ShowVersionResult(asset: $asset, version: $version); + } +} diff --git a/src/Handler/Asset/Version/ShowVersionResult.php b/src/Handler/Asset/Version/ShowVersionResult.php new file mode 100644 index 00000000..cbee6baa --- /dev/null +++ b/src/Handler/Asset/Version/ShowVersionResult.php @@ -0,0 +1,31 @@ +userContext->getAdminUser()?->getId() ?? 0; + $parent = DataObject::getById($parentId); + if ($parent === null) { + throw new NotFoundHttpException("Parent object not found: $parentId"); + } + + if (!$parent->isAllowed('create')) { + throw new AccessDeniedHttpException('prevented creating folder because of missing permissions'); + } + + if (DataObject\Service::pathExists($parent->getRealFullPath() . '/' . $key)) { + throw new BadRequestHttpException('folder with same path+key already exists'); + } + + $folder = DataObject\Folder::create([ + 'parentId' => $parentId, + 'creationDate' => time(), + 'userOwner' => $userId, + 'userModification' => $userId, + 'key' => $key, + 'published' => true, + ]); + + $folder->save(); + } +} diff --git a/src/Handler/DataObject/AddObjectHandler.php b/src/Handler/DataObject/AddObjectHandler.php new file mode 100644 index 00000000..8c1bbe52 --- /dev/null +++ b/src/Handler/DataObject/AddObjectHandler.php @@ -0,0 +1,84 @@ +userContext->getAdminUser()?->getId() ?? 0; + $parent = DataObject::getById($parentId); + if ($parent === null) { + throw new NotFoundHttpException("Parent object not found: $parentId"); + } + + if (!$parent->isAllowed('create')) { + throw new AccessDeniedHttpException('prevented adding object because of missing permissions'); + } + + if (DataObject\Service::pathExists($parent->getRealFullPath() . '/' . $key)) { + throw new BadRequestHttpException('prevented creating object because object with same path+key already exists'); + } + + if ($variantViaTree) { + if (!$parent instanceof DataObject\Concrete) { + throw new BadRequestHttpException('Parent must be a concrete object for variant creation'); + } + $classId = $parent->getClass()->getId(); + } + + $fqcn = 'OpenDxp\\Model\\DataObject\\' . ucfirst($className); + /** @var DataObject\Concrete $object */ + $object = $this->modelFactory->build($fqcn); + $object->setOmitMandatoryCheck(true); + $object->setClassId($classId); + $object->setClassName($className); + $object->setParentId($parentId); + $object->setKey($key); + $object->setCreationDate(time()); + $object->setUserOwner($userId); + $object->setUserModification($userId); + $object->setPublished(false); + + if (in_array($objectType, [DataObject::OBJECT_TYPE_OBJECT, DataObject::OBJECT_TYPE_VARIANT])) { + $object->setType($objectType); + } + + $object->save(); + + return new AddObjectResult(id: $object->getId(), type: $object->getType()); + } +} diff --git a/src/Handler/DataObject/AddObjectResult.php b/src/Handler/DataObject/AddObjectResult.php new file mode 100644 index 00000000..d2c8f286 --- /dev/null +++ b/src/Handler/DataObject/AddObjectResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + if (!in_array($sortOrder, ['ASC', 'DESC'])) { + $sortOrder = 'ASC'; + } + + $object = DataObject::getById($id); + + if (!$object) { + throw new NotFoundHttpException(sprintf('DataObject with id %d not found', $id)); + } + + $currentSortBy = $object->getChildrenSortBy(); + + $object->setChildrenSortBy($sortBy); + $object->setChildrenSortOrder($sortOrder); + + if ($currentSortBy !== $sortBy) { + if (!$adminUser->isAdmin() && !$adminUser->isAllowed('objects_sort_method')) { + throw new AccessDeniedHttpException('Changing the sort method is only allowed for admin users'); + } + + if ($sortBy === 'index') { + $this->reindexBasedOnSortOrder($object, $sortOrder); + } + } + + $object->save(); + } + + private function reindexBasedOnSortOrder(DataObject\AbstractObject $parentObject, string $currentSortOrder): void + { + $fn = function () use ($parentObject, $currentSortOrder): void { + $list = new DataObject\Listing(); + + $db = Db::get(); + $db->executeStatement( + 'UPDATE ' . $list->getDao()->getTableName() . ' o, + ( + SELECT newIndex, id FROM ( + SELECT @n := @n +1 AS newIndex, id + FROM ' . $list->getDao()->getTableName() . ', + (SELECT @n := -1) variable + WHERE parentId = ? ORDER BY `key` ' . $currentSortOrder + . ') tmp + ) order_table + SET o.index = order_table.newIndex + WHERE o.id=order_table.id', + [ + $parentObject->getId(), + ] + ); + + $db = Db::get(); + $children = $db->fetchAllAssociative( + 'SELECT id, modificationDate, versionCount FROM objects WHERE parentId = ? ORDER BY `index` ASC', + [$parentObject->getId()] + ); + + foreach ($children as $child) { + $this->updateLatestVersionIndex($child['id'], $child['modificationDate']); + + DataObject::clearDependentCacheByObjectId($child['id']); + } + }; + + $this->executeInsideTransaction($fn); + } + + private function updateLatestVersionIndex(int $objectId, int $newIndex): void + { + $object = DataObject\Concrete::getById($objectId); + + if ( + $object && + $object->getType() !== DataObject::OBJECT_TYPE_FOLDER && + $latestVersion = $object->getLatestVersion() + ) { + // don't renew references (which means loading the target elements) + // Not needed as we just save a new version with the updated index + $object = $latestVersion->loadData(false); + if ($newIndex !== $object->getIndex()) { + $object->setIndex($newIndex); + } + $latestVersion->save(); + } + } + + private function executeInsideTransaction(callable $fn): void + { + $maxRetries = 5; + for ($retries = 0; $retries < $maxRetries; $retries++) { + try { + Db::get()->beginTransaction(); + + $fn(); + + Db::get()->commit(); + + break; + } catch (Exception $e) { + Db::get()->rollBack(); + + // we try to start the transaction $maxRetries times again (deadlocks, ...) + if ($retries < ($maxRetries - 1)) { + $run = $retries + 1; + $waitTime = random_int(1, 5) * 100000; // microseconds + Logger::warn('Unable to finish transaction (' . $run . ". run) because of the following reason '" . $e->getMessage() . "'. --> Retrying in " . $waitTime . ' microseconds ... (' . ($run + 1) . ' of ' . $maxRetries . ')'); + + usleep($waitTime); // wait specified time until we restart the transaction + } else { + // if the transaction still fail after $maxRetries retries, we throw out the exception + Logger::error('Finally giving up restarting the same transaction again and again, last message: ' . $e->getMessage()); + + throw $e; + } + } + } + } +} diff --git a/src/Handler/DataObject/ClassDef/AddClassHandler.php b/src/Handler/DataObject/ClassDef/AddClassHandler.php new file mode 100644 index 00000000..d5a674ad --- /dev/null +++ b/src/Handler/DataObject/ClassDef/AddClassHandler.php @@ -0,0 +1,51 @@ +userContext->getAdminUser()?->getId() ?? 0; + $existingClass = DataObject\ClassDefinition::getById($classId); + if ($existingClass) { + throw new Exception('Class identifier already exists'); + } + + $class = DataObject\ClassDefinition::create([ + 'name' => $className, + 'userOwner' => $userId, + ]); + + $class->setId($classId); + $class->save(true); + + return new AddClassResult(id: $class->getId()); + } +} diff --git a/src/Handler/DataObject/ClassDef/AddClassResult.php b/src/Handler/DataObject/ClassDef/AddClassResult.php new file mode 100644 index 00000000..c2e02208 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/AddClassResult.php @@ -0,0 +1,25 @@ + 'classes', 'objectbrick' => 'objectbricks', 'fieldcollection' => 'fieldcollections', 'customlayout' => 'classes']; + $type = $data['type']; + + if (isset($permissionMap[$type])) { + $adminUser = $this->userContext->getAdminUser(); + if (!$adminUser?->isAllowed($permissionMap[$type])) { + throw new AccessDeniedHttpException('Permission denied for bulk commit of type: ' . $type); + } + } + + $session = Session::getSessionBag($this->requestStack->getCurrentRequest()->getSession(), 'opendxp_objects'); + $filename = $session->get('class_bulk_import_file'); + + $json = @file_get_contents($filename); + $json = json_decode($json, true); + + $name = $data['name']; + $list = $json[$type]; + + foreach ($list as $item) { + unset($item['creationDate'], $item['modificationDate'], $item['userOwner'], $item['userModification']); + + if ($type === 'class' && $item['name'] == $name) { + $class = DataObject\ClassDefinition::getByName($name); + if (!$class) { + $class = new DataObject\ClassDefinition(); + $class->setName($name); + } + if (!DataObject\ClassDefinition\Service::importClassDefinitionFromJson($class, json_encode($item), true)) { + throw new RuntimeException('Failed to import class definition: ' . $name); + } + + return; + } + + if ($type === 'objectbrick' && $item['key'] == $name) { + if (!$brick = DataObject\Objectbrick\Definition::getByKey($name)) { + $brick = new DataObject\Objectbrick\Definition(); + $brick->setKey($name); + } + if (!DataObject\ClassDefinition\Service::importObjectBrickFromJson($brick, json_encode($item), true)) { + throw new RuntimeException('Failed to import objectbrick: ' . $name); + } + + return; + } + + if ($type === 'fieldcollection' && $item['key'] == $name) { + if (!$fieldCollection = DataObject\Fieldcollection\Definition::getByKey($name)) { + $fieldCollection = new DataObject\Fieldcollection\Definition(); + $fieldCollection->setKey($name); + } + if (!DataObject\ClassDefinition\Service::importFieldCollectionFromJson($fieldCollection, json_encode($item), true)) { + throw new RuntimeException('Failed to import field collection: ' . $name); + } + + return; + } + + if ($type === 'customlayout') { + $layoutData = json_decode(base64_decode($data['name']), true); + $className = $layoutData['className']; + $layoutName = $layoutData['name']; + + if ($item['name'] == $layoutName && $item['className'] == $className) { + $class = DataObject\ClassDefinition::getByName($className); + if (!$class) { + throw new BadRequestHttpException('Class does not exist'); + } + + $classId = $class->getId(); + + $layoutList = new DataObject\ClassDefinition\CustomLayout\Listing(); + $layoutList->setFilter(fn (DataObject\ClassDefinition\CustomLayout $layout) => $layout->getName() === $layoutName && $layout->getClassId() === $classId); + $layoutList = $layoutList->load(); + + $layoutDefinition = null; + if ($layoutList) { + $layoutDefinition = array_values($layoutList)[0]; + } + + if (!$layoutDefinition) { + $layoutDefinition = new DataObject\ClassDefinition\CustomLayout(); + $layoutDefinition->setName($layoutName); + $layoutDefinition->setClassId($classId); + } + + $layoutDefinition->setDescription($item['description']); + $layoutDef = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($item['layoutDefinitions'], true); + $layoutDefinition->setLayoutDefinitions($layoutDef); + $layoutDefinition->save(); + } + } + } + } +} diff --git a/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php b/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php new file mode 100644 index 00000000..dc4d83b6 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php @@ -0,0 +1,38 @@ +requestStack->getCurrentRequest()->getSession(), + static function (AttributeBagInterface $session) use ($data): void { + $session->set('class_bulk_export_settings', $data); + }, + 'opendxp_objects' + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/BulkImportHandler.php b/src/Handler/DataObject/ClassDef/BulkImportHandler.php new file mode 100644 index 00000000..df308177 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/BulkImportHandler.php @@ -0,0 +1,83 @@ +requestStack->getCurrentRequest()->getSession(), + static function (AttributeBagInterface $session) use ($tmpName): void { + $session->set('class_bulk_import_file', $tmpName); + }, + 'opendxp_objects' + ); + + $parsed = json_decode($json, true); + $result = []; + + foreach ($parsed as $groupName => $group) { + foreach ($group as $groupItem) { + $displayName = null; + $icon = null; + + if ($groupName === 'class') { + $name = $groupItem['name']; + $icon = 'class'; + } elseif ($groupName === 'customlayout') { + $className = $groupItem['className']; + $layoutData = ['className' => $className, 'name' => $groupItem['name']]; + $name = base64_encode(json_encode($layoutData)); + $displayName = $className . ' / ' . $groupItem['name']; + $icon = 'custom_views'; + } else { + if ($groupName === 'objectbrick') { + $icon = 'objectbricks'; + } elseif ($groupName === 'fieldcollection') { + $icon = 'fieldcollection'; + } + $name = $groupItem['key']; + } + + if (!$displayName) { + $displayName = $name; + } + + $result[] = [ + 'icon' => $icon, + 'checked' => true, + 'type' => $groupName, + 'name' => $name, + 'displayName' => $displayName, + ]; + } + } + + return new BulkImportResult(items: $result); + } +} diff --git a/src/Handler/DataObject/ClassDef/BulkImportResult.php b/src/Handler/DataObject/ClassDef/BulkImportResult.php new file mode 100644 index 00000000..18f9a9bb --- /dev/null +++ b/src/Handler/DataObject/ClassDef/BulkImportResult.php @@ -0,0 +1,25 @@ +delete(); + } + } +} diff --git a/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php b/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php new file mode 100644 index 00000000..a3d1c976 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php @@ -0,0 +1,34 @@ +delete(); + } +} diff --git a/src/Handler/DataObject/ClassDef/DoBulkExportHandler.php b/src/Handler/DataObject/ClassDef/DoBulkExportHandler.php new file mode 100644 index 00000000..e05d652b --- /dev/null +++ b/src/Handler/DataObject/ClassDef/DoBulkExportHandler.php @@ -0,0 +1,73 @@ +requestStack->getCurrentRequest()->getSession(), 'opendxp_objects'); + $list = json_decode($session->get('class_bulk_export_settings'), true); + + $adminUser = $this->userContext->getAdminUser(); + $result = []; + + foreach ($list as $item) { + if ($item['type'] === 'fieldcollection' && $adminUser->isAllowed('fieldcollections')) { + if ($fieldCollection = DataObject\Fieldcollection\Definition::getByKey($item['name'])) { + $fieldCollectionJson = json_decode(DataObject\ClassDefinition\Service::generateFieldCollectionJson($fieldCollection)); + $fieldCollectionJson->key = $item['name']; + $result['fieldcollection'][] = $fieldCollectionJson; + } + } elseif ($item['type'] === 'class' && $adminUser->isAllowed('classes')) { + if ($class = DataObject\ClassDefinition::getByName($item['name'])) { + $data = json_decode(DataObject\ClassDefinition\Service::generateClassDefinitionJson($class)); + $data->name = $item['name']; + $result['class'][] = $data; + } + } elseif ($item['type'] === 'objectbrick' && $adminUser->isAllowed('objectbricks')) { + if ($objectBrick = DataObject\Objectbrick\Definition::getByKey($item['name'])) { + $objectBrickJson = json_decode(DataObject\ClassDefinition\Service::generateObjectBrickJson($objectBrick)); + $objectBrickJson->key = $item['name']; + $result['objectbrick'][] = $objectBrickJson; + } + } elseif ($item['type'] === 'customlayout' && $adminUser->isAllowed('classes')) { + if ($customLayout = DataObject\ClassDefinition\CustomLayout::getById($item['name'])) { + $classId = $customLayout->getClassId(); + $class = DataObject\ClassDefinition::getById($classId); + $customLayoutJson = json_decode(DataObject\ClassDefinition\Service::generateCustomLayoutJson($customLayout)); + $customLayoutJson->name = $customLayout->getName(); + $customLayoutJson->className = $class->getName(); + $result['customlayout'][] = $customLayoutJson; + } + } + } + + return new DoBulkExportResult(json: json_encode($result, JSON_PRETTY_PRINT)); + } +} diff --git a/src/Handler/DataObject/ClassDef/DoBulkExportResult.php b/src/Handler/DataObject/ClassDef/DoBulkExportResult.php new file mode 100644 index 00000000..1e12cac2 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/DoBulkExportResult.php @@ -0,0 +1,25 @@ +getName(), + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/ExportClassResult.php b/src/Handler/DataObject/ClassDef/ExportClassResult.php new file mode 100644 index 00000000..e76693b7 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/ExportClassResult.php @@ -0,0 +1,26 @@ + $assetType]; + } + + return new GetAssetTypesResult(types: $typeItems); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetAssetTypesResult.php b/src/Handler/DataObject/ClassDef/GetAssetTypesResult.php new file mode 100644 index 00000000..9ece2065 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetAssetTypesResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $result = []; + + if ($adminUser->isAllowed('fieldcollections')) { + $fieldCollections = new DataObject\Fieldcollection\Definition\Listing(); + $fieldCollections = $fieldCollections->load(); + + foreach ($fieldCollections as $fieldCollection) { + $result[] = [ + 'icon' => 'fieldcollection', + 'checked' => true, + 'type' => 'fieldcollection', + 'name' => $fieldCollection->getKey(), + 'displayName' => $fieldCollection->getKey(), + ]; + } + } + + if ($adminUser->isAllowed('classes')) { + $classes = new DataObject\ClassDefinition\Listing(); + $classes->setOrder('ASC'); + $classes->setOrderKey('id'); + $classes = $classes->load(); + + foreach ($classes as $class) { + $result[] = [ + 'icon' => 'class', + 'checked' => true, + 'type' => 'class', + 'name' => $class->getName(), + 'displayName' => $class->getName(), + ]; + } + } + + if ($adminUser->isAllowed('objectbricks')) { + $objectBricks = new DataObject\Objectbrick\Definition\Listing(); + $objectBricks = $objectBricks->loadNames(); + + foreach ($objectBricks as $brickName) { + $result[] = [ + 'icon' => 'objectbricks', + 'checked' => true, + 'type' => 'objectbrick', + 'name' => $brickName, + 'displayName' => $brickName, + ]; + } + } + + if ($adminUser->isAllowed('classes')) { + $customLayouts = new DataObject\ClassDefinition\CustomLayout\Listing(); + $customLayouts = $customLayouts->load(); + foreach ($customLayouts as $customLayout) { + $class = DataObject\ClassDefinition::getById($customLayout->getClassId()); + $displayName = $class->getName() . ' / ' . $customLayout->getName(); + + $result[] = [ + 'icon' => 'custom_views', + 'checked' => true, + 'type' => 'customlayout', + 'name' => $customLayout->getId(), + 'displayName' => $displayName, + ]; + } + } + + return new GetClassBulkExportListResult(data: $result); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassBulkExportListResult.php b/src/Handler/DataObject/ClassDef/GetClassBulkExportListResult.php new file mode 100644 index 00000000..e3818b45 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassBulkExportListResult.php @@ -0,0 +1,25 @@ +setFieldDefinitions([]); + + $result = []; + + DataObject\Service::enrichLayoutDefinition($layoutDefinitions); + + $result['objectColumns']['children'] = $layoutDefinitions->getChildren(); + $result['objectColumns']['nodeLabel'] = 'object_columns'; + $result['objectColumns']['nodeType'] = 'object'; + + $systemColumnNames = DataObject\Concrete::SYSTEM_COLUMN_NAMES; + $systemColumns = []; + foreach ($systemColumnNames as $systemColumn) { + $systemColumns[] = ['title' => $systemColumn, 'name' => $systemColumn, 'datatype' => 'data', 'fieldtype' => 'system']; + } + $result['systemColumns']['nodeLabel'] = 'system_columns'; + $result['systemColumns']['nodeType'] = 'system'; + $result['systemColumns']['children'] = $systemColumns; + + $list = new DataObject\Objectbrick\Definition\Listing(); + $list = $list->load(); + + foreach ($list as $brickDefinition) { + $classDefs = $brickDefinition->getClassDefinitions(); + if (!empty($classDefs)) { + foreach ($classDefs as $classDef) { + if ($classDef['classname'] == $class->getName()) { + $fieldName = $classDef['fieldname']; + if (isset($filteredFieldDefinition[$fieldName]) && !$filteredFieldDefinition[$fieldName]) { + continue; + } + + $key = $brickDefinition->getKey(); + + $brickLayoutDefinitions = $brickDefinition->getLayoutDefinitions(); + $context = [ + 'containerType' => 'objectbrick', + 'containerKey' => $key, + 'outerFieldname' => $fieldName, + ]; + DataObject\Service::enrichLayoutDefinition($brickLayoutDefinitions, null, $context); + + $result[$key]['nodeLabel'] = $key; + $result[$key]['brickField'] = $fieldName; + $result[$key]['nodeType'] = 'objectbricks'; + $result[$key]['children'] = $brickLayoutDefinitions->getChildren(); + + break; + } + } + } + } + + return new GetClassDefinitionForColumnConfigResult(config: $result); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigResult.php b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigResult.php new file mode 100644 index 00000000..94af95fd --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigResult.php @@ -0,0 +1,25 @@ +setFieldDefinitions([]); + $isWriteable = $class->isWritable(); + $classData = $class->getObjectVars(); + $classData['isWriteable'] = $isWriteable; + + return new GetClassResult(classData: $classData); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php b/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php new file mode 100644 index 00000000..03146b97 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php @@ -0,0 +1,113 @@ + FileSystemHelper::scanDirectory($iconDir . '/flat-color-icons/'), + 'white' => FileSystemHelper::scanDirectory($iconDir . '/flat-white-icons/'), + 'twemoji-1', 'twemoji-2', 'twemoji-3', + 'twemoji_variants-1', 'twemoji_variants-2', 'twemoji_variants-3' + => FileSystemHelper::scanDirectory($iconDir . '/twemoji/'), + default => [], + }; + } + + $style = $type === 'white' ? 'background-color:#000' : ''; + + foreach ($icons as &$icon) { + $icon = str_replace(OPENDXP_WEB_ROOT, '', $icon); + } + + $event = new GenericEvent(null, ['icons' => $icons, 'classId' => $classId]); + $this->eventDispatcher->dispatch($event, AdminEvents::CLASS_OBJECT_ICONS_PRE_SEND_DATA); + $icons = $event->getArgument('icons'); + + $startIndex = 0; + + if ($type !== null && str_starts_with($type, 'twemoji')) { + foreach ($icons as $index => $twemojiIcon) { + $iconBase = basename($twemojiIcon); + $explodeByHyphen = explode('-', $iconBase); + + if ( + (!str_starts_with($type, 'twemoji_variants') && isset($explodeByHyphen[1])) || + (str_starts_with($type, 'twemoji_variants') && !isset($explodeByHyphen[1])) + ) { + unset($icons[$index]); + } + } + + $icons = array_values($icons); + $limit = count($icons); + + if (str_ends_with($type, '-1')) { + $limit = (int) floor($limit / 3); + } + if (str_ends_with($type, '-2')) { + $startIndex = (int) floor($limit / 3); + $limit = (int) floor($limit / 3 * 2); + } + if (str_ends_with($type, '-3')) { + $startIndex = (int) floor($limit / 3 * 2); + } + } else { + $limit = count($icons); + } + + $result = []; + for ($i = $startIndex; $i < $limit; $i++) { + $icon = $icons[$i]; + $content = file_get_contents(OPENDXP_WEB_ROOT . $icon); + $result[] = [ + 'text' => sprintf( + '', + $style, + mime_content_type(OPENDXP_WEB_ROOT . $icon), + base64_encode($content) + ), + 'value' => $icon, + ]; + } + + return new GetClassIconsResult(icons: $result); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassIconsResult.php b/src/Handler/DataObject/ClassDef/GetClassIconsResult.php new file mode 100644 index 00000000..bec40b50 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassIconsResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $classesList = new DataObject\ClassDefinition\Listing(); + $classesList->setOrderKey('name'); + $classesList->setOrder('asc'); + $classes = $classesList->load(); + + if ($createAllowed) { + $classes = array_filter($classes, fn ($class) => $adminUser->isAllowed($class->getId(), 'class')); + $classes = array_values($classes); + } + + $getClassConfig = static function ($class) use ($withId, $useTitle): array { + $text = $class->getName(); + if ($useTitle) { + $text = $class->getTitle() ?: $class->getName(); + } + if ($withId) { + $text .= ' (' . $class->getId() . ')'; + } + + $hasBrickField = false; + foreach ($class->getFieldDefinitions() as $fieldDefinition) { + if ($fieldDefinition instanceof DataObject\ClassDefinition\Data\Objectbricks) { + $hasBrickField = true; + break; + } + } + + return [ + 'id' => $class->getId(), + 'text' => $text, + 'leaf' => true, + 'icon' => $class->getIcon() ? htmlspecialchars($class->getIcon()) : self::DEFAULT_ICON, + 'cls' => 'opendxp_class_icon', + 'propertyVisibility' => $class->getPropertyVisibility(), + 'enableGridLocking' => $class->isEnableGridLocking(), + 'hasBrickField' => $hasBrickField, + ]; + }; + + $groups = []; + foreach ($classes as $class) { + $groupName = null; + + if ($class->getGroup()) { + $type = 'manual'; + $groupName = $class->getGroup(); + } else { + $type = 'auto'; + if (preg_match('@^([A-Za-z])([^A-Z]+)@', $class->getName(), $matches)) { + $groupName = $matches[0]; + } + if (!$groupName) { + $groupName = $class->getName(); + } + } + + $groupName = Translation::getByKeyLocalized($groupName, Translation::DOMAIN_ADMIN, true, true); + + if (!isset($groups[$groupName])) { + $groups[$groupName] = [ + 'classes' => [], + 'type' => $type, + ]; + } + $groups[$groupName]['classes'][] = $class; + } + + $treeNodes = []; + if ($groups !== []) { + $types = array_column($groups, 'type'); + array_multisort($types, SORT_ASC, array_keys($groups), SORT_ASC, $groups); + } + + if (!$grouped) { + foreach ($groups as $groupName => $groupData) { + foreach ($groupData['classes'] as $class) { + $node = $getClassConfig($class); + if (count($groupData['classes']) > 1 || $groupData['type'] === 'manual') { + $node['group'] = $groupName; + } + $treeNodes[] = $node; + } + } + } else { + foreach ($groups as $groupName => $groupData) { + if (count($groupData['classes']) === 1 && $groupData['type'] === 'auto') { + $node = $getClassConfig($groupData['classes'][0]); + } else { + $node = [ + 'id' => 'folder_' . $groupName, + 'text' => $groupName, + 'leaf' => false, + 'expandable' => true, + 'allowChildren' => true, + 'iconCls' => 'opendxp_icon_folder', + 'children' => [], + ]; + + foreach ($groupData['classes'] as $class) { + $node['children'][] = $getClassConfig($class); + } + } + + $treeNodes[] = $node; + } + } + + return new GetClassTreeResult(nodes: $treeNodes); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassTreeResult.php b/src/Handler/DataObject/ClassDef/GetClassTreeResult.php new file mode 100644 index 00000000..c59ef585 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassTreeResult.php @@ -0,0 +1,25 @@ + $documentType]; + } + + return new GetDocumentTypesResult(types: $typeItems); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetDocumentTypesResult.php b/src/Handler/DataObject/ClassDef/GetDocumentTypesResult.php new file mode 100644 index 00000000..32864f08 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetDocumentTypesResult.php @@ -0,0 +1,25 @@ +getObjectVars(); + $data['isWriteable'] = $selectOptions->isWriteable(); + $data['enumName'] = $selectOptions->getEnumName(true); + + return new GetSelectOptionsResult(data: $data); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsResult.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsResult.php new file mode 100644 index 00000000..c0cc8820 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsResult.php @@ -0,0 +1,25 @@ +getId(); + $configurationData = [ + 'id' => $id, + 'text' => $id, + 'leaf' => true, + 'iconCls' => 'opendxp_icon_select', + ]; + + if ($grouped === 0 || !$selectOptionConfig->hasGroup()) { + $configurations[] = $configurationData; + + continue; + } + + $group = $selectOptionConfig->getGroup(); + if (!isset($groups[$group])) { + $groups[$group] = [ + 'id' => 'group_' . $id, + 'text' => htmlspecialchars($group ?? ''), + 'expandable' => true, + 'leaf' => false, + 'allowChildren' => true, + 'iconCls' => 'opendxp_icon_folder', + 'group' => $group, + 'children' => [], + ]; + } + $groups[$group]['children'][] = $configurationData; + } + + foreach ($groups as $group) { + $configurations[] = $group; + } + + $event = new GenericEvent(null, ['list' => $configurations]); + $this->eventDispatcher->dispatch($event, AdminEvents::CLASS_SELECTOPTIONS_LIST_PRE_SEND_DATA); + $configurations = $event->getArgument('list'); + + return new GetSelectOptionsTreeResult(configurations: $configurations); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeResult.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeResult.php new file mode 100644 index 00000000..95dc12f8 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeResult.php @@ -0,0 +1,25 @@ +getFieldsUsedIn() as $className => $fieldNames) { + foreach ($fieldNames as $fieldName) { + $usages[] = [ + 'class' => $className, + 'field' => $fieldName, + ]; + } + } + + return new GetSelectOptionsUsagesResult(usages: $usages); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesResult.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesResult.php new file mode 100644 index 00000000..78718856 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesResult.php @@ -0,0 +1,25 @@ +setName('textLayoutPreview' . $className); + + $context = [ + 'data' => $renderingData, + ]; + + if ($renderingClass) { + $textLayout->setRenderingClass($renderingClass); + $textLayout->setRenderingData($renderingData); + } + + if ($html) { + $textLayout->setHtml($html); + } + + $renderedHtml = $textLayout->enrichLayoutDefinition($obj, $context)->getHtml(); + + $content = + "\n" . + "\n" . + '\n" . + "\n\n" . + "\n" . + $renderedHtml . + "\n\n\n" . + "\n"; + + return new GetTextLayoutPreviewResult(content: $content); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewResult.php b/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewResult.php new file mode 100644 index 00000000..f165ef9f --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewResult.php @@ -0,0 +1,25 @@ +getSupportedTypes() as $type) { + $res[] = [ + 'key' => $type, + 'value' => $this->translator->trans($type, [], 'admin'), + ]; + } + + return new GetVideoAllowedTypesResult(types: $res); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetVideoAllowedTypesResult.php b/src/Handler/DataObject/ClassDef/GetVideoAllowedTypesResult.php new file mode 100644 index 00000000..a7493cc9 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetVideoAllowedTypesResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser()?->getId() ?? 0; + $class = DataObject\ClassDefinition::getById($id); + if (!$class) { + throw new NotFoundHttpException('Class not found'); + } + + if ($class->getModificationDate() != $values['modificationDate']) { + throw new BadRequestHttpException('The class was modified during editing, please reload the class and make your changes again'); + } + + if ($values['name'] != $class->getName()) { + $classByName = DataObject\ClassDefinition::getByName($values['name']); + if ($classByName && $classByName->getId() !== $class->getId()) { + throw new BadRequestHttpException('Class name already exists'); + } + + $values['name'] = $this->correctClassname($values['name']); + $class->rename($values['name']); + } + + if ($values['compositeIndices']) { + foreach ($values['compositeIndices'] as $index => $compositeIndex) { + if ($compositeIndex['index_key'] !== ($sanitizedKey = preg_replace('/[^a-za-z0-9_\-+]/', '', $compositeIndex['index_key']))) { + $values['compositeIndices'][$index]['index_key'] = $sanitizedKey; + } + } + } + + unset($values['creationDate'], $values['userOwner'], $values['layoutDefinitions'], $values['fieldDefinitions']); + + $configuration['datatype'] = 'layout'; + $configuration['fieldtype'] = 'panel'; + $configuration['name'] = 'opendxp_root'; + + $class->setValues($values); + + $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($configuration, true); + $class->setLayoutDefinitions($layout); + $class->setUserModification($userId); + $class->setModificationDate(time()); + + $propertyVisibility = []; + foreach ($values as $key => $value) { + if (false !== stripos($key, 'propertyVisibility')) { + if (preg_match("/\.grid\./i", $key)) { + $propertyVisibility['grid'][preg_replace("/propertyVisibility\.grid\./i", '', $key)] = (bool) $value; + } elseif (preg_match("/\.search\./i", $key)) { + $propertyVisibility['search'][preg_replace("/propertyVisibility\.search\./i", '', $key)] = (bool) $value; + } + } + } + if (!empty($propertyVisibility)) { + $class->setPropertyVisibility($propertyVisibility); + } + + $class->save(); + + $class->setFieldDefinitions([]); + + return new SaveClassDefinitionResult(class: $class); + } + + private function correctClassname(string $name): string + { + $name = preg_replace('/[^a-zA-Z0-9_]+/', '', $name); + + return preg_replace('/^\d+/', '', $name); + } +} diff --git a/src/Handler/DataObject/ClassDef/SaveClassDefinitionResult.php b/src/Handler/DataObject/ClassDef/SaveClassDefinitionResult.php new file mode 100644 index 00000000..b7e460f3 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/SaveClassDefinitionResult.php @@ -0,0 +1,27 @@ +hasConfig($id)) { + throw new BadRequestHttpException('Select options with the same ID already exists (lower/upper cases may be different)'); + } + + $selectOptionsConfiguration = DataObject\SelectOptions\Config::createFromData([ + DataObject\SelectOptions\Config::PROPERTY_ID => $id, + DataObject\SelectOptions\Config::PROPERTY_GROUP => $group, + DataObject\SelectOptions\Config::PROPERTY_USE_TRAITS => $useTraits, + DataObject\SelectOptions\Config::PROPERTY_IMPLEMENTS_INTERFACES => $implementsInterfaces, + DataObject\SelectOptions\Config::PROPERTY_SELECT_OPTIONS => $selectOptionsData, + ]); + + $event = new GenericEvent(null, ['selectOptionsConfiguration' => $selectOptionsConfiguration]); + $this->eventDispatcher->dispatch($event, AdminEvents::CLASS_SELECTOPTIONS_UPDATE_CONFIGURATION); + /** @var DataObject\SelectOptions\Config $selectOptionsConfiguration */ + $selectOptionsConfiguration = $event->getArgument('selectOptionsConfiguration'); + + $selectOptionsConfiguration->save(); + + return new SaveSelectOptionsResult(id: $selectOptionsConfiguration->getId()); + } +} diff --git a/src/Handler/DataObject/ClassDef/SaveSelectOptionsResult.php b/src/Handler/DataObject/ClassDef/SaveSelectOptionsResult.php new file mode 100644 index 00000000..e85091e2 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/SaveSelectOptionsResult.php @@ -0,0 +1,25 @@ +fetchOne('SELECT MAX(CAST(id AS SIGNED)) FROM classes'); + $existingIds = $db->fetchFirstColumn('SELECT LOWER(id) FROM classes'); + + return new SuggestClassIdentifierResult( + suggestedIdentifier: $maxId ? $maxId + 1 : 1, + existingIds: $existingIds, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/SuggestClassIdentifierResult.php b/src/Handler/DataObject/ClassDef/SuggestClassIdentifierResult.php new file mode 100644 index 00000000..0295c5e7 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/SuggestClassIdentifierResult.php @@ -0,0 +1,26 @@ +fetchAllAssociative( + 'SELECT * FROM classificationstore_groups g, classificationstore_collectionrelations c + WHERE colId IN (?) AND g.id = c.groupId', + [array_values(array_filter($ids, is_numeric(...)))], + [ArrayParameterType::INTEGER] + ); + + foreach ($groupsData as $groupData) { + $mappedData[$groupData['id']] = $groupData; + } + + $groupIdList = []; + $groupId = null; + $allowedGroupIds = null; + + $object = $oid ? DataObject\Concrete::getById($oid) : null; + if ($object) { + $class = $object->getClass(); + /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ + $fd = $class->getFieldDefinition($fieldname); + $allowedGroupIds = $fd->getAllowedGroupIds(); + } + + foreach ($groupsData as $groupItem) { + $groupId = $groupItem['groupId']; + if (!$allowedGroupIds || in_array($groupId, $allowedGroupIds)) { + $groupIdList[] = $groupId; + } + } + + if ($groupIdList) { + $groupList = new Classificationstore\GroupConfig\Listing(); + $groupCondition = 'id in (' . implode(',', $groupIdList) . ')'; + $groupList->setCondition($groupCondition); + $groupList = $groupList->load(); + + $keyCondition = 'groupId in (' . implode(',', $groupIdList) . ')'; + $keyList = new Classificationstore\KeyGroupRelation\Listing(); + $keyList->setCondition($keyCondition); + $keyList->setOrderKey(['sorter', 'id']); + $keyList->setOrder(['ASC', 'ASC']); + $keyList = $keyList->load(); + + foreach ($groupList as $groupData) { + $data[$groupData->getId()] = [ + 'name' => $groupData->getName(), + 'id' => $groupData->getId(), + 'description' => $groupData->getDescription(), + 'keys' => [], + 'sorter' => (int) $mappedData[$groupData->getId()]['sorter'], + 'collectionId' => $mappedData[$groupId]['colId'], + ]; + } + + foreach ($keyList as $keyData) { + $groupId = $keyData->getGroupId(); + + $keys = $data[$groupId]['keys']; + $type = $keyData->getType(); + $definition = json_decode($keyData->getDefinition(), true); + $definition = Classificationstore\Service::getFieldDefinitionFromJson($definition, $type); + + if (method_exists($definition, '__wakeup')) { + $definition->__wakeup(); + } + + $context['object'] = $object; + $context['class'] = $object ? $object->getClass() : null; + $context['ownerType'] = 'classificationstore'; + $context['ownerName'] = $fieldname; + $context['keyId'] = $keyData->getKeyId(); + $context['groupId'] = $groupId; + $context['keyDefinition'] = $definition; + + if ($definition instanceof LayoutDefinitionEnrichmentInterface) { + $definition = $definition->enrichLayoutDefinition($object, $context); + } + + $keys[] = [ + 'name' => $keyData->getName(), + 'id' => $keyData->getKeyId(), + 'description' => $keyData->getDescription(), + 'definition' => $definition, + ]; + $data[$groupId]['keys'] = $keys; + } + } + + return new AddCollectionsResult(data: $data); + } +} diff --git a/src/Handler/DataObject/Classificationstore/AddCollectionsResult.php b/src/Handler/DataObject/Classificationstore/AddCollectionsResult.php new file mode 100644 index 00000000..03fa1511 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/AddCollectionsResult.php @@ -0,0 +1,25 @@ +setCondition('groupId in (' . $placeholders . ')', $ids); + $keyList->setOrderKey(['sorter', 'id']); + $keyList->setOrder(['ASC', 'ASC']); + $keyList = $keyList->load(); + + $groupList = new Classificationstore\GroupConfig\Listing(); + $groupList->setCondition('id in (' . $placeholders . ')', $ids); + $groupList->setOrder('ASC'); + $groupList->setOrderKey('id'); + $groupList = $groupList->load(); + + $data = []; + + foreach ($groupList as $groupData) { + $data[$groupData->getId()] = [ + 'name' => $groupData->getName(), + 'id' => $groupData->getId(), + 'description' => $groupData->getDescription(), + 'keys' => [], + ]; + } + + foreach ($keyList as $keyData) { + $groupId = $keyData->getGroupId(); + + $keys = $data[$groupId]['keys']; + $type = $keyData->getType(); + $definition = json_decode($keyData->getDefinition(), true); + $definition = Classificationstore\Service::getFieldDefinitionFromJson($definition, $type); + + if (method_exists($definition, '__wakeup')) { + $definition->__wakeup(); + } + + $context['object'] = $object; + $context['class'] = $object ? $object->getClass() : null; + $context['ownerType'] = 'classificationstore'; + $context['ownerName'] = $fieldname; + $context['keyId'] = $keyData->getKeyId(); + $context['groupId'] = $groupId; + $context['keyDefinition'] = $definition; + + if ($definition instanceof LayoutDefinitionEnrichmentInterface) { + $definition = $definition->enrichLayoutDefinition($object, $context); + } + + $keys[] = [ + 'name' => $keyData->getName(), + 'id' => $keyData->getKeyId(), + 'description' => $keyData->getDescription(), + 'definition' => $definition, + ]; + $data[$groupId]['keys'] = $keys; + } + + return new AddGroupsResult(data: $data); + } +} diff --git a/src/Handler/DataObject/Classificationstore/AddGroupsResult.php b/src/Handler/DataObject/Classificationstore/AddGroupsResult.php new file mode 100644 index 00000000..0cea0fa2 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/AddGroupsResult.php @@ -0,0 +1,25 @@ + 'input', + 'name' => $name, + 'title' => $name, + 'datatype' => 'data', + ]; + + $config = new Classificationstore\KeyConfig(); + $config->setName($name); + $config->setTitle($name); + $config->setType('input'); + $config->setStoreId($storeId); + $config->setEnabled(true); + $config->setDefinition(json_encode($definition)); + $config->save(); + + return new AddPropertyResult(name: $config->getName()); + } +} diff --git a/src/Handler/DataObject/Classificationstore/AddPropertyResult.php b/src/Handler/DataObject/Classificationstore/AddPropertyResult.php new file mode 100644 index 00000000..1f960b9b --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/AddPropertyResult.php @@ -0,0 +1,25 @@ +setName($name); + $config->setStoreId($storeId); + $config->save(); + } + + return new CreateCollectionResult(name: $config->getName()); + } +} diff --git a/src/Handler/DataObject/Classificationstore/CreateCollectionResult.php b/src/Handler/DataObject/Classificationstore/CreateCollectionResult.php new file mode 100644 index 00000000..bdc54693 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/CreateCollectionResult.php @@ -0,0 +1,25 @@ +setStoreId($storeId); + $config->setName($name); + $config->save(); + + return new CreateGroupResult(name: $config->getName(), alreadyExists: false); + } + + return new CreateGroupResult(name: $config->getName(), alreadyExists: true); + } +} diff --git a/src/Handler/DataObject/Classificationstore/CreateGroupResult.php b/src/Handler/DataObject/Classificationstore/CreateGroupResult.php new file mode 100644 index 00000000..a51ef495 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/CreateGroupResult.php @@ -0,0 +1,26 @@ +setName($name); + $config->save(); + } else { + throw new Exception('Store with the given name exists'); + } + + return new CreateStoreResult(storeId: $config->getId()); + } +} diff --git a/src/Handler/DataObject/Classificationstore/CreateStoreResult.php b/src/Handler/DataObject/Classificationstore/CreateStoreResult.php new file mode 100644 index 00000000..87dc2458 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/CreateStoreResult.php @@ -0,0 +1,25 @@ +setCondition('colId = ?', $id); + $list = $configRelations->load(); + foreach ($list as $item) { + $item->delete(); + } + + $config = Classificationstore\CollectionConfig::getById($id); + $config->delete(); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php new file mode 100644 index 00000000..c9a7cc3e --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php @@ -0,0 +1,31 @@ +setColId($colId); + $config->setGroupId($groupId); + $config->delete(); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php b/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php new file mode 100644 index 00000000..9db7464c --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php @@ -0,0 +1,29 @@ +delete(); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php b/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php new file mode 100644 index 00000000..17383b15 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php @@ -0,0 +1,30 @@ +setEnabled(false); + $config->save(); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php b/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php new file mode 100644 index 00000000..3af537f1 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php @@ -0,0 +1,31 @@ +setKeyId($keyId); + $config->setGroupId($groupId); + $config->delete(); + } +} diff --git a/src/Handler/DataObject/Classificationstore/EditStoreHandler.php b/src/Handler/DataObject/Classificationstore/EditStoreHandler.php new file mode 100644 index 00000000..4df70d02 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/EditStoreHandler.php @@ -0,0 +1,49 @@ +getId() != $id) { + throw new Exception('There is already a config with the same name'); + } + + $config = Classificationstore\StoreConfig::getById($id); + + if (!$config) { + throw new Exception('Configuration does not exist'); + } + + $config->setName($name); + $config->setDescription($description); + $config->save(); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php b/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php new file mode 100644 index 00000000..bf73ce7d --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php @@ -0,0 +1,110 @@ + 'name', 'groupDescription' => 'description']; + + $orderKey = 'sorter'; + $order = 'ASC'; + + if ($dir !== null) { + $order = $dir; + } + + $sortingSettings = QueryParams::extractSortingSettings($queryAll); + if ($sortingSettings['orderKey'] && $sortingSettings['order']) { + $orderKey = $sortingSettings['orderKey']; + $order = $sortingSettings['order']; + } + + if ($overrideSort) { + $orderKey = 'id'; + $order = 'DESC'; + } + + $list = new Classificationstore\CollectionGroupRelation\Listing(); + + if ($limit > 0) { + $list->setLimit($limit); + } + $list->setOffset($start); + $list->setOrder($order); + $list->setOrderKey($mapping[$orderKey] ?? $orderKey); + $condition = ''; + + if ($filter !== null) { + $db = Db::get(); + $filters = json_decode($filter); + + $count = 0; + /** @var stdClass $f */ + foreach ($filters as $f) { + if (!isset($f->value)) { + continue; + } + + if ($count > 0) { + $condition .= ' AND '; + } + $count++; + $fieldname = $mapping[$f->field]; + $condition .= $db->quoteIdentifier($fieldname) . ' LIKE ' . $db->quote('%' . $f->value . '%'); + } + } + + if ($condition) { + $condition = '( ' . $condition . ' ) AND'; + } + $condition .= ' colId = ' . $list->quote($colId); + + $list->setCondition($condition); + + $listItems = $list->load(); + + $data = []; + foreach ($listItems as $config) { + $item = [ + 'colId' => $config->getColId(), + 'groupId' => $config->getGroupId(), + 'groupName' => $config->getName(), + 'groupDescription' => $config->getDescription(), + 'id' => $config->getColId() . '-' . $config->getGroupId(), + 'sorter' => $config->getSorter(), + ]; + $data[] = $item; + } + + return new GetCollectionRelationsResult(data: $data, total: $list->getTotalCount()); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsResult.php b/src/Handler/DataObject/Classificationstore/GetCollectionRelationsResult.php new file mode 100644 index 00000000..c0658e1b --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetCollectionRelationsResult.php @@ -0,0 +1,26 @@ +getClass(); + /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ + $fd = $class->getFieldDefinition($fieldname); + $allowedGroupIds = $fd->getAllowedGroupIds(); + + if ($allowedGroupIds) { + $db = Db::get(); + $relationList = $db->fetchAllAssociative( + 'SELECT * FROM classificationstore_collectionrelations WHERE groupId IN (?)', + [$allowedGroupIds], + [ArrayParameterType::INTEGER] + ); + + foreach ($relationList as $item) { + $allowedCollectionIds[] = $item['colId']; + } + } + + $storeIdFromDefinition = $fd->getStoreId(); + } + + $list = new Classificationstore\CollectionConfig\Listing(); + + $list->setLimit($limit); + $list->setOffset($start); + $list->setOrder($order); + $list->setOrderKey($orderKey); + + $conditionParts = []; + $db = Db::get(); + + if ($searchfilter) { + $searchFilterConditions = []; + + $searchTerms = [$searchfilter, ...$this->searchTermResolver->resolve($searchfilter)]; + foreach ($searchTerms as $searchFilterTerm) { + $searchFilterConditions[] = 'name LIKE ' . $db->quote('%' . $searchFilterTerm . '%') . ' OR description LIKE ' . $db->quote('%' . $searchFilterTerm . '%'); + } + + $conditionParts[] = '(' . implode(' OR ', $searchFilterConditions) . ')'; + } + + $storeId = $storeId ?: $storeIdFromDefinition; + + $conditionParts[] = ' (storeId = ' . $db->quote($storeId) . ')'; + + if ($filter !== null) { + $filters = json_decode($filter); + /** @var stdClass $f */ + foreach ($filters as $f) { + if (!isset($f->value)) { + continue; + } + + $conditionParts[] = $db->quoteIdentifier($f->property) . ' LIKE ' . $db->quote('%' . $f->value . '%'); + } + } + + if ($allowedCollectionIds) { + $conditionParts[] = ' id in (' . implode(',', $allowedCollectionIds) . ')'; + } + + $condition = implode(' AND ', $conditionParts); + + $list->setCondition($condition); + + $list->load(); + $configList = $list->getList(); + + $data = []; + foreach ($configList as $config) { + $name = $config->getName(); + if (!$name) { + $name = 'EMPTY'; + } + $item = [ + 'storeId' => $config->getStoreId(), + 'id' => $config->getId(), + 'name' => $name, + 'description' => $config->getDescription(), + ]; + if ($config->getCreationDate()) { + $item['creationDate'] = $config->getCreationDate(); + } + + if ($config->getModificationDate()) { + $item['modificationDate'] = $config->getModificationDate(); + } + + $data[] = $item; + } + + return new GetCollectionsResult(data: $data, total: $list->getTotalCount()); + } + +} diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionsResult.php b/src/Handler/DataObject/Classificationstore/GetCollectionsResult.php new file mode 100644 index 00000000..00df9b10 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetCollectionsResult.php @@ -0,0 +1,26 @@ +setLimit($limit); + $list->setOffset($start); + $list->setOrder($order); + $list->setOrderKey($orderKey); + + $conditionParts = []; + $db = Db::get(); + + if ($searchfilter !== null) { + $searchFilterConditions = []; + + $searchTerms = [$searchfilter, ...$this->searchTermResolver->resolve($searchfilter)]; + foreach ($searchTerms as $searchFilterTerm) { + $searchFilterConditions[] = 'name LIKE ' . $db->quote('%' . $searchFilterTerm . '%') . ' OR description LIKE ' . $db->quote('%' . $searchFilterTerm . '%'); + } + + $conditionParts[] = '(' . implode(' OR ', $searchFilterConditions) . ')'; + } + + if ($storeId) { + $conditionParts[] = '(storeId = ' . $db->quote($storeId) . ')'; + } + + if ($filter !== null) { + $filters = json_decode($filter); + /** @var stdClass $f */ + foreach ($filters as $f) { + if (!isset($f->value)) { + continue; + } + + $conditionParts[] = $db->quoteIdentifier($f->property) . ' LIKE ' . $db->quote('%' . $f->value . '%'); + } + } + + if ($oid !== null) { + $object = DataObject\Concrete::getById($oid); + $class = $object->getClass(); + /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ + $fd = $class->getFieldDefinition($fieldname); + $allowedGroupIds = $fd->getAllowedGroupIds(); + + if ($allowedGroupIds) { + $conditionParts[] = 'ID in (' . implode(',', $allowedGroupIds) . ')'; + } + } + + $condition = implode(' AND ', $conditionParts); + $list->setCondition($condition); + + $list->load(); + $configList = $list->getList(); + + $data = []; + foreach ($configList as $config) { + $name = $config->getName(); + if (!$name) { + $name = 'EMPTY'; + } + $item = [ + 'storeId' => $config->getStoreId(), + 'id' => $config->getId(), + 'name' => $name, + 'description' => $config->getDescription(), + ]; + if ($config->getCreationDate()) { + $item['creationDate'] = $config->getCreationDate(); + } + + if ($config->getModificationDate()) { + $item['modificationDate'] = $config->getModificationDate(); + } + + $data[] = $item; + } + + return new GetGroupsResult(data: $data, total: $list->getTotalCount()); + } + +} diff --git a/src/Handler/DataObject/Classificationstore/GetGroupsResult.php b/src/Handler/DataObject/Classificationstore/GetGroupsResult.php new file mode 100644 index 00000000..99720c49 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetGroupsResult.php @@ -0,0 +1,26 @@ +executeStatement('SET @rownum = 0'); + $result = $db->fetchAllAssociative($query); + + $page = (int) $result[0]['page']; + + return new GetPageResult(page: $page); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetPageResult.php b/src/Handler/DataObject/Classificationstore/GetPageResult.php new file mode 100644 index 00000000..7bc7beec --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetPageResult.php @@ -0,0 +1,25 @@ +getId(); + $groupList = new Classificationstore\CollectionGroupRelation\Listing(); + $groupList->setCondition('colId = ' . $db->quote($frameId)); + $groupList = $groupList->load(); + $groupIdList = []; + foreach ($groupList as $groupEntry) { + $groupIdList[] = $groupEntry->getGroupId(); + } + + if ($groupIdList) { + $keyIdList = new Classificationstore\KeyGroupRelation\Listing(); + $keyIdList->setCondition('groupId in (' . implode(',', $groupIdList) . ')'); + $keyIdList = $keyIdList->load(); + if ($keyIdList) { + $keyIdValues = []; + foreach ($keyIdList as $keyEntry) { + $keyIdValues[] = $keyEntry->getKeyId(); + } + + $keyCriteria = ' id in (' . implode(',', $keyIdValues) . ')'; + } + } + } + + $conditionParts[] = $keyCriteria; + } + + $orderKey = 'name'; + $order = 'ASC'; + + if ($dir !== null) { + $order = $dir; + } + + $sortingSettings = QueryParams::extractSortingSettings($queryAll); + if ($sortingSettings['orderKey'] && $sortingSettings['order']) { + $orderKey = $sortingSettings['orderKey']; + $order = $sortingSettings['order']; + } + + if ($overrideSort) { + $orderKey = 'id'; + $order = 'DESC'; + } + + $list = new Classificationstore\KeyConfig\Listing(); + + if ($limit > 0 && !$groupIds && !$keyIds) { + $list->setLimit($limit); + } + $list->setOffset($start); + $list->setOrder($order); + $list->setOrderKey($orderKey); + + if ($searchfilter) { + $conditionParts[] = '(name LIKE ' . $db->quote('%' . $searchfilter . '%') . ' OR description LIKE ' . $db->quote('%' . $searchfilter . '%') . ')'; + } + + if ($storeId) { + $conditionParts[] = '(storeId = ' . $db->quote($storeId) . ')'; + } + + if ($filter !== null) { + $filters = json_decode($filter); + /** @var stdClass $f */ + foreach ($filters as $f) { + if (!isset($f->value)) { + continue; + } + + $conditionParts[] = $db->quoteIdentifier($f->property) . ' LIKE ' . $db->quote('%' . $f->value . '%'); + } + } + $condition = implode(' AND ', $conditionParts); + $list->setCondition($condition); + + if ($groupIds || $keyIds) { + if ($groupIds) { + $ids = json_decode($groupIds, true); + $col = 'group'; + } else { + $ids = json_decode($keyIds, true); + $col = 'id'; + } + + $condition = $db->quoteIdentifier($col) . ' IN ('; + $count = 0; + foreach ($ids as $theId) { + if ($count > 0) { + $condition .= ','; + } + $condition .= $theId; + $count++; + } + + $condition .= ')'; + $list->setCondition($condition); + } + + $list->load(); + $configList = $list->getList(); + + $data = []; + foreach ($configList as $config) { + $data[] = self::buildKeyConfigItem($config); + } + + return new GetPropertiesResult(data: $data, total: $list->getTotalCount()); + } + + public static function buildKeyConfigItem(Classificationstore\KeyConfig $config): array + { + $name = $config->getName(); + + $item = [ + 'storeId' => $config->getStoreId(), + 'id' => $config->getId(), + 'name' => $name, + 'description' => $config->getDescription(), + ]; + + if ($config->getCreationDate()) { + $item['creationDate'] = $config->getCreationDate(); + } + + if ($config->getModificationDate()) { + $item['modificationDate'] = $config->getModificationDate(); + } + + $item['type'] = $config->getType() ?: 'input'; + $definition = $config->getDefinition(); + $item['definition'] = $definition; + + if ($definition) { + $definition = json_decode($definition, true); + if ($definition) { + $item['title'] = $definition['title']; + } + } + + return $item; + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetPropertiesResult.php b/src/Handler/DataObject/Classificationstore/GetPropertiesResult.php new file mode 100644 index 00000000..82f6f7e2 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetPropertiesResult.php @@ -0,0 +1,26 @@ + 'name', 'keyDescription' => 'description']; + + $orderKey = 'name'; + $order = 'ASC'; + + $relationIdList = $relationIds ? json_decode($relationIds, true) : null; + + if ($dir !== null) { + $order = $dir; + } + + $sortingSettings = QueryParams::extractSortingSettings($queryAll); + + if ($sortingSettings['orderKey'] && $sortingSettings['order']) { + $orderKey = $mapping[$sortingSettings['orderKey']] ?? $sortingSettings['orderKey']; + $order = $sortingSettings['order']; + } + + if ($overrideSort) { + $orderKey = 'id'; + $order = 'DESC'; + } + + if ($limit === 0 && is_array($relationIdList)) { + $limit = count($relationIdList); + } + + $list = new Classificationstore\KeyGroupRelation\Listing(); + + if ($limit > 0) { + $list->setLimit($limit); + } + + $list->setOffset($start); + $list->setOrder($order); + $list->setOrderKey($orderKey); + $conditionParts = []; + + if ($filter !== null) { + $db = Db::get(); + $filters = json_decode($filter); + /** @var stdClass $f */ + foreach ($filters as $f) { + if (!isset($f->value)) { + continue; + } + + $fieldname = $mapping[$f->field]; + $conditionParts[] = $db->quoteIdentifier($fieldname) . ' LIKE ' . $db->quote('%' . $f->value . '%'); + } + } + + if ($relationIdList === null) { + $conditionParts[] = ' groupId = ' . $list->quote($groupId); + } + + if ($relationIdList) { + $relationParts = []; + + foreach ($relationIdList as $relationId) { + $keyId = $relationId['keyId']; + $entryGroupId = $relationId['groupId']; + $relationParts[] = '(keyId = ' . $list->quote($keyId) . ' AND groupId = ' . $list->quote($entryGroupId) . ')'; + } + + $conditionParts[] = '(' . implode(' OR ', $relationParts) . ')'; + } + + $condition = implode(' AND ', $conditionParts); + + $list->setCondition($condition); + + $listItems = $list->load(); + + $data = []; + foreach ($listItems as $config) { + $type = $config->getType(); + $definition = json_decode($config->getDefinition(), true); + $definition = Classificationstore\Service::getFieldDefinitionFromJson($definition, $type); + DataObject\Service::enrichLayoutDefinition($definition); + + $item = [ + 'keyId' => $config->getKeyId(), + 'groupId' => $config->getGroupId(), + 'keyName' => $config->getName(), + 'keyDescription' => $config->getDescription(), + 'id' => $config->getGroupId() . '-' . $config->getKeyId(), + 'sorter' => $config->getSorter(), + 'layout' => $definition, + 'mandatory' => $config->isMandatory(), + ]; + + $data[] = $item; + } + + return new GetRelationsResult(data: $data, total: $list->getTotalCount()); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetRelationsResult.php b/src/Handler/DataObject/Classificationstore/GetRelationsResult.php new file mode 100644 index 00000000..3c7c9b4c --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetRelationsResult.php @@ -0,0 +1,26 @@ +load(); + foreach ($list as $item) { + $resultItem = [ + 'id' => $item->getId(), + 'text' => htmlspecialchars($item->getName() ?? '', ENT_QUOTES), + 'expandable' => false, + 'leaf' => true, + 'expanded' => true, + 'description' => htmlspecialchars($item->getDescription() ?? '', ENT_QUOTES), + 'iconCls' => 'opendxp_icon_classificationstore', + ]; + + $resultItem['qtitle'] = 'ID: ' . $item->getId(); + $resultItem['qtip'] = $item->getDescription() ? htmlspecialchars($item->getDescription(), ENT_QUOTES) : ' '; + $result[] = $resultItem; + } + + return new GetStoreTreeResult(items: $result); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetStoreTreeResult.php b/src/Handler/DataObject/Classificationstore/GetStoreTreeResult.php new file mode 100644 index 00000000..c003960b --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetStoreTreeResult.php @@ -0,0 +1,25 @@ +load(); + + foreach ($storeConfigListing as $storeConfig) { + $storeConfigs[] = $storeConfig->getObjectVars(); + } + + return new ListStoresResult(storeConfigs: $storeConfigs); + } +} diff --git a/src/Handler/DataObject/Classificationstore/ListStoresResult.php b/src/Handler/DataObject/Classificationstore/ListStoresResult.php new file mode 100644 index 00000000..4b0fa72a --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/ListStoresResult.php @@ -0,0 +1,25 @@ +setGroupId($groupId); + $config->setColId($colId); + $config->setSorter((int) $sorter); + + $config->save(); + + $row['id'] = $config->getColId() . '-' . $config->getGroupId(); + } + + return new SaveCollectionRelationsResult(data: $data); + } +} diff --git a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsResult.php b/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsResult.php new file mode 100644 index 00000000..5fede306 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsResult.php @@ -0,0 +1,25 @@ +setGroupId((int) $groupId); + $config->setKeyId((int) $keyId); + $config->setSorter($sorter); + $config->setMandatory($mandatory); + + $config->save(); + $data['id'] = $config->getGroupId() . '-' . $config->getKeyId(); + + return new SaveRelationResult(data: $data); + } +} diff --git a/src/Handler/DataObject/Classificationstore/SaveRelationResult.php b/src/Handler/DataObject/Classificationstore/SaveRelationResult.php new file mode 100644 index 00000000..6f1afbaa --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/SaveRelationResult.php @@ -0,0 +1,25 @@ + Classificationstore\GroupConfig\Dao::TABLE_NAME_GROUPS . '.name', + 'keyName' => Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS . '.name', + 'keyDescription' => Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS . '.description', + ]; + + $orderKey = 'name'; + $order = 'ASC'; + + if ($dir) { + $order = $dir; + } + + $sortingSettings = QueryParams::extractSortingSettings($queryAll); + if ($sortingSettings['orderKey'] && $sortingSettings['order']) { + $orderKey = $sortingSettings['orderKey']; + if ($orderKey === 'keyName') { + $orderKey = 'name'; + } + $order = $sortingSettings['order']; + } + + if ($overrideSort) { + $orderKey = 'id'; + $order = 'DESC'; + } + + $list = new Classificationstore\KeyGroupRelation\Listing(); + + if ($limit > 0) { + $list->setLimit($limit); + } + $list->setOffset($start); + $list->setOrder($order); + $list->setOrderKey($orderKey); + + $conditionParts = []; + + if ($filter !== null) { + $filters = json_decode($filter); + /** @var stdClass $f */ + foreach ($filters as $f) { + if (!isset($f->value)) { + continue; + } + + $fieldname = $mapping[$f->property]; + $conditionParts[] = $fieldname . ' LIKE ' . $db->quote('%' . $f->value . '%'); + } + } + + $conditionParts[] = ' groupId IN (select id from classificationstore_groups where storeId = ' . $db->quote($storeId) . ')'; + + if ($searchfilter) { + $searchFilterConditions = []; + + $searchTerms = [$searchfilter, ...$this->searchTermResolver->resolve($searchfilter)]; + foreach ($searchTerms as $searchFilterTerm) { + $searchFilterConditions[] = Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS . '.name LIKE ' . $db->quote('%' . $searchFilterTerm . '%') + . ' OR ' . Classificationstore\GroupConfig\Dao::TABLE_NAME_GROUPS . '.name LIKE ' . $db->quote('%' . $searchFilterTerm . '%') + . ' OR ' . Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS . '.description LIKE ' . $db->quote('%' . $searchFilterTerm . '%'); + } + + $conditionParts[] = '(' . implode(' OR ', $searchFilterConditions) . ')'; + } + + $condition = implode(' AND ', $conditionParts); + $list->setCondition($condition); + $list->setResolveGroupName(true); + + $data = []; + foreach ($list->getList() as $config) { + $item = [ + 'keyId' => $config->getKeyId(), + 'groupId' => $config->getGroupId(), + 'keyName' => $config->getName(), + 'keyDescription' => $config->getDescription(), + 'id' => $config->getGroupId() . '-' . $config->getKeyId(), + 'sorter' => $config->getSorter(), + ]; + + $groupConfig = Classificationstore\GroupConfig::getById($config->getGroupId()); + if ($groupConfig) { + $item['groupName'] = $groupConfig->getName(); + } + + $data[] = $item; + } + + return new SearchRelationsResult(data: $data, total: $list->getTotalCount()); + } + +} diff --git a/src/Handler/DataObject/Classificationstore/SearchRelationsResult.php b/src/Handler/DataObject/Classificationstore/SearchRelationsResult.php new file mode 100644 index 00000000..cbfb0040 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/SearchRelationsResult.php @@ -0,0 +1,26 @@ + $value) { + if ($key !== 'id') { + $setter = 'set' . $key; + $config->$setter($value); + } + } + + $config->save(); + + $name = $config->getName(); + $item = [ + 'storeId' => $config->getStoreId(), + 'id' => $config->getId(), + 'name' => $name, + 'description' => $config->getDescription(), + ]; + + if ($config->getCreationDate()) { + $item['creationDate'] = $config->getCreationDate(); + } + + if ($config->getModificationDate()) { + $item['modificationDate'] = $config->getModificationDate(); + } + + return new UpdateCollectionResult(item: $item); + } +} diff --git a/src/Handler/DataObject/Classificationstore/UpdateCollectionResult.php b/src/Handler/DataObject/Classificationstore/UpdateCollectionResult.php new file mode 100644 index 00000000..ca36bc40 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/UpdateCollectionResult.php @@ -0,0 +1,25 @@ + $value) { + if ($key !== 'id') { + $setter = 'set' . $key; + $config->$setter($value); + } + } + + $config->save(); + + $name = $config->getName(); + $item = [ + 'storeId' => $config->getStoreId(), + 'id' => $config->getId(), + 'name' => $name, + 'description' => $config->getDescription(), + ]; + + if ($config->getCreationDate()) { + $item['creationDate'] = $config->getCreationDate(); + } + + if ($config->getModificationDate()) { + $item['modificationDate'] = $config->getModificationDate(); + } + + return new UpdateGroupResult(item: $item); + } +} diff --git a/src/Handler/DataObject/Classificationstore/UpdateGroupResult.php b/src/Handler/DataObject/Classificationstore/UpdateGroupResult.php new file mode 100644 index 00000000..8fead9fc --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/UpdateGroupResult.php @@ -0,0 +1,25 @@ + $value) { + if ($key !== 'id') { + $setter = 'set' . $key; + if (method_exists($config, $setter)) { + $config->$setter($value); + } + } + } + + $config->save(); + $item = GetPropertiesHandler::buildKeyConfigItem($config); + + return new UpdatePropertyResult(item: $item); + } +} diff --git a/src/Handler/DataObject/Classificationstore/UpdatePropertyResult.php b/src/Handler/DataObject/Classificationstore/UpdatePropertyResult.php new file mode 100644 index 00000000..80522a2c --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/UpdatePropertyResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $source = DataObject::getById($sourceId); + + if ($sourceParentId !== null && $targetParentId !== null) { + $sourceParent = DataObject::getById($sourceParentId) ?? throw new NotFoundHttpException('Source parent not found'); + $resolvedTargetParentId = $sessionParentId ?? $targetParentId; + $targetParent = DataObject::getById($resolvedTargetParentId) ?? throw new NotFoundHttpException('Target parent not found'); + $targetPath = preg_replace('@^' . preg_quote($sourceParent->getRealFullPath(), '@') . '@', $targetParent . '/', $source->getRealPath()); + $target = DataObject::getByPath($targetPath); + } else { + $target = DataObject::getById($targetId); + } + + if (!$target instanceof DataObject) { + throw new NotFoundHttpException('Target not found'); + } + + $hasClassPermission = !($source instanceof DataObject\Concrete) || $adminUser->isAllowed($source->getClassId(), 'class'); + if (!$target->isAllowed('create') || !$hasClassPermission) { + throw new AccessDeniedHttpException(); + } + + $source = DataObject::getById($sourceId); + if (!$source instanceof DataObject) { + throw new NotFoundHttpException("Source object not found: $sourceId"); + } + + if ($source instanceof DataObject\Concrete && $latestVersion = $source->getLatestVersion()) { + $source = $latestVersion->loadData(); + $source->setPublished(false); + } + + $objectService = $this->serviceFactory->createDataObjectService(); + + if ($type === 'child') { + $newObject = $objectService->copyAsChild($target, $source); + + return new CopyDataObjectResult($sourceId, $newObject); + } + + if ($type === 'replace') { + $concreteTarget = DataObject\Concrete::getById($target->getId()); + $concreteSource = DataObject\Concrete::getById($source->getId()); + $objectService->copyContents($concreteTarget, $concreteSource); + } + + return new CopyDataObjectResult($sourceId); + } +} diff --git a/src/Handler/DataObject/Copy/CopyDataObjectResult.php b/src/Handler/DataObject/Copy/CopyDataObjectResult.php new file mode 100644 index 00000000..5e5dda88 --- /dev/null +++ b/src/Handler/DataObject/Copy/CopyDataObjectResult.php @@ -0,0 +1,28 @@ +hasChildren(DataObject::$types)) { + return new ChildIdsResult([]); + } + + $list = new DataObject\Listing(); + $list->setCondition('`path` LIKE ' . $list->quote($list->escapeLike($object->getRealFullPath()) . '/%')); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('ASC'); + $list->setObjectTypes(DataObject::$types); + + return new ChildIdsResult($list->loadIdList()); + } +} diff --git a/src/Handler/DataObject/Copy/RewriteDataObjectIdsHandler.php b/src/Handler/DataObject/Copy/RewriteDataObjectIdsHandler.php new file mode 100644 index 00000000..77058752 --- /dev/null +++ b/src/Handler/DataObject/Copy/RewriteDataObjectIdsHandler.php @@ -0,0 +1,39 @@ +userContext->getAdminUser()?->getId() ?? 0; + $object = DataObject::getById($objectId) ?? throw new DataObjectNotFoundException($objectId); + + $object = DataObject\Service::rewriteIds($object, ['object' => $idMapping]); + $object->setUserModification($userId); + $object->save(); + } +} diff --git a/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php new file mode 100644 index 00000000..ef9f4130 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php @@ -0,0 +1,54 @@ +userContext->getAdminUser()?->getId() ?? 0; + if (DataObject\ClassDefinition\CustomLayout::getById($layoutId)) { + throw new BadRequestHttpException('Custom Layout identifier already exists'); + } + + $customLayout = DataObject\ClassDefinition\CustomLayout::create([ + 'name' => $layoutName, + 'userOwner' => $userId, + 'classId' => $classId, + ]); + + $customLayout->setId($layoutId); + + if (!$customLayout->isWriteable()) { + throw new ConfigWriteException(); + } + + $customLayout->save(); + + return $customLayout; + } +} diff --git a/src/Handler/DataObject/CustomLayout/AllLayoutsResult.php b/src/Handler/DataObject/CustomLayout/AllLayoutsResult.php new file mode 100644 index 00000000..5b9215f6 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/AllLayoutsResult.php @@ -0,0 +1,25 @@ +setFilter(function (DataObject\ClassDefinition\CustomLayout $layout) use ($id) { + $currentLayoutId = $layout->getId(); + + return $currentLayoutId === $id || str_starts_with($currentLayoutId, $id . '.brick.'); + }); + + foreach ($customLayouts->getLayoutDefinitions() as $customLayout) { + $customLayout->delete(); + } + } +} diff --git a/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php new file mode 100644 index 00000000..35ecbf4e --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php @@ -0,0 +1,43 @@ +getName(), + DataObject\ClassDefinition\Service::generateCustomLayoutJson($customLayout), + ); + } + } + + $errorMessage = ': Custom Layout with id [ ' . $id . ' not found. ]'; + Logger::error($errorMessage); + + throw new NotFoundHttpException($errorMessage); + } +} diff --git a/src/Handler/DataObject/CustomLayout/ExportCustomLayoutResult.php b/src/Handler/DataObject/CustomLayout/ExportCustomLayoutResult.php new file mode 100644 index 00000000..4b81981c --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/ExportCustomLayoutResult.php @@ -0,0 +1,26 @@ +setFilter(fn (DataObject\ClassDefinition\CustomLayout $layout) => !str_contains($layout->getId(), '.brick.')); + $customLayouts->setOrder(fn (DataObject\ClassDefinition\CustomLayout $a, DataObject\ClassDefinition\CustomLayout $b) => strcmp($a->getName(), $b->getName())); + + foreach ($customLayouts->load() as $layout) { + $mapping[$layout->getClassId()][] = $layout; + } + + $classList = new DataObject\ClassDefinition\Listing(); + $classList->setOrder('ASC'); + $classList->setOrderKey('name'); + + $layouts = []; + foreach ($classList->load() as $class) { + if (!isset($mapping[$class->getId()])) { + continue; + } + $layouts[] = [ + 'type' => 'main', + 'id' => $class->getId() . '_' . 0, + 'name' => $class->getName(), + ]; + foreach ($mapping[$class->getId()] as $layout) { + $layouts[] = [ + 'type' => 'custom', + 'id' => $class->getId() . '_' . $layout->getId(), + 'name' => $class->getName() . ' - ' . $layout->getName(), + ]; + } + } + + return new AllLayoutsResult($layouts); + } +} diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php new file mode 100644 index 00000000..7dae3745 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php @@ -0,0 +1,44 @@ +setFilter( + fn (DataObject\ClassDefinition\CustomLayout $layout) => + in_array($layout->getClassId(), $classIds) && !str_contains($layout->getId(), '.brick.') + ); + + $definitions = []; + foreach ($list->load() as $item) { + $definitions[] = [ + 'id' => $item->getId(), + 'name' => $item->getName(), + 'default' => $item->getDefault(), + ]; + } + + return new CustomLayoutDefinitionsResult($definitions); + } +} diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php new file mode 100644 index 00000000..87c8fbfc --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php @@ -0,0 +1,61 @@ +userContext->getAdminUser()?->getId() ?? 0; + $customLayout = DataObject\ClassDefinition\CustomLayout::getById($id); + + if (!$customLayout) { + $brickLayoutSeparator = strpos($id, '.brick.'); + if ($brickLayoutSeparator !== false) { + $parentLayout = DataObject\ClassDefinition\CustomLayout::getById(substr($id, 0, $brickLayoutSeparator)); + if ($parentLayout instanceof DataObject\ClassDefinition\CustomLayout) { + $customLayout = DataObject\ClassDefinition\CustomLayout::create([ + 'name' => $parentLayout->getName() . ' ' . substr($id, $brickLayoutSeparator + strlen('.brick.')), + 'userOwner' => $userId, + 'classId' => $parentLayout->getClassId(), + ]); + $customLayout->setId($id); + if (!$customLayout->isWriteable()) { + throw new ConfigWriteException(); + } + $customLayout->save(); + } + } + + if (!$customLayout) { + throw new NotFoundHttpException(); + } + } + + return new GetCustomLayoutResult($customLayout->getObjectVars(), $customLayout->isWriteable()); + } +} diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutResult.php b/src/Handler/DataObject/CustomLayout/GetCustomLayoutResult.php new file mode 100644 index 00000000..308470e3 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayoutResult.php @@ -0,0 +1,26 @@ +setLayoutDefinitions($layout); + if (isset($importData['name'])) { + $customLayout->setName($importData['name']); + } + $customLayout->setDescription($importData['description']); + + if (!$customLayout->isWriteable()) { + throw new ConfigWriteException(); + } + + $customLayout->save(); + } +} diff --git a/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php new file mode 100644 index 00000000..fdbd4f2e --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php @@ -0,0 +1,55 @@ +getModificationDate()) { + throw new BadRequestHttpException('custom_layout_changed'); + } + + $configuration['datatype'] = 'layout'; + $configuration['fieldtype'] = 'panel'; + $configuration['name'] = 'opendxp_root'; + + $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($configuration, true); + $customLayout->setLayoutDefinitions($layout); + $customLayout->setName($values['name']); + $customLayout->setDescription($values['description']); + $customLayout->setDefault($values['default']); + + if (!$customLayout->isWriteable()) { + throw new ConfigWriteException(); + } + + $customLayout->save(); + + return $customLayout; + } +} diff --git a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php new file mode 100644 index 00000000..a0a9ebb2 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php @@ -0,0 +1,40 @@ +load() as $item) { + $existingIds[] = $item->getId(); + if ($item->getClassId() == $classId) { + $existingNames[] = $item->getName(); + } + } + + return new SuggestCustomLayoutIdentifierResult($identifier, $existingIds, $existingNames); + } +} diff --git a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierResult.php b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierResult.php new file mode 100644 index 00000000..c64cdce9 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierResult.php @@ -0,0 +1,27 @@ + $allParams]); + $this->eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::OBJECT_LIST_BEFORE_FILTER_PREPARE); + $allParams = $filterPrepareEvent->getArgument('requestParams'); + + $requestedLanguage = $allParams['language'] ?? null; + if (!$requestedLanguage) { + $requestedLanguage = $fallbackLocale; + } + + return new DataObjectGridProxyResult( + data: $this->gridService->gridProxy($allParams, DataObject::OBJECT_TYPE_OBJECT, $requestedLanguage), + requestedLanguage: $requestedLanguage, + ); + } +} diff --git a/src/Handler/DataObject/DataObjectGridProxyResult.php b/src/Handler/DataObject/DataObjectGridProxyResult.php new file mode 100644 index 00000000..1f1d28c1 --- /dev/null +++ b/src/Handler/DataObject/DataObjectGridProxyResult.php @@ -0,0 +1,26 @@ +setCondition('`path` LIKE ' . $list->quote($list->escapeLike($parentObject->getRealFullPath()) . '/%')); + $list->setLimit($amount); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('DESC'); + + $deletedItems = []; + foreach ($list as $object) { + $deletedItems[$object->getId()] = $object->getRealFullPath(); + if ($object->isAllowed('delete') && !$object->isLocked()) { + $object->delete(); + } + } + + return new DeleteDataObjectResult(deleted: $deletedItems); + } + + $object = DataObject::getById($id); + + if ($object) { + if (!$object->isAllowed('delete')) { + throw new AccessDeniedHttpException('Missing permission to delete object'); + } + + if ($object->isLocked()) { + throw new RuntimeException('prevented deleting object, because it is locked: ID: ' . $object->getId()); + } + + $object->delete(); + } + + // Return empty result even when the object doesn't exist — valid for batch delete incl. children + return new DeleteDataObjectResult(); + } +} diff --git a/src/Handler/DataObject/DeleteDataObjectResult.php b/src/Handler/DataObject/DeleteDataObjectResult.php new file mode 100644 index 00000000..89ee950c --- /dev/null +++ b/src/Handler/DataObject/DeleteDataObjectResult.php @@ -0,0 +1,25 @@ +delete(); + } +} diff --git a/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php new file mode 100644 index 00000000..979f80f0 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php @@ -0,0 +1,42 @@ +getKey(), + DataObject\ClassDefinition\Service::generateFieldCollectionJson($fieldCollection), + ); + } +} diff --git a/src/Handler/DataObject/FieldCollection/ExportFieldCollectionResult.php b/src/Handler/DataObject/FieldCollection/ExportFieldCollectionResult.php new file mode 100644 index 00000000..0b8766b7 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/ExportFieldCollectionResult.php @@ -0,0 +1,26 @@ +getObjectVars(), $fc->isWritable()); + } +} diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php new file mode 100644 index 00000000..d157db5c --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php @@ -0,0 +1,75 @@ +userContext->getAdminUser(); + $list = (new DataObject\Fieldcollection\Definition\Listing())->load(); + $currentLayoutId = $layoutId; + + if ($allowedTypes !== null) { + $filteredList = []; + $object = DataObject\Concrete::getById($objectId); + + foreach ($list as $type) { + if (!in_array($type->getKey(), $allowedTypes)) { + continue; + } + + $filteredList[] = $type; + + $layoutDefinitions = $type->getLayoutDefinitions(); + $context = [ + 'containerType' => 'fieldcollection', + 'containerKey' => $type->getKey(), + 'outerFieldname' => $fieldName, + ]; + + DataObject\Service::enrichLayoutDefinition($layoutDefinitions, $object, $context); + + if ($currentLayoutId == -1 && $adminUser->isAdmin()) { + DataObject\Service::createSuperLayout($layoutDefinitions); + } + } + + $list = $filteredList; + } + + $event = new GenericEvent(null, ['list' => $list, 'objectId' => $objectId]); + $this->eventDispatcher->dispatch($event, AdminEvents::CLASS_FIELDCOLLECTION_LIST_PRE_SEND_DATA); + + return new FieldCollectionListResult($event->getArgument('list')); + } +} diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionResult.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionResult.php new file mode 100644 index 00000000..0e669567 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + $list = (new DataObject\Fieldcollection\Definition\Listing())->load(); + + $layoutDefinitions = []; + $definitions = []; + $currentLayoutId = $layoutId; + $object = $objectId > 0 ? DataObject\Concrete::getById($objectId) : null; + + $groups = []; + foreach ($list as $item) { + if ($allowedTypes && !in_array($item->getKey(), $allowedTypes)) { + continue; + } + + $nodeData = [ + 'id' => $item->getKey(), + 'text' => $item->getKey(), + 'title' => $item->getTitle(), + 'key' => $item->getKey(), + 'leaf' => true, + 'iconCls' => 'opendxp_icon_fieldcollection', + ]; + + if ($forObjectEditor) { + $itemLayoutDefinitions = $item->getLayoutDefinitions(); + DataObject\Service::enrichLayoutDefinition($itemLayoutDefinitions, $object); + if ($currentLayoutId == -1 && $adminUser->isAdmin()) { + DataObject\Service::createSuperLayout($itemLayoutDefinitions); + } + $layoutDefinitions[$item->getKey()] = $itemLayoutDefinitions; + } + + if ($item->getGroup()) { + if (!isset($groups[$item->getGroup()])) { + $groups[$item->getGroup()] = [ + 'id' => 'group_' . $item->getKey(), + 'text' => htmlspecialchars($item->getGroup()), + 'expandable' => true, + 'leaf' => false, + 'allowChildren' => true, + 'iconCls' => 'opendxp_icon_folder', + 'group' => $item->getGroup(), + 'children' => [], + ]; + } + $groups[$item->getGroup()]['children'][] = $nodeData; + } else { + $definitions[] = $nodeData; + } + } + + foreach ($groups as $group) { + $definitions[] = $group; + } + + $event = new GenericEvent(null, [ + 'list' => $definitions, + 'objectId' => $objectId, + 'layoutDefinitions' => $layoutDefinitions, + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::CLASS_FIELDCOLLECTION_LIST_PRE_SEND_DATA); + + return new FieldCollectionTreeResult( + $event->getArgument('list'), + $event->getArgument('layoutDefinitions'), + ); + } +} diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php new file mode 100644 index 00000000..81166f3c --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php @@ -0,0 +1,42 @@ +load() as $class) { + foreach ($class->getFieldDefinitions() as $fieldDef) { + if ($fieldDef instanceof Fieldcollections && in_array($key, $fieldDef->getAllowedTypes())) { + $result[] = [ + 'class' => $class->getName(), + 'field' => $fieldDef->getName(), + ]; + } + } + } + + return $result; + } +} diff --git a/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php new file mode 100644 index 00000000..8539037c --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php @@ -0,0 +1,33 @@ +loadNames() as $fcName) { + if (strtolower($key) === strtolower($fcName)) { + throw new BadRequestHttpException('FieldCollection with the same name already exists (lower/upper cases may be different)'); + } + } + } + + $fcDef = new DataObject\Fieldcollection\Definition(); + $fcDef->setKey($key); + $fcDef->setTitle($title); + $fcDef->setGroup($group); + + if ($values !== null) { + $fcDef->setParentClass($values['parentClass']); + $fcDef->setImplementsInterfaces($values['implementsInterfaces']); + } + + if ($configuration !== null) { + $configuration['datatype'] = 'layout'; + $configuration['fieldtype'] = 'panel'; + + $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($configuration, true); + $fcDef->setLayoutDefinitions($layout); + } + + $fcDef->save(); + + return $fcDef; + } +} diff --git a/src/Handler/DataObject/GetDataObjectChildrenHandler.php b/src/Handler/DataObject/GetDataObjectChildrenHandler.php new file mode 100644 index 00000000..8ab07818 --- /dev/null +++ b/src/Handler/DataObject/GetDataObjectChildrenHandler.php @@ -0,0 +1,78 @@ +userContext->getAdminUser(); + $objects = []; + + $cv = $view ? ($this->elementService->getCustomViewById($view) ?? []) : []; + + if (!is_null($filter)) { + // When filter is applied, limit was capped to 100 by caller + $limit = 100; + } + + $children = $childrenList->load(); + $filteredTotalCount = $childrenList->getTotalCount(); + + foreach ($children as $child) { + $objectTreeNode = $this->elementService->getElementTreeNodeConfig($child); + // this if is obsolete since as long as the change with #11714 about list on line 175-179 are working fine, we already filter the list=1 there + if ($objectTreeNode['permissions']['list'] == 1) { + $objects[] = $objectTreeNode; + } + } + + //pagination for custom view + $total = $cv + ? $filteredTotalCount + : $object->getChildAmount(null, $adminUser); + + return new GetDataObjectChildrenResult( + objects: $objects, + offset: $offset, + limit: $limit, + total: $total, + filteredTotalCount: $filteredTotalCount, + filter: $filter, + fromPaging: $fromPaging, + ); + } +} diff --git a/src/Handler/DataObject/GetDataObjectChildrenResult.php b/src/Handler/DataObject/GetDataObjectChildrenResult.php new file mode 100644 index 00000000..d83c81d3 --- /dev/null +++ b/src/Handler/DataObject/GetDataObjectChildrenResult.php @@ -0,0 +1,31 @@ +userContext->getAdminUser(); + $object = DataObject::getById($objectId); + + if (!$object) { + throw new NotFoundHttpException(sprintf('DataObject with id %d not found', $objectId)); + } + + if (!$object->isAllowed('view')) { + throw new AccessDeniedHttpException('Missing permission to view object'); + } + + $objectData = []; + + $objectData['general'] = []; + $objectData['idPath'] = Element\Service::getIdPath($object); + $objectData['type'] = $object->getType(); + $allowedKeys = ['published', 'key', 'id', 'type', 'path', 'modificationDate', 'creationDate', 'userOwner', 'userModification']; + foreach ($object->getObjectVars() as $key => $value) { + if (in_array($key, $allowedKeys)) { + $objectData['general'][$key] = $value; + } + } + $objectData['general']['fullpath'] = $object->getRealFullPath(); + $objectData['general']['locked'] = $object->isLocked(); + + $objectData['properties'] = Element\Service::minimizePropertiesForEditmode($object->getProperties()); + $objectData['userPermissions'] = $object->getUserPermissions($adminUser); + $objectData['classes'] = $this->prepareChildClasses($object->getDao()->getClasses()); + + $this->normalizer->normalize($object, $objectData, self::class); + + $event = new GenericEvent($this, ['data' => $objectData, 'object' => $object]); + $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_GET_PRE_SEND_DATA); + $objectData = $event->getArgument('data'); + + return new GetDataObjectFolderResult(data: $objectData); + } + + /** + * @param DataObject\ClassDefinition[] $classes + */ + private function prepareChildClasses(array $classes): array + { + $reduced = []; + foreach ($classes as $class) { + $reduced[] = [ + 'id' => $class->getId(), + 'name' => $class->getName(), + 'inheritance' => $class->getAllowInherit(), + ]; + } + + return $reduced; + } +} diff --git a/src/Handler/DataObject/GetDataObjectFolderResult.php b/src/Handler/DataObject/GetDataObjectFolderResult.php new file mode 100644 index 00000000..64da507c --- /dev/null +++ b/src/Handler/DataObject/GetDataObjectFolderResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $object = DataObjectVersionHelper::resolveLatestDraft($objectFromDatabase, $adminUser?->getId(), $draftVersion); + $objectFromVersion = $object !== $objectFromDatabase; + + if (!$object->isAllowed('view')) { + throw new AccessDeniedHttpException(); + } + + if ($object->isAllowed('save') || $object->isAllowed('publish') || $object->isAllowed('unpublish') || $object->isAllowed('delete')) { + $this->editLockService->checkAndAcquire($object->getId(), 'object', AdminEvents::OBJECT_GET_IS_LOCKED, $object); + } + + $objectData = []; + + /** ------------------------------------------------------------- + * Load some general data from published object (if existing) + * ------------------------------------------------------------- */ + $objectData['idPath'] = Element\Service::getIdPath($objectFromDatabase); + + $linkGeneratorReference = $objectFromDatabase->getClass()->getLinkGeneratorReference(); + $previewGenerator = $objectFromDatabase->getClass()->getPreviewGenerator(); + if (empty($previewGenerator) && !empty($linkGeneratorReference)) { + $previewGenerator = $this->defaultPreviewGenerator; + } + + $objectData['hasPreview'] = false; + if ($linkGeneratorReference || $previewGenerator) { + $objectData['hasPreview'] = true; + } + + $objectData['general'] = []; + + $allowedKeys = ['published', 'key', 'id', 'creationDate', 'classId', 'className', 'type', 'parentId', 'userOwner', 'userModification']; + foreach ($objectFromDatabase->getObjectVars() as $key => $value) { + if (in_array($key, $allowedKeys)) { + $objectData['general'][$key] = $value; + } + } + $objectData['general']['classTitle'] = $objectFromDatabase->getClass()->getTitle() ?: $objectFromDatabase->getClassName(); + $objectData['general']['fullpath'] = $objectFromDatabase->getRealFullPath(); + $objectData['general']['locked'] = $objectFromDatabase->isLocked(); + $objectData['general']['php'] = [ + 'classes' => [$objectFromDatabase::class, ...array_values(class_parents($objectFromDatabase))], + 'interfaces' => array_values(class_implements($objectFromDatabase)), + ]; + $objectData['general']['allowInheritance'] = $objectFromDatabase->getClass()->getAllowInherit(); + $objectData['general']['allowVariants'] = $objectFromDatabase->getClass()->getAllowVariants(); + $objectData['general']['showVariants'] = $objectFromDatabase->getClass()->getShowVariants(); + $objectData['general']['showAppLoggerTab'] = $objectFromDatabase->getClass()->getShowAppLoggerTab(); + $objectData['general']['showFieldLookup'] = $objectFromDatabase->getClass()->getShowFieldLookup(); + $objectData['general']['linkGeneratorReference'] = $linkGeneratorReference; + + if ($previewGenerator) { + $objectData['general']['previewConfig'] = $previewGenerator->getPreviewConfig($objectFromDatabase); + } + + $objectData['layout'] = $objectFromDatabase->getClass()->getLayoutDefinitions(); + $objectData['userPermissions'] = $objectFromDatabase->getUserPermissions($adminUser); + $objectVersions = Element\Service::getSafeVersionInfo($objectFromDatabase->getVersions()); + $objectData['versions'] = array_splice($objectVersions, -1, 1); + $objectData['scheduledTasks'] = array_map( + static fn (Task $task) => $task->getObjectVars(), + $objectFromDatabase->getScheduledTasks() + ); + + $objectData['childdata']['id'] = $objectFromDatabase->getId(); + $objectData['childdata']['data']['classes'] = $this->prepareChildClasses($objectFromDatabase->getDao()->getClasses()); + $objectData['childdata']['data']['general'] = $objectData['general']; + + /** ------------------------------------------------------------- + * Load remaining general data from latest version + * ------------------------------------------------------------- */ + $allowedKeys = ['modificationDate', 'userModification']; + foreach ($object->getObjectVars() as $key => $value) { + if (in_array($key, $allowedKeys)) { + $objectData['general'][$key] = $value; + } + } + + $loadContext = new DataObjectLoadContext(); + try { + $this->getDataForObject($object, $loadContext, $objectFromVersion); + } catch (Throwable) { + $loadContext = new DataObjectLoadContext(); + $this->getDataForObject($objectFromDatabase, $loadContext, false); + } + + $objectData['data'] = $loadContext->objectData; + $objectData['metaData'] = $loadContext->metaData; + $objectData['properties'] = Element\Service::minimizePropertiesForEditmode($object->getProperties()); + + // this used for the "this is not a published version" hint + // and for adding the published icon to version overview + $objectData['general']['versionDate'] = $objectFromDatabase->getModificationDate(); + $objectData['general']['versionCount'] = $objectFromDatabase->getVersionCount(); + + $currentLayoutId = $layoutId; + + $validLayouts = DataObject\Service::getValidLayouts($object); + + //Fallback if $currentLayoutId is not set or empty string + //Uses first valid layout instead of admin layout when empty + $ok = false; + foreach ($validLayouts as $layout) { + if ($currentLayoutId == $layout->getId()) { + $ok = true; + } + } + + if (!$ok) { + $currentLayoutId = null; + } + + //main layout has id 0 so we check for is_null() + if ($currentLayoutId === null && $validLayouts !== []) { + if (count($validLayouts) === 1) { + $firstLayout = reset($validLayouts); + $currentLayoutId = $firstLayout->getId(); + } else { + foreach ($validLayouts as $checkDefaultLayout) { + if ($checkDefaultLayout->getDefault()) { + $currentLayoutId = $checkDefaultLayout->getId(); + } + } + } + } + + if ($currentLayoutId === null && count($validLayouts) > 0) { + $currentLayoutId = reset($validLayouts)->getId(); + } + + if ($validLayouts !== []) { + $objectData['validLayouts'] = []; + + foreach ($validLayouts as $validLayout) { + $objectData['validLayouts'][] = ['id' => $validLayout->getId(), 'name' => $validLayout->getName()]; + } + + usort($objectData['validLayouts'], static function ($layoutData1, $layoutData2) { + if ($layoutData2['id'] === '-1') { + return 1; + } + + if ($layoutData1['id'] === '-1') { + return -1; + } + + if ($layoutData2['id'] === '0') { + return 1; + } + if ($layoutData1['id'] === '0') { + return -1; + } + + return strcasecmp($layoutData1['name'], $layoutData2['name']); + }); + + $user = Tool\Admin::getCurrentUser(); + + if ($currentLayoutId == -1 && $user->isAdmin()) { + $layout = DataObject\Service::getSuperLayoutDefinition($object); + $objectData['layout'] = $layout; + } elseif (!empty($currentLayoutId)) { + $objectData['layout'] = $validLayouts[$currentLayoutId]->getLayoutDefinitions(); + } + + $objectData['currentLayoutId'] = $currentLayoutId; + } + + DataObject\Service::enrichLayoutDefinition($objectData['layout'], $object); + + $event = new GenericEvent($this, ['data' => $objectData, 'object' => $object]); + $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_GET_PRE_SEND_DATA); + $objectData = $event->getArgument('data'); + + $this->normalizer->normalize($object, $objectData, self::class, ['draftVersion' => $draftVersion]); + + return new GetDataObjectResult(data: $objectData); + } + + private function getDataForObject(DataObject\Concrete $object, DataObjectLoadContext $loadContext, bool $objectFromVersion = false): void + { + foreach ($object->getClass()->getFieldDefinitions(['object' => $object]) as $key => $def) { + $this->getDataForField($object, $key, $def, $loadContext, $objectFromVersion); + } + } + + /** + * Gets recursively attribute data from parent and fills objectData and metaData + */ + private function getDataForField(DataObject\Concrete $object, string $key, DataObject\ClassDefinition\Data $fielddefinition, DataObjectLoadContext $loadContext, bool $objectFromVersion, int $level = 0): void + { + $parent = DataObject\Service::hasInheritableParentObject($object); + $getter = 'get' . ucfirst($key); + + // Editmode optimization for lazy loaded relations (note that this is just for AbstractRelations, not for all + // LazyLoadingSupportInterface types. It tries to optimize fetching the data needed for the editmode without + // loading the entire target element. + // ReverseObjectRelation should go in there anyway (regardless if it a version or not), + // so that the values can be loaded. + if ( + (!$objectFromVersion && $fielddefinition instanceof AbstractRelations) + || $fielddefinition instanceof ReverseObjectRelation + ) { + $refId = null; + + if ($fielddefinition instanceof ReverseObjectRelation) { + $refKey = $fielddefinition->getOwnerFieldName(); + $refClass = DataObject\ClassDefinition::getByName($fielddefinition->getOwnerClassName()); + if ($refClass) { + $refId = $refClass->getId(); + } + } else { + $refKey = $key; + } + + $relations = $object->getRelationData($refKey, !$fielddefinition instanceof ReverseObjectRelation, $refId); + + if ($fielddefinition->supportsInheritance() && $relations === [] && !empty($parent)) { + $this->getDataForField($parent, $key, $fielddefinition, $loadContext, $objectFromVersion, $level + 1); + } else { + $data = []; + + if ($fielddefinition instanceof DataObject\ClassDefinition\Data\ManyToOneRelation) { + if (isset($relations[0])) { + $data = $relations[0]; + $data['published'] = (bool)$data['published']; + } else { + $data = null; + } + } elseif ( + ($fielddefinition instanceof DataObject\ClassDefinition\Data\OptimizedAdminLoadingInterface && $fielddefinition->isOptimizedAdminLoading()) + || ($fielddefinition instanceof ManyToManyObjectRelation && !$fielddefinition->getVisibleFields() && !$fielddefinition instanceof DataObject\ClassDefinition\Data\AdvancedManyToManyObjectRelation) + ) { + foreach ($relations as $rkey => $rel) { + $index = $rkey + 1; + $rel['fullpath'] = $rel['path']; + $rel['classname'] = $rel['subtype']; + $rel['rowId'] = $rel['id'] . AbstractRelations::RELATION_ID_SEPARATOR . $index . AbstractRelations::RELATION_ID_SEPARATOR . $rel['type']; + $rel['published'] = (bool)$rel['published']; + $data[] = $rel; + } + } else { + $fieldData = $object->$getter(); + $data = $fielddefinition->getDataForEditmode($fieldData, $object, ['objectFromVersion' => $objectFromVersion]); + } + $loadContext->objectData[$key] = $data; + $loadContext->metaData[$key]['objectid'] = $object->getId(); + $loadContext->metaData[$key]['inherited'] = $level !== 0; + } + } else { + $fieldData = $object->$getter(); + $isInheritedValue = false; + + if ($fielddefinition instanceof DataObject\ClassDefinition\Data\CalculatedValue) { + $fieldData = new DataObject\Data\CalculatedValue($fielddefinition->getName()); + $fieldData->setContextualData('object', null, null, null, null, null, $fielddefinition); + $value = $fielddefinition->getDataForEditmode($fieldData, $object, ['objectFromVersion' => $objectFromVersion]); + } else { + $value = $fielddefinition->getDataForEditmode($fieldData, $object, ['objectFromVersion' => $objectFromVersion]); + } + + // following some exceptions for special data types (localizedfields, objectbricks) + if ($value && ($fieldData instanceof DataObject\Localizedfield || $fieldData instanceof DataObject\Classificationstore)) { + // make sure that the localized field participates in the inheritance detection process + $isInheritedValue = $value['inherited']; + } + if ($fielddefinition instanceof DataObject\ClassDefinition\Data\Objectbricks && is_array($value)) { + // make sure that the objectbricks participate in the inheritance detection process + foreach ($value as $singleBrickData) { + if (!empty($singleBrickData['inherited'])) { + $isInheritedValue = true; + } + } + } + + if ($fielddefinition->isEmpty($fieldData) && !empty($parent)) { + $this->getDataForField($parent, $key, $fielddefinition, $loadContext, $objectFromVersion, $level + 1); + // exception for classification store. if there are no items then it is empty by definition. + // consequence is that we have to preserve the metadata information + if ($fielddefinition instanceof DataObject\ClassDefinition\Data\Classificationstore && $level === 0) { + $loadContext->objectData[$key]['metaData'] = $value['metaData'] ?? []; + $loadContext->objectData[$key]['inherited'] = true; + } + } else { + $isInheritedValue = $isInheritedValue || ($level !== 0); + $loadContext->metaData[$key]['objectid'] = $object->getId(); + + $loadContext->objectData[$key] = $value; + $loadContext->metaData[$key]['inherited'] = $isInheritedValue; + + if ($isInheritedValue && !$fielddefinition->isEmpty($fieldData) && !$fielddefinition->supportsInheritance()) { + $loadContext->objectData[$key] = null; + $loadContext->metaData[$key]['inherited'] = false; + $loadContext->metaData[$key]['hasParentValue'] = true; + } + } + } + } + + /** + * @param DataObject\ClassDefinition[] $classes + */ + private function prepareChildClasses(array $classes): array + { + $reduced = []; + foreach ($classes as $class) { + $reduced[] = [ + 'id' => $class->getId(), + 'name' => $class->getName(), + 'inheritance' => $class->getAllowInherit(), + ]; + } + + return $reduced; + } + +} diff --git a/src/Handler/DataObject/GetDataObjectPreviewUrlHandler.php b/src/Handler/DataObject/GetDataObjectPreviewUrlHandler.php new file mode 100644 index 00000000..07b78a19 --- /dev/null +++ b/src/Handler/DataObject/GetDataObjectPreviewUrlHandler.php @@ -0,0 +1,60 @@ +getClass()->getPreviewGenerator()) { + $url = $previewService->generatePreviewUrl($object, ['preview' => true, ...$queryParams]); + } elseif ($object->getClass()->getLinkGenerator()) { + $parameters = [ + 'preview' => true, + ]; + + $url = $this->defaultPreviewGenerator->generatePreviewUrl($object, [...$parameters, ...$queryParams]); + } + + if (!$url) { + throw new NotFoundHttpException('Cannot render preview due to empty URL'); + } + + // replace all remaining % signs + $url = str_replace('%', '%25', $url); + + $urlParts = parse_url($url); + + $redirectParameters = array_filter([ + 'opendxp_object_preview' => $object->getId(), + 'site' => $queryParams[PreviewGeneratorInterface::PARAMETER_SITE] ?? null, + 'dc' => time(), + ]); + + return $urlParts['path'] . '?' . http_build_query($redirectParameters) . (isset($urlParts['query']) ? '&' . $urlParts['query'] : ''); + } +} diff --git a/src/Handler/DataObject/GetDataObjectResult.php b/src/Handler/DataObject/GetDataObjectResult.php new file mode 100644 index 00000000..443bf982 --- /dev/null +++ b/src/Handler/DataObject/GetDataObjectResult.php @@ -0,0 +1,25 @@ +getParent()) { + $list = new DataObject\Listing(); + $list->setCondition('parentId = ?', $parent->getId()); + $list->setUnpublished(true); + $total = $list->getTotalCount(); + + $info = [ + 'total' => $total, + ]; + + if ($total > $limit) { + $idList = $list->loadIdList(); + $position = array_search($object->getId(), $idList); + $info['position'] = $position + 1; + $info['page'] = ceil($info['position'] / $limit); + } + + $data[$parent->getId()] = $info; + + $object = $parent; + } + + return new GetIdPathPagingInfoResult(data: $data); + } +} diff --git a/src/Handler/DataObject/GetIdPathPagingInfoResult.php b/src/Handler/DataObject/GetIdPathPagingInfoResult.php new file mode 100644 index 00000000..2578e0d2 --- /dev/null +++ b/src/Handler/DataObject/GetIdPathPagingInfoResult.php @@ -0,0 +1,25 @@ +mapper->applyChanges($object, $changedData); + } + + /** @var DataObject\ClassDefinition\Data\Select|DataObject\ClassDefinition\Data\Multiselect $fieldDefinition */ + $fieldDefinition = DataObject\Classificationstore\Service::getFieldDefinitionFromJson( + $fieldDefinitionConfig, + $fieldDefinitionConfig['fieldtype'] + ); + + $optionsProvider = OptionsProviderResolver::resolveProvider( + $fieldDefinition->getOptionsProviderClass(), + $fieldDefinition instanceof DataObject\ClassDefinition\Data\Multiselect + ? OptionsProviderResolver::MODE_MULTISELECT + : OptionsProviderResolver::MODE_SELECT + ); + + return $optionsProvider->getOptions( + [ + 'object' => $object, + 'fieldname' => $fieldDefinition->getName(), + 'class' => $object->getClass(), + 'context' => $context, + ], + $fieldDefinition + ); + } +} diff --git a/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php b/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php new file mode 100644 index 00000000..19bcf7ba --- /dev/null +++ b/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php @@ -0,0 +1,50 @@ +userContext->getAdminUser(); + $object = DataObject::getById($objectId); + if (!$object) { + throw new NotFoundHttpException(); + } + + if (!$object->isAllowed('list')) { + throw new AccessDeniedHttpException(); + } + + Db::get()->executeStatement( + 'DELETE FROM gridconfig_favourites WHERE ownerId = ? AND classId = ? AND searchType = ? AND objectId != ? AND objectId != 0', + [$adminUser->getId(), $classId, $searchType, $objectId] + ); + } +} diff --git a/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php new file mode 100644 index 00000000..36f349ee --- /dev/null +++ b/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php @@ -0,0 +1,46 @@ +userContext->getAdminUser(); + $gridConfig = GridConfig::getById($gridConfigId); + if (!$gridConfig) { + throw new NotFoundHttpException('Grid config not found: ' . $gridConfigId); + } + + if ($gridConfig->getOwnerId() !== $adminUser->getId() && !$adminUser->isAdmin()) { + throw new BadRequestHttpException("don't mess with someone elses grid config"); + } + + $gridConfig->delete(); + } +} diff --git a/src/Handler/DataObject/Helper/DeleteGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/DeleteGridColumnConfigHandler.php new file mode 100644 index 00000000..3cd50d57 --- /dev/null +++ b/src/Handler/DataObject/Helper/DeleteGridColumnConfigHandler.php @@ -0,0 +1,50 @@ +gridConfigResolver->resolve($request, $params, true); + $data = [...$resolverResult->jsonSerialize(), 'deleteSuccess' => true]; + + $event = new GenericEvent($this, [ + 'data' => $data, + 'request' => $request, + 'config' => $this->config, + 'context' => 'delete', + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_GRID_GET_COLUMN_CONFIG_PRE_SEND_DATA); + + return $event->getArgument('data'); + } +} diff --git a/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php b/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php new file mode 100644 index 00000000..3a786e7d --- /dev/null +++ b/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php @@ -0,0 +1,142 @@ +getName()) . '\\Listing'; + + /** @var Listing $list */ + $list = new $listClass(); + + $quotedIds = []; + foreach ($ids as $id) { + $quotedIds[] = $list->quote($id); + } + + $list->setObjectTypes(DataObject::$types); + $list->setCondition('id IN (' . implode(',', $quotedIds) . ')'); + $list->setOrderKey(' FIELD(id, ' . implode(',', $quotedIds) . ')', false); + + $beforeListExportEvent = new GenericEvent(null, [ + 'list' => $list, + 'context' => $allParams, + ]); + $this->eventDispatcher->dispatch($beforeListExportEvent, AdminEvents::OBJECT_LIST_BEFORE_EXPORT); + $list = $beforeListExportEvent->getArgument('list'); + + $csv = DataObject\Service::getCsvData( + $requestedLanguage, + $this->localeService, + $list, + $fields, + $header, + $addTitles, + $context + ); + + $temp = tmpfile(); + + try { + $storage = Storage::get('temp'); + $csvFile = $this->gridExportService->getCsvFile($fileHandle); + + $fileStream = $storage->readStream($csvFile); + stream_copy_to_stream($fileStream, $temp, null, 0); + + $firstLine = true; + + if ($addTitles && $header === 'no_header') { + array_shift($csv); + $firstLine = false; + } + + $lineCount = count($csv); + + if (!$addTitles && $lineCount > 0) { + fwrite($temp, "\r\n"); + } + + for ($i = 0; $i < $lineCount; $i++) { + $line = $csv[$i]; + if ($addTitles && $firstLine) { + $firstLine = false; + fwrite($temp, implode($delimiter, $line)); + } else { + fwrite($temp, implode($delimiter, array_map($this->gridColumnConfigService->encode(...), $line))); + } + if ($i < $lineCount - 1) { + fwrite($temp, "\r\n"); + } + } + + $storage->writeStream($csvFile, $temp); + } catch (UnableToReadFile $exception) { + Logger::err($exception->getMessage()); + throw new BadRequestHttpException(sprintf('export file not found: %s', $fileHandle)); + } finally { + if (is_resource($temp)) { + fclose($temp); + } + } + } +} diff --git a/src/Handler/DataObject/Helper/ExecuteBatchHandler.php b/src/Handler/DataObject/Helper/ExecuteBatchHandler.php new file mode 100644 index 00000000..b4b45590 --- /dev/null +++ b/src/Handler/DataObject/Helper/ExecuteBatchHandler.php @@ -0,0 +1,36 @@ +userContext->getAdminUser(); + + return $this->gridBatchService->executeObjectBatch($params, $locale, $adminUser); + } +} diff --git a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php b/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php new file mode 100644 index 00000000..1cadadd8 --- /dev/null +++ b/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php @@ -0,0 +1,114 @@ + $field, + 'value' => $field, + ]; + } + + $commonFields = []; + $firstOne = true; + + foreach ($classNameList as $className) { + $class = DataObject\ClassDefinition::getByName($className); + if (!$class) { + continue; + } + + $fds = $class->getFieldDefinitions(); + $additionalFieldNames = array_keys($fds); + + $localizedFields = $class->getFieldDefinition('localizedfields'); + if ($localizedFields instanceof DataObject\ClassDefinition\Data\Localizedfields) { + $lfNames = array_keys($localizedFields->getFieldDefinitions()); + $additionalFieldNames = [...$additionalFieldNames, ...$lfNames]; + } + + foreach ($commonFields as $commonFieldKey => $commonFieldDefinition) { + if (!in_array($commonFieldKey, $additionalFieldNames)) { + unset($commonFields[$commonFieldKey]); + } + } + + $this->processAvailableFieldDefinitions($fds, $firstOne, $commonFields); + $firstOne = false; + } + + foreach (array_keys($commonFields) as $field) { + $availableFields[] = [ + 'key' => $field, + 'value' => $field, + ]; + } + + return new GetAvailableVisibleFieldsResult($availableFields); + } + + /** + * @param DataObject\ClassDefinition\Data[] $fds + * @param DataObject\ClassDefinition\Data[] $commonFields + */ + private function processAvailableFieldDefinitions(array $fds, bool &$firstOne, array &$commonFields): void + { + foreach ($fds as $fd) { + if ($fd instanceof DataObject\ClassDefinition\Data\Fieldcollections) { + continue; + } + if ($fd instanceof DataObject\ClassDefinition\Data\Objectbricks) { + continue; + } + if ($fd instanceof DataObject\ClassDefinition\Data\Block) { + continue; + } + if ($fd instanceof DataObject\ClassDefinition\Data\Localizedfields) { + $lfDefs = $fd->getFieldDefinitions(); + $this->processAvailableFieldDefinitions($lfDefs, $firstOne, $commonFields); + } elseif ($firstOne || (isset($commonFields[$fd->getName()]) && $commonFields[$fd->getName()]->getFieldtype() == $fd->getFieldtype())) { + $commonFields[$fd->getName()] = $fd; + } + } + } +} diff --git a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsResult.php b/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsResult.php new file mode 100644 index 00000000..d2bb84d2 --- /dev/null +++ b/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + + return new GetBatchJobsResult( + jobs: $this->gridBatchService->getObjectBatchJobIds($allParams, $locale, $adminUser), + ); + } +} diff --git a/src/Handler/DataObject/Helper/GetBatchJobsResult.php b/src/Handler/DataObject/Helper/GetBatchJobsResult.php new file mode 100644 index 00000000..65ee0bbf --- /dev/null +++ b/src/Handler/DataObject/Helper/GetBatchJobsResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $userId = $adminUser?->getId() ?? 0; + + $list = $this->gridColumnConfigService->getMyOwnColumnConfigs($userId, $classId); + $list = [...$list, ...$this->gridColumnConfigService->getSharedColumnConfigs($adminUser, $classId)]; + + $result = [['id' => -1, 'name' => '--default--']]; + + foreach ($list as $config) { + $result[] = [ + 'id' => $config['id'], + 'name' => $config['name'], + ]; + } + + return $result; + } +} diff --git a/src/Handler/DataObject/Helper/GetExportJobsHandler.php b/src/Handler/DataObject/Helper/GetExportJobsHandler.php new file mode 100644 index 00000000..14aed124 --- /dev/null +++ b/src/Handler/DataObject/Helper/GetExportJobsHandler.php @@ -0,0 +1,61 @@ +gridHelperService->prepareListingForGrid($allParams, $requestedLanguage); + + $beforeListPrepareEvent = new GenericEvent($this, [ + 'list' => $list, + 'context' => $allParams, + ]); + $this->eventDispatcher->dispatch($beforeListPrepareEvent, AdminEvents::OBJECT_LIST_BEFORE_EXPORT_PREPARE); + $list = $beforeListPrepareEvent->getArgument('list'); + + $ids = $list->loadIdList(); + $jobs = array_chunk($ids, 20); + + $fileHandle = uniqid('export-'); + Storage::get('temp')->write($this->gridExportService->getCsvFile($fileHandle), ''); + + return new GetExportJobsResult(jobs: $jobs, fileHandle: $fileHandle); + } +} diff --git a/src/Handler/DataObject/Helper/GetExportJobsResult.php b/src/Handler/DataObject/Helper/GetExportJobsResult.php new file mode 100644 index 00000000..4febe800 --- /dev/null +++ b/src/Handler/DataObject/Helper/GetExportJobsResult.php @@ -0,0 +1,26 @@ +gridConfigResolver->resolve($request, $params); + + $event = new GenericEvent($this, [ + 'data' => $result->jsonSerialize(), + 'request' => $request, + 'config' => $this->config, + 'context' => 'get', + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_GRID_GET_COLUMN_CONFIG_PRE_SEND_DATA); + + return $event->getArgument('data'); + } +} diff --git a/src/Handler/DataObject/Helper/ImportUploadHandler.php b/src/Handler/DataObject/Helper/ImportUploadHandler.php new file mode 100644 index 00000000..0cc665ce --- /dev/null +++ b/src/Handler/DataObject/Helper/ImportUploadHandler.php @@ -0,0 +1,38 @@ +filesystem->dumpFile($importFile, $data); + + $importFileOriginal = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/import_' . $importId . '_original'; + $this->filesystem->dumpFile($importFileOriginal, $data); + } +} diff --git a/src/Handler/DataObject/Helper/LoadObjectDataHandler.php b/src/Handler/DataObject/Helper/LoadObjectDataHandler.php new file mode 100644 index 00000000..cb703fc0 --- /dev/null +++ b/src/Handler/DataObject/Helper/LoadObjectDataHandler.php @@ -0,0 +1,34 @@ +userContext->getAdminUser(); + $object = DataObject::getById($objectId); + if (!$object) { + throw new NotFoundHttpException(); + } + + if (!$object->isAllowed('list')) { + throw new AccessDeniedHttpException(); + } + + $class = DataObject\ClassDefinition::getById($classId); + if (!$class) { + throw new BadRequestHttpException('class ' . $classId . ' does not exist anymore'); + } + + $favourite = new GridConfigFavourite(); + $favourite->setOwnerId($adminUser->getId()); + $favourite->setClassId($classId); + $favourite->setSearchType($searchType); + $favourite->setType($type); + + $specializedConfigs = false; + + try { + if ($gridConfigId !== 0) { + $gridConfig = GridConfig::getById($gridConfigId); + $favourite->setGridConfigId($gridConfig->getId()); + } + + $favourite->setObjectId($objectId); + $favourite->save(); + + if ($global) { + $favourite->setObjectId(0); + $favourite->save(); + } + + $count = Db::get()->fetchOne( + 'SELECT * FROM gridconfig_favourites WHERE ownerId = ? AND classId = ? AND searchType = ? AND objectId != ? AND objectId != 0 AND `type` != ?', + [$adminUser->getId(), $classId, $searchType, $objectId, $type] + ); + $specializedConfigs = $count > 0; + } catch (Exception) { + $favourite->delete(); + } + + return new MarkDataObjectGridConfigFavouriteResult($specializedConfigs); + } +} diff --git a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteResult.php b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteResult.php new file mode 100644 index 00000000..6c440079 --- /dev/null +++ b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteResult.php @@ -0,0 +1,25 @@ +} + */ + public function __invoke(array $columns, array $existingHelperColumns): array + { + $helperColumns = []; + $newData = []; + + foreach ($columns as $item) { + if (!empty($item->isOperator)) { + $itemKey = '#' . uniqid('', false); + $item->key = $itemKey; + $helperColumns[$itemKey] = $item; + } + $newData[] = $item; + } + + return [ + 'newData' => $newData, + 'helperColumns' => [...$helperColumns, ...$existingHelperColumns], + ]; + } +} diff --git a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php new file mode 100644 index 00000000..9945c129 --- /dev/null +++ b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php @@ -0,0 +1,110 @@ +userContext->getAdminUser(); + $object = DataObject::getById($objectId); + if (!$object) { + throw new NotFoundHttpException(); + } + + if (!$object->isAllowed('list')) { + throw new AccessDeniedHttpException(); + } + + $gridConfigData['opendxp_version'] = Version::getVersion(); + $gridConfigData['opendxp_revision'] = Version::getRevision(); + $gridConfigData['context'] = $context; + unset($gridConfigData['settings']['isShared']); + + $gridConfigId = $metadata['gridConfigId'] ?? null; + $gridConfig = null; + if ($gridConfigId) { + $gridConfig = GridConfig::getById($gridConfigId); + } + + if ($gridConfig && $gridConfig->getOwnerId() !== $adminUser->getId() && !$adminUser->isAdmin()) { + throw new BadRequestHttpException("don't mess around with somebody elses configuration"); + } + + $this->gridColumnConfigService->updateGridConfigShares($gridConfig, $metadata ?? [], $adminUser, adminCanEditAll: true); + + if (!empty($metadata['setAsFavourite']) && $adminUser->isAdmin()) { + $this->gridColumnConfigService->updateGridConfigFavourites($gridConfig, $metadata, $adminUser, $objectId); + } + + if (!$gridConfig) { + $gridConfig = new GridConfig(); + $gridConfig->setName(date('c')); + $gridConfig->setClassId($classId); + $gridConfig->setSearchType($searchType); + $gridConfig->setOwnerId($adminUser->getId()); + } + + if ($metadata) { + $gridConfig->setName(SecurityHelper::convertHtmlSpecialChars($metadata['gridConfigName'])); + $gridConfig->setDescription(SecurityHelper::convertHtmlSpecialChars($metadata['gridConfigDescription'])); + $gridConfig->setShareGlobally($metadata['shareGlobally'] && $adminUser->isAdmin()); + $gridConfig->setSetAsFavourite($metadata['setAsFavourite'] && $adminUser->isAdmin()); + $gridConfig->setSaveFilters($metadata['saveFilters'] ?? false); + } + + $gridConfig->setConfig(json_encode($gridConfigData)); + $gridConfig->save(); + + $availableConfigs = $this->gridColumnConfigService->getMyOwnColumnConfigs($adminUser->getId(), $classId ?? '', $searchType); + $sharedConfigs = $this->gridColumnConfigService->getSharedColumnConfigs($adminUser, $classId ?? '', $searchType); + + $settings = $this->gridColumnConfigService->getShareSettings($gridConfig->getId()); + $settings['gridConfigId'] = (int) $gridConfig->getId(); + $settings['gridConfigName'] = SecurityHelper::convertHtmlSpecialChars($gridConfig->getName()); + $settings['gridConfigDescription'] = SecurityHelper::convertHtmlSpecialChars($gridConfig->getDescription()); + $settings['shareGlobally'] = $gridConfig->isShareGlobally(); + $settings['setAsFavourite'] = $gridConfig->isSetAsFavourite(); + $settings['saveFilters'] = $gridConfig->isSaveFilters(); + $settings['isShared'] = $gridConfig->getOwnerId() !== $adminUser->getId() && !$adminUser->isAdmin(); + + return new SaveDataObjectGridColumnConfigResult($settings, $availableConfigs, $sharedConfigs); + } +} diff --git a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigResult.php b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigResult.php new file mode 100644 index 00000000..17a556ea --- /dev/null +++ b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigResult.php @@ -0,0 +1,27 @@ +delete(); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php new file mode 100644 index 00000000..f992858f --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php @@ -0,0 +1,42 @@ +getKey(), + DataObject\ClassDefinition\Service::generateObjectBrickJson($objectBrick), + ); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/ExportObjectBrickResult.php b/src/Handler/DataObject/ObjectBrick/ExportObjectBrickResult.php new file mode 100644 index 00000000..29522a0d --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/ExportObjectBrickResult.php @@ -0,0 +1,26 @@ +load() as $brickDefinition) { + foreach ($brickDefinition->getClassDefinitions() as $class) { + if ($myClass->getName() == $class['classname']) { + $usages[] = [ + 'objectbrick' => $brickDefinition->getKey(), + 'field' => $class['fieldname'], + ]; + } + } + } + + return new BrickUsagesResult($usages); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php new file mode 100644 index 00000000..2fcaaab1 --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php @@ -0,0 +1,30 @@ +getObjectVars(), $brick->isWritable()); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php new file mode 100644 index 00000000..643d58dd --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php @@ -0,0 +1,80 @@ +userContext->getAdminUser(); + $list = (new DataObject\Objectbrick\Definition\Listing())->load(); + + if ($classId !== null && $fieldName !== null) { + $filteredList = []; + $className = DataObject\ClassDefinition::getById($classId)->getName(); + + foreach ($list as $type) { + $clsDefs = $type->getClassDefinitions(); + if (!empty($clsDefs)) { + foreach ($clsDefs as $cd) { + if ($cd['classname'] == $className && $cd['fieldname'] == $fieldName) { + $filteredList[] = $type; + break; + } + } + } + + $layout = $type->getLayoutDefinitions(); + if ($layoutId == -1 && $adminUser->isAdmin()) { + DataObject\Service::createSuperLayout($layout); + } + + $context = [ + 'containerType' => 'objectbrick', + 'containerKey' => $type->getKey(), + 'outerFieldname' => $fieldName, + ]; + + $object = DataObject\Concrete::getById($objectId); + DataObject\Service::enrichLayoutDefinition($layout, $object, $context); + $type->setLayoutDefinitions($layout); + } + + $list = $filteredList; + } + + $event = new GenericEvent(null, ['list' => $list, 'objectId' => $objectId]); + $this->eventDispatcher->dispatch($event, AdminEvents::CLASS_OBJECTBRICK_LIST_PRE_SEND_DATA); + + return new ObjectBrickListResult($event->getArgument('list')); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickResult.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickResult.php new file mode 100644 index 00000000..a747549c --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + $list = (new DataObject\Objectbrick\Definition\Listing())->load(); + + $layoutDefinitions = []; + $groups = []; + $definitions = []; + $object = $objectId > 0 ? DataObject\Concrete::getById($objectId) : null; + + $className = null; + $fieldname = null; + + if ($classId !== null && $fieldName !== null) { + $fieldname = $fieldName; + $className = DataObject\ClassDefinition::getById($classId)->getName(); + } + + foreach ($list as $item) { + $context = []; + if ($forObjectEditor) { + $context = [ + 'containerType' => 'objectbrick', + 'containerKey' => $item->getKey(), + 'outerFieldname' => $fieldname, + ]; + } + + if ($className !== null && $fieldname !== null) { + $keep = false; + foreach ($item->getClassDefinitions() as $cd) { + if ($cd['classname'] == $className && $cd['fieldname'] == $fieldname) { + $keep = true; + break; + } + } + if (!$keep) { + continue; + } + } + + $nodeData = [ + 'id' => $item->getKey(), + 'text' => $item->getKey(), + 'title' => $item->getTitle(), + 'key' => $item->getKey(), + 'leaf' => true, + 'iconCls' => 'opendxp_icon_objectbricks', + ]; + + if ($item->getGroup()) { + if (!isset($groups[$item->getGroup()])) { + $groups[$item->getGroup()] = [ + 'id' => 'group_' . $item->getKey(), + 'text' => htmlspecialchars($item->getGroup()), + 'expandable' => true, + 'leaf' => false, + 'allowChildren' => true, + 'iconCls' => 'opendxp_icon_folder', + 'group' => $item->getGroup(), + 'children' => [], + ]; + } + if ($forObjectEditor) { + $itemLayoutDefinitions = null; + if ($layoutId) { + $layout = DataObject\ClassDefinition\CustomLayout::getById($layoutId . '.brick.' . $item->getKey()); + if ($layout instanceof DataObject\ClassDefinition\CustomLayout) { + $itemLayoutDefinitions = $layout->getLayoutDefinitions(); + } + } + if (!$itemLayoutDefinitions instanceof DataObject\ClassDefinition\Layout) { + $itemLayoutDefinitions = $item->getLayoutDefinitions(); + } + DataObject\Service::enrichLayoutDefinition($itemLayoutDefinitions, $object, $context); + $layoutDefinitions[$item->getKey()] = $itemLayoutDefinitions; + } + $groups[$item->getGroup()]['children'][] = $nodeData; + } else { + if ($forObjectEditor) { + $layout = $item->getLayoutDefinitions(); + if ($layoutId == -1 && $adminUser->isAdmin()) { + DataObject\Service::createSuperLayout($layout); + } elseif ($layoutId) { + $customLayout = DataObject\ClassDefinition\CustomLayout::getById($layoutId . '.brick.' . $item->getKey()); + if ($customLayout instanceof DataObject\ClassDefinition\CustomLayout) { + $layout = $customLayout->getLayoutDefinitions(); + } + } + DataObject\Service::enrichLayoutDefinition($layout, $object, $context); + $layoutDefinitions[$item->getKey()] = $layout; + } + $definitions[] = $nodeData; + } + } + + foreach ($groups as $group) { + $definitions[] = $group; + } + + $event = new GenericEvent(null, [ + 'list' => $definitions, + 'objectId' => $objectId, + 'forObjectEditor' => $forObjectEditor, + 'layoutDefinitions' => $layoutDefinitions, + 'fieldName' => $fieldName, + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::CLASS_OBJECTBRICK_LIST_PRE_SEND_DATA); + + return new ObjectBrickTreeResult( + $event->getArgument('list'), + $event->getArgument('layoutDefinitions'), + ); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php new file mode 100644 index 00000000..1827ad00 --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php @@ -0,0 +1,33 @@ +loadNames() as $brickName) { + if (strtolower($key) === strtolower($brickName)) { + throw new BadRequestHttpException('Brick with the same name already exists (lower/upper cases may be different)'); + } + } + } + + $brickDef = new DataObject\Objectbrick\Definition(); + $brickDef->setKey($key); + $brickDef->setTitle($title); + $brickDef->setGroup($group); + + if ($values !== null) { + $brickDef->setParentClass($values['parentClass']); + $brickDef->setImplementsInterfaces($values['implementsInterfaces']); + $brickDef->setClassDefinitions($values['classDefinitions']); + } + + if ($configuration !== null) { + $configuration['datatype'] = 'layout'; + $configuration['fieldtype'] = 'panel'; + + $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($configuration, true); + $brickDef->setLayoutDefinitions($layout); + } + + $event = new GenericEvent(null, ['brickDefinition' => $brickDef]); + $this->eventDispatcher->dispatch($event, AdminEvents::CLASS_OBJECTBRICK_UPDATE_DEFINITION); + $brickDef = $event->getArgument('brickDefinition'); + + $brickDef->save(); + + return $brickDef; + } +} diff --git a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php new file mode 100644 index 00000000..bc03446c --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php @@ -0,0 +1,55 @@ +getBaseunit() ?? $fromUnit; + + $units = new Unit\Listing(); + $units->setCondition('baseunit = ' . $units->quote($baseUnit->getId()) . ' AND id != ' . $units->quote($fromUnit->getId())); + + $convertedValues = []; + foreach ($units->getUnits() as $targetUnit) { + $convertedValue = $this->conversionService->convert(new QuantityValue($value, $fromUnit), $targetUnit); + $convertedValues[] = [ + 'unit' => $targetUnit->getAbbreviation(), + 'unitName' => $targetUnit->getLongname(), + 'value' => round($convertedValue->getValue(), 4), + ]; + } + + return new ConvertAllQuantityValuesResult($value, $fromUnit->getAbbreviation(), $convertedValues); + } +} diff --git a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesResult.php b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesResult.php new file mode 100644 index 00000000..28e4c3a9 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesResult.php @@ -0,0 +1,27 @@ +conversionService->convert(new QuantityValue($value, $fromUnit), $toUnit); + + return new ConvertQuantityValueResult($convertedValue->getValue()); + } +} diff --git a/src/Handler/DataObject/QuantityValue/ConvertQuantityValueResult.php b/src/Handler/DataObject/QuantityValue/ConvertQuantityValueResult.php new file mode 100644 index 00000000..e57f4365 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/ConvertQuantityValueResult.php @@ -0,0 +1,25 @@ +service->generateDefinitionJson(); + } +} diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php new file mode 100644 index 00000000..41f8c525 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php @@ -0,0 +1,60 @@ +setOrderKey(['baseunit', 'factor', 'abbreviation']); + $list->setOrder(['ASC', 'ASC', 'ASC']); + + if ($filter) { + $array = explode(',', $filter); + $quotedArray = []; + $db = Db::get(); + foreach ($array as $a) { + $quotedArray[] = $db->quote($a); + } + $list->setCondition('id IN (' . implode(',', $quotedArray) . ')'); + } + + $result = []; + foreach ($list->getUnits() as $unit) { + try { + if ($unit->getAbbreviation()) { + $unit->setAbbreviation(Translation::getByKeyLocalized($unit->getAbbreviation(), Translation::DOMAIN_ADMIN, true, true)); + } + if ($unit->getLongname()) { + $unit->setLongname(Translation::getByKeyLocalized($unit->getLongname(), Translation::DOMAIN_ADMIN, true, true)); + } + $result[] = $unit->getObjectVars(); + } catch (Exception) { + // nothing to do + } + } + + return new GetQuantityValueUnitListResult($result, $list->getTotalCount()); + } +} diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListResult.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListResult.php new file mode 100644 index 00000000..e7d95451 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListResult.php @@ -0,0 +1,26 @@ +setOrder($order); + $list->setOrderKey($orderKey); + $list->setLimit($limit); + $list->setOffset($start); + + if ($filter) { + $condition = '1 = 1'; + $filters = json_decode($filter); + $db = Db::get(); + foreach ($filters as $f) { + if ($f->type === 'string') { + $condition .= ' AND ' . $db->quoteIdentifier($f->property) . ' LIKE ' . $db->quote('%' . $f->value . '%'); + } elseif ($f->type === 'numeric') { + $condition .= ' AND ' . $db->quoteIdentifier($f->property) . ' ' . $this->getOperator($f->comparison) . ' ' . $db->quote($f->value); + } + } + $list->setCondition($condition); + } + + $units = []; + foreach ($list->getUnits() as $u) { + $units[] = $u->getObjectVars(); + } + + return new GetQuantityValueUnitsResult($units, $list->getTotalCount()); + } + + private function getOperator(string $comparison): string + { + return match ($comparison) { + 'lt' => '<', + 'gt' => '>', + default => '=', + }; + } +} diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsResult.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsResult.php new file mode 100644 index 00000000..9f7e79ac --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsResult.php @@ -0,0 +1,26 @@ +service->importDefinitionFromJson($json); + } +} diff --git a/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitHandler.php b/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitHandler.php new file mode 100644 index 00000000..b4fee9e8 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitHandler.php @@ -0,0 +1,72 @@ +delete(); + + return new ManageQuantityValueUnitResult([]); + } + + if ($xaction === 'update') { + $unit = Unit::getById($data['id']); + if (!$unit) { + throw new NotFoundHttpException('Unit with id ' . $data['id'] . ' not found.'); + } + if (($data['baseunit'] ?? null) == -1) { + $data['baseunit'] = null; + } + $unit->setValues($data); + $unit->save(); + + return new ManageQuantityValueUnitResult($unit->getObjectVars()); + } + + if ($xaction === 'create') { + if (isset($data['baseunit']) && $data['baseunit'] === -1) { + $data['baseunit'] = null; + } + $id = $data['id']; + if (Unit::getById($id)) { + throw new BadRequestHttpException('unit with ID [' . $id . '] already exists'); + } + if (mb_strlen($id) > 50) { + throw new BadRequestHttpException('The maximal character length for the unit ID is 50 characters, the provided ID has ' . mb_strlen($id) . ' characters.'); + } + $unit = new Unit(); + $unit->setValues($data); + $unit->save(); + + return new ManageQuantityValueUnitResult($unit->getObjectVars()); + } + + throw new BadRequestHttpException('Unknown xaction: ' . $xaction); + } +} diff --git a/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitResult.php b/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitResult.php new file mode 100644 index 00000000..78e5d595 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $object = DataObject::getById($id); + + if (!$object) { + throw new NotFoundHttpException(sprintf('DataObject with id %d not found', $id)); + } + + if (!$object->isAllowed('publish')) { + throw new AccessDeniedHttpException('Missing permission to publish object'); + } + + $object->setValues($general); + $object->setUserModification($adminUser->getId()); + + $this->applyProperties($object, $propertiesData); + + $object->save(); + } + + private function applyProperties(DataObject\AbstractObject $object, ?array $propertiesData): void + { + if ($propertiesData === null) { + return; + } + + $properties = []; + + // preserve inherited properties + foreach ($object->getProperties() as $p) { + if ($p->isInherited()) { + $properties[$p->getName()] = $p; + } + } + + foreach ($propertiesData as $propertyName => $propertyData) { + $value = $propertyData['data']; + + try { + $property = new Model\Property(); + $property->setType($propertyData['type']); + $property->setName($propertyName); + $property->setCtype('object'); + $property->setDataFromEditmode($value); + $property->setInheritable($propertyData['inheritable']); + + $properties[$propertyName] = $property; + } catch (Exception) { + Logger::err("Can't add " . $propertyName . ' to object ' . $object->getRealFullPath()); + } + } + + $object->setProperties($properties); + } +} diff --git a/src/Handler/DataObject/SaveDataObjectHandler.php b/src/Handler/DataObject/SaveDataObjectHandler.php new file mode 100644 index 00000000..4bdad46b --- /dev/null +++ b/src/Handler/DataObject/SaveDataObjectHandler.php @@ -0,0 +1,90 @@ +userContext->getAdminUser(); + $object = DataObjectVersionHelper::resolveLatestDraft($objectFromDatabase, $adminUser?->getId()); + $object->setUserModification($adminUser->getId()); + + $objectFromVersion = $object !== $objectFromDatabase; + if ($objectFromVersion) { + if (method_exists($object, 'getLocalizedFields')) { + /** @var DataObject\Localizedfield $localizedFields */ + $localizedFields = $object->getLocalizedFields(); + $localizedFields->setLoadedAllLazyData(); + } + + // Mark fields that have changed as dirty + if ($payload->task !== 'autoSave' && $payload->task !== 'unpublish') { + foreach ($object->getClass()->getFieldDefinitions() as $fieldName => $fieldDefinition) { + $getter = 'get' . ucfirst($fieldName); + $oldValue = $objectFromDatabase->$getter(); + $newValue = $object->$getter(); + $isEqual = $fieldDefinition instanceof EqualComparisonInterface + ? $fieldDefinition->isEqual($oldValue, $newValue) + : $oldValue === $newValue; + + if (!$isEqual) { + $object->markFieldDirty($fieldName); + } + } + } + } + + if (($payload->task === 'unpublish' && !$object->isAllowed('unpublish')) || ($payload->task === 'publish' && !$object->isAllowed('publish'))) { + throw new AccessDeniedHttpException(); + } + + $this->mapper->applyPayload($payload, $object, $objectFromDatabase); + + if ($payload->task === 'session') { + $this->sessionService->saveObject($object); + + return new SaveDataObjectResult(modificationDate: 0, versionDate: 0, versionCount: 0, treeData: [], draftData: []); + } + + return $this->coordinator->save($object, $payload->task); + } +} diff --git a/src/Handler/DataObject/SaveDataObjectResult.php b/src/Handler/DataObject/SaveDataObjectResult.php new file mode 100644 index 00000000..95eaa332 --- /dev/null +++ b/src/Handler/DataObject/SaveDataObjectResult.php @@ -0,0 +1,29 @@ +getClass(); + if ($class->getShowVariants()) { + $objectTypes = DataObject::$types; + } + } + + $objects = []; + $offset = $total = $filteredTotalCount = 0; + + if ($object->hasChildren($objectTypes)) { + $offset = $start; + + $filterForCondition = $filter; + $effectiveLimit = $limit; + if (!is_null($filterForCondition)) { + if (!str_ends_with($filterForCondition, '*')) { + $filterForCondition .= '*'; + } + $filterForCondition = str_replace('*', '%', $filterForCondition); + $effectiveLimit = 100; + } + + $childrenList = new DataObject\Listing(); + $childrenList->setCondition($this->buildChildrenCondition($object, $filterForCondition, $view)); + $childrenList->setLimit($effectiveLimit); + $childrenList->setOffset($offset); + + if ($object->getChildrenSortBy() === 'index') { + $childrenList->setOrderKey('objects.index ASC', false); + } else { + $childrenList->setOrderKey( + sprintf( + 'CAST(objects.%s AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci %s', + $object->getChildrenSortBy(), $object->getChildrenSortOrder() + ), + false + ); + } + $childrenList->setObjectTypes($objectTypes); + + $cv = $view ? ($this->elementService->getCustomViewById($view) ?? []) : []; + Element\Service::addTreeFilterJoins($cv, $childrenList); + + $beforeListLoadEvent = new GenericEvent($this, [ + 'list' => $childrenList, + 'context' => $requestQueryAll, + ]); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::OBJECT_LIST_BEFORE_LIST_LOAD); + + /** @var DataObject\Listing $childrenList */ + $childrenList = $beforeListLoadEvent->getArgument('list'); + + $result = ($this->childrenHandler)( + object: $object, + childrenList: $childrenList, + view: $view, + filter: $filter, + limit: $limit, + offset: $offset, + fromPaging: $fromPaging, + objectTypes: $objectTypes, + ); + + $objects = $result->objects; + $offset = $result->offset; + $limit = $result->limit; + $total = $result->total; + $filteredTotalCount = $result->filteredTotalCount; + $filter = $result->filter; + } + + $event = new GenericEvent($this, ['objects' => $objects]); + $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA); + $objects = $event->getArgument('objects'); + + return new GetDataObjectChildrenResult( + objects: $objects, + offset: $offset, + limit: $limit, + total: $total, + filteredTotalCount: $filteredTotalCount, + filter: $filter, + fromPaging: $fromPaging, + ); + } + + private function buildChildrenCondition(DataObject\AbstractObject $object, ?string $filter, ?string $view): string + { + $condition = "objects.parentId = '" . $object->getId() . "'"; + + if ($view) { + $cv = $this->elementService->getCustomViewById($view); + + if (!empty($cv['classes'])) { + $cvConditions = []; + $cvClasses = $cv['classes']; + foreach ($cvClasses as $key => $cvClass) { + $cvConditions[] = "objects.classId = '" . $key . "'"; + } + + $cvConditions[] = "objects.type = 'folder'"; + $condition .= ' AND (' . implode(' OR ', $cvConditions) . ')'; + } + } + + $adminUser = $this->userContext->getAdminUser(); + if (!$adminUser->isAdmin()) { + $userIds = $adminUser->getRoles(); + $currentUserId = $adminUser->getId(); + $userIds[] = $currentUserId; + + $inheritedPermission = $object->getDao()->isInheritingPermission('list', $userIds); + + $anyAllowedRowOrChildren = 'EXISTS(SELECT list FROM users_workspaces_object uwo WHERE userId IN (' . implode(',', $userIds) . ') AND list=1 AND LOCATE(CONCAT(objects.path,objects.key),cpath)=1 AND + NOT EXISTS(SELECT list FROM users_workspaces_object WHERE userId =' . $currentUserId . ' AND list=0 AND cpath = uwo.cpath))'; + $isDisallowedCurrentRow = 'EXISTS(SELECT list FROM users_workspaces_object WHERE userId IN (' . implode(',', $userIds) . ') AND cid = objects.id AND list=0)'; + + $condition .= ' AND IF(' . $anyAllowedRowOrChildren . ',1,IF(' . $inheritedPermission . ', ' . $isDisallowedCurrentRow . ' = 0, 0)) = 1'; + } + + if (!is_null($filter)) { + $db = Db::get(); + $condition .= ' AND CAST(objects.key AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ' . $db->quote($filter); + } + + return $condition; + } +} diff --git a/src/Handler/DataObject/UpdateDataObjectHandler.php b/src/Handler/DataObject/UpdateDataObjectHandler.php new file mode 100644 index 00000000..bfd2eab3 --- /dev/null +++ b/src/Handler/DataObject/UpdateDataObjectHandler.php @@ -0,0 +1,254 @@ +userContext->getAdminUser(); + if ($object instanceof DataObject\Concrete) { + $object->setOmitMandatoryCheck(true); + } + + // this prevents the user from renaming, relocating (actions in the tree) if the newest version isn't the published one + // the reason is that otherwise the content of the newer not published version will be overwritten + if ($object instanceof DataObject\Concrete) { + $latestVersion = $object->getLatestVersion(); + if ($latestVersion && $latestVersion->getData()->getModificationDate() != $object->getModificationDate()) { + throw new RuntimeException("You can't rename or relocate if there's a newer not published version"); + } + } + + $key = $values['key'] ?? null; + if ($key) { + $key = Service::getValidKey($key, 'object'); + } + + if ($object->isAllowed('settings')) { + if ($key) { + if ($object->isAllowed('rename')) { + $object->setKey($key); + } elseif ($key !== $object->getKey()) { + Logger::debug('prevented renaming object because of missing permissions '); + } + } + + if (!empty($values['parentId'])) { + $parent = DataObject::getById((int) $values['parentId']); + + //check if parent is changed + if ($object->getParentId() !== $parent->getId()) { + if (!$parent->isAllowed('create')) { + throw new AccessDeniedHttpException('Prevented moving object - no create permission on new parent '); + } + + $objectWithSamePath = DataObject::getByPath($parent->getRealFullPath() . '/' . $object->getKey()); + + if ($objectWithSamePath != null) { + throw new RuntimeException('prevented creating object because object with same path+key already exists'); + } + + if ($object->isLocked()) { + throw new RuntimeException('prevented moving object, because it is locked: ID: ' . $object->getId()); + } + + $object->setParentId($values['parentId']); + } + } + + if (array_key_exists('locked', $values)) { + $object->setLocked($values['locked']); + } + + $object->setModificationDate(time()); + $object->setUserModification($adminUser->getId()); + + $isIndexUpdate = isset($values['indices']); + + if ($isIndexUpdate) { + // Ensure the update sort index is already available in the postUpdate eventListener + $indexUpdate = is_int($values['indices']) ? $values['indices'] : $values['indices'][$object->getId()]; + $object->setIndex($indexUpdate); + } + + $object->save(); + + if ($isIndexUpdate) { + $this->updateIndexesOfObjectSiblings($object, $indexUpdate); + } + + return new UpdateDataObjectResult( + treeData: $this->elementService->getElementTreeNodeConfig($object), + ); + } + + if ($key && $object->isAllowed('rename')) { + $result = $this->dataObjectGridService->renameObject($object, $key); + if (!$result['success']) { + throw new RuntimeException($result['message'] ?? 'Failed to rename object'); + } + + return new UpdateDataObjectResult( + treeData: $this->elementService->getElementTreeNodeConfig($object), + ); + } + + Logger::debug('prevented update object because of missing permissions.'); + + // Return current tree data even when no changes were applied + return new UpdateDataObjectResult( + treeData: $this->elementService->getElementTreeNodeConfig($object), + ); + } + + private function updateIndexesOfObjectSiblings(DataObject\AbstractObject $updatedObject, int $newIndex): void + { + $fn = function () use ($updatedObject, $newIndex): void { + $list = new DataObject\Listing(); + $updatedObject->saveIndex($newIndex); + + // The cte and the limit are needed to order the data before the newIndex is set + $db = Db::get(); + $db->executeStatement( + 'UPDATE ' . $list->getDao()->getTableName() . ' o, + ( + SELECT newIndex, id + FROM ( + With cte As (SELECT `index`, id FROM ' . $list->getDao()->getTableName() . ' WHERE parentId = ? AND id != ? AND `type` IN (\'' . implode( + "','", [ + DataObject::OBJECT_TYPE_OBJECT, + DataObject::OBJECT_TYPE_VARIANT, + DataObject::OBJECT_TYPE_FOLDER, + ] + ) . '\') ORDER BY `index` LIMIT ' . $updatedObject->getParent()->getChildAmount([ + DataObject::OBJECT_TYPE_OBJECT, + DataObject::OBJECT_TYPE_VARIANT, + DataObject::OBJECT_TYPE_FOLDER, + ]) . ') + SELECT @n := IF(@n = ? - 1,@n + 2,@n + 1) AS newIndex, id + FROM cte, + (SELECT @n := -1) variable + ) tmp + ) order_table + SET o.index = order_table.newIndex + WHERE o.id=order_table.id', + [ + $updatedObject->getParentId(), + $updatedObject->getId(), + $newIndex, + ] + ); + + $siblings = $db->fetchAllAssociative( + 'SELECT id, modificationDate, versionCount, `key`, `index` FROM objects + WHERE parentId = ? AND id != ? AND `type` IN ("object", "variant", "folder") ORDER BY `index` ASC', + [$updatedObject->getParentId(), $updatedObject->getId()] + ); + $index = 0; + + foreach ($siblings as $sibling) { + if ($index === $newIndex) { + $index++; + } + + $this->updateLatestVersionIndex($sibling['id'], $index); + $index++; + + DataObject::clearDependentCacheByObjectId($sibling['id']); + } + }; + + $this->executeInsideTransaction($fn); + } + + private function updateLatestVersionIndex(int $objectId, int $newIndex): void + { + $object = DataObject\Concrete::getById($objectId); + + if ( + $object && + $object->getType() !== DataObject::OBJECT_TYPE_FOLDER && + $latestVersion = $object->getLatestVersion() + ) { + // don't renew references (which means loading the target elements) + // Not needed as we just save a new version with the updated index + $object = $latestVersion->loadData(false); + if ($newIndex !== $object->getIndex()) { + $object->setIndex($newIndex); + } + $latestVersion->save(); + } + } + + private function executeInsideTransaction(callable $fn): void + { + $maxRetries = 5; + for ($retries = 0; $retries < $maxRetries; $retries++) { + try { + Db::get()->beginTransaction(); + + $fn(); + + Db::get()->commit(); + + break; + } catch (Exception $e) { + Db::get()->rollBack(); + + // we try to start the transaction $maxRetries times again (deadlocks, ...) + if ($retries < ($maxRetries - 1)) { + $run = $retries + 1; + $waitTime = random_int(1, 5) * 100000; // microseconds + Logger::warn('Unable to finish transaction (' . $run . ". run) because of the following reason '" . $e->getMessage() . "'. --> Retrying in " . $waitTime . ' microseconds ... (' . ($run + 1) . ' of ' . $maxRetries . ')'); + + usleep($waitTime); // wait specified time until we restart the transaction + } else { + // if the transaction still fail after $maxRetries retries, we throw out the exception + Logger::error('Finally giving up restarting the same transaction again and again, last message: ' . $e->getMessage()); + + throw $e; + } + } + } + } +} diff --git a/src/Handler/DataObject/UpdateDataObjectResult.php b/src/Handler/DataObject/UpdateDataObjectResult.php new file mode 100644 index 00000000..47360661 --- /dev/null +++ b/src/Handler/DataObject/UpdateDataObjectResult.php @@ -0,0 +1,25 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException(); + } + + $allParams['folderId'] = $parentObject->getId(); + $allParams['classId'] = $parentObject->getClassId(); + + return new GetVariantsResult( + $this->dataObjectGridService->gridProxy($allParams, DataObject::OBJECT_TYPE_VARIANT, $requestedLanguage) + ); + } +} diff --git a/src/Handler/DataObject/Variants/GetVariantsResult.php b/src/Handler/DataObject/Variants/GetVariantsResult.php new file mode 100644 index 00000000..8f9130da --- /dev/null +++ b/src/Handler/DataObject/Variants/GetVariantsResult.php @@ -0,0 +1,25 @@ +dataObjectGridService->renameObject($object, $key), + ); + } +} diff --git a/src/Handler/DataObject/Variants/UpdateObjectKeyResult.php b/src/Handler/DataObject/Variants/UpdateObjectKeyResult.php new file mode 100644 index 00000000..a9deaca2 --- /dev/null +++ b/src/Handler/DataObject/Variants/UpdateObjectKeyResult.php @@ -0,0 +1,25 @@ +loadData(); + + if (!$object1 instanceof DataObject\AbstractObject) { + DataObject::setDoNotRestoreKeyAndPath(false); + throw new DataObjectNotFoundException($fromId); + } + + if (method_exists($object1, 'getLocalizedFields')) { + /** @var DataObject\Localizedfield $localizedFields1 */ + $localizedFields1 = $object1->getLocalizedFields(); + $localizedFields1->setLoadedAllLazyData(); + } + + $version2 = Version::getById($toId); + $object2 = $version2?->loadData(); + + if (!$object2 instanceof DataObject\AbstractObject) { + DataObject::setDoNotRestoreKeyAndPath(false); + throw new DataObjectNotFoundException($toId); + } + + if (method_exists($object2, 'getLocalizedFields')) { + /** @var DataObject\Localizedfield $localizedFields2 */ + $localizedFields2 = $object2->getLocalizedFields(); + $localizedFields2->setLoadedAllLazyData(); + } + + DataObject::setDoNotRestoreKeyAndPath(false); + + if (!$object1->isAllowed('versions') || !$object2->isAllowed('versions')) { + throw new AccessDeniedHttpException('Permission denied for version ids [' . $fromId . ', ' . $toId . ']'); + } + + return new DiffVersionsResult($object1, $version1, $object2, $version2); + } +} diff --git a/src/Handler/DataObject/Version/DiffVersionsResult.php b/src/Handler/DataObject/Version/DiffVersionsResult.php new file mode 100644 index 00000000..ae24ef18 --- /dev/null +++ b/src/Handler/DataObject/Version/DiffVersionsResult.php @@ -0,0 +1,31 @@ +loadData(); + + if (!$object instanceof DataObject\AbstractObject) { + DataObject::setDoNotRestoreKeyAndPath(false); + throw new DataObjectNotFoundException($versionId); + } + + if (method_exists($object, 'getLocalizedFields')) { + /** @var DataObject\Localizedfield $localizedFields */ + $localizedFields = $object->getLocalizedFields(); + $localizedFields->setLoadedAllLazyData(); + } + + DataObject::setDoNotRestoreKeyAndPath(false); + + if (!$object->isAllowed('versions')) { + throw new AccessDeniedHttpException('Permission denied for version id [' . $versionId . ']'); + } + + return new PreviewVersionResult($object, $version); + } +} diff --git a/src/Handler/DataObject/Version/PreviewVersionResult.php b/src/Handler/DataObject/Version/PreviewVersionResult.php new file mode 100644 index 00000000..924f8825 --- /dev/null +++ b/src/Handler/DataObject/Version/PreviewVersionResult.php @@ -0,0 +1,29 @@ +userContext->getAdminUser()?->getId() ?? 0; + $version = Version::getById($versionId); + $object = $version?->loadData(); + + if (!$object instanceof DataObject\AbstractObject) { + throw new DataObjectNotFoundException($versionId); + } + + $currentObject = DataObject::getById($object->getId()); + if (!$currentObject?->isAllowed('publish')) { + throw new AccessDeniedHttpException('Missing permission to publish object version'); + } + + $object->setPublished(true); + $object->setUserModification($userId); + $object->save(); + + $treeData = []; + $this->normalizer->normalize($object, $treeData, self::class); + + return new PublishVersionResult( + modificationDate: (int) $object->getModificationDate(), + treeData: $treeData, + ); + } +} diff --git a/src/Handler/DataObject/Version/PublishVersionResult.php b/src/Handler/DataObject/Version/PublishVersionResult.php new file mode 100644 index 00000000..61f7c16c --- /dev/null +++ b/src/Handler/DataObject/Version/PublishVersionResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $parentDocument = Document::getById($parentId); + + if (!$parentDocument || !$parentDocument->isAllowed('create')) { + throw new AccessDeniedHttpException('Prevented adding a document because of missing permissions'); + } + + $intendedPath = $parentDocument->getRealFullPath() . '/' . $key; + + if (Document\Service::pathExists($intendedPath)) { + throw new BadRequestHttpException( + sprintf('Prevented adding a document because document with same path+key [%s] already exists', $intendedPath) + ); + } + + $createValues = [ + 'userOwner' => $adminUser->getId(), + 'userModification' => $adminUser->getId(), + 'published' => false, + ]; + + $createValues['key'] = Service::getValidKey($key, 'document'); + + // determine template / controller from docType or translationsBaseDocument + $docType = Document\DocType::getById($docTypeId ?? ''); + + if ($docType) { + $createValues['template'] = $docType->getTemplate(); + $createValues['controller'] = $docType->getController(); + $createValues['staticGeneratorEnabled'] = $docType->getStaticGeneratorEnabled(); + } elseif ($translationsBaseDocumentId !== null) { + $translationsBaseDocument = Document::getById((int) $translationsBaseDocumentId); + if ($translationsBaseDocument instanceof Document\PageSnippet) { + $createValues['template'] = $translationsBaseDocument->getTemplate(); + $createValues['controller'] = $translationsBaseDocument->getController(); + } + } elseif (in_array($type, ['page', 'snippet', 'email'])) { + $createValues['controller'] = $this->defaultDocumentController; + } + + if ($inheritanceSource !== null) { + $createValues['contentMainDocumentId'] = $inheritanceSource; + } + + $document = match ($type) { + 'page' => $this->createPage($parentDocument, $createValues, $title, $name), + 'snippet' => Document\Snippet::create($parentDocument->getId(), $createValues), + 'email' => Document\Email::create($parentDocument->getId(), $createValues), + 'link' => Document\Link::create($parentDocument->getId(), $createValues), + 'hardlink' => Document\Hardlink::create($parentDocument->getId(), $createValues), + 'folder' => $this->createFolder($parentDocument, $createValues), + default => $this->createCustomType($type, $parentDocument, $createValues), + }; + + // link translation if translationsBaseDocument given + if ($translationsBaseDocumentId !== null) { + $translationsBaseDocument = Document::getById((int) $translationsBaseDocumentId); + if ($translationsBaseDocument) { + $properties = $translationsBaseDocument->getProperties(); + $properties = [...$properties, ...$document->getProperties()]; + $document->setProperties($properties); + $document->setProperty('language', 'text', $language, false, true); + $document->save(); + + $service = $this->serviceFactory->createDocumentService(); + $service->addTranslation($translationsBaseDocument, $document); + } + } + + return new AddDocumentResult($document); + } + + private function createPage(Document $parentDocument, array $createValues, ?string $title, ?string $name): Document\Page + { + $document = Document\Page::create($parentDocument->getId(), $createValues, false); + $document->setTitle((string) $title); + $document->setProperty('navigation_name', 'text', $name, false, false); + $document->save(); + + return $document; + } + + private function createFolder(Document $parentDocument, array $createValues): Document\Folder + { + $document = Document\Folder::create($parentDocument->getId(), $createValues); + $document->setPublished(true); + $document->save(); + + return $document; + } + + private function createCustomType(string $type, Document $parentDocument, array $createValues): Document + { + $classname = $this->documentClassResolver->resolve($type); + + if ($classname !== null && Tool::classExists($classname)) { + $document = $classname::create($parentDocument->getId(), $createValues); + $document->save(); + + return $document; + } + + Logger::debug("Unknown document type, can't add [ $type ] "); + + throw new BadRequestHttpException(sprintf("Unknown document type '%s'", $type)); + } +} diff --git a/src/Handler/Document/AddDocumentResult.php b/src/Handler/Document/AddDocumentResult.php new file mode 100644 index 00000000..66772bd7 --- /dev/null +++ b/src/Handler/Document/AddDocumentResult.php @@ -0,0 +1,26 @@ +setEditables([]); + $doc->setContentMainDocumentId($contentMainDocumentPath, true); + $doc->saveVersion(); + } +} \ No newline at end of file diff --git a/src/Handler/Document/ConvertDocumentHandler.php b/src/Handler/Document/ConvertDocumentHandler.php new file mode 100644 index 00000000..bb696b55 --- /dev/null +++ b/src/Handler/Document/ConvertDocumentHandler.php @@ -0,0 +1,61 @@ +getId(), $new); + + $props = $document->getObjectVars(); + foreach ($props as $name => $value) { + if (in_array($name, ['children', 'siblings', 'scheduledTasks', 'controller', 'template'])) { + continue; + } + $new->setValue($name, $value); + } + + if ($type === 'hardlink' || $type === 'folder') { + foreach (['name', 'title', 'target', 'exclude', 'class', 'anchor', 'parameters', 'relation', 'accesskey', 'tabindex'] as $propertyName) { + $new->removeProperty('navigation_' . $propertyName); + } + } + + $new->setType($type); + $new->save(); + } +} diff --git a/src/Handler/Document/Copy/ChildIdsResult.php b/src/Handler/Document/Copy/ChildIdsResult.php new file mode 100644 index 00000000..2bc3596c --- /dev/null +++ b/src/Handler/Document/Copy/ChildIdsResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser()?->getId() ?? 0; + $source = Document::getById($sourceId); + + if ($sourceParentId !== null && $targetParentId !== null) { + $sourceParent = Document::getById($sourceParentId) ?? throw new NotFoundHttpException('Source parent not found'); + $resolvedTargetParentId = $sessionParentId ?? $targetParentId; + $targetParent = Document::getById($resolvedTargetParentId) ?? throw new NotFoundHttpException('Target parent not found'); + $targetPath = preg_replace('@^' . $sourceParent->getRealFullPath() . '@', $targetParent . '/', $source->getRealPath()); + $target = Document::getByPath($targetPath); + } else { + $target = Document::getById($targetId); + } + + if (!$target instanceof Document) { + throw new NotFoundHttpException('Target document not found'); + } + + if (!$target->isAllowed('create')) { + Logger::error('could not execute copy/paste because of missing permissions on target [ ' . $targetId . ' ]'); + throw new AccessDeniedHttpException(); + } + + if ($source === null) { + throw new NotFoundHttpException('Source document not found'); + } + + if ($source instanceof Document\PageSnippet && $latestVersion = $source->getLatestVersion()) { + $source = $latestVersion->loadData(); + $source->setPublished(false); + } + + $documentService = $this->serviceFactory->createDocumentService(); + + if ($type === 'child') { + if ($language !== null && !Tool::isValidLanguage($language)) { + throw new BadRequestHttpException('Invalid language: ' . $language); + } + + $newDocument = $documentService->copyAsChild($target, $source, $enableInheritance, $resetIndex, $language); + + return new CopyDocumentResult($sourceId, $newDocument); + } + + if ($type === 'replace') { + $documentService->copyContents($target, $source); + } + + return new CopyDocumentResult($sourceId); + } +} diff --git a/src/Handler/Document/Copy/CopyDocumentResult.php b/src/Handler/Document/Copy/CopyDocumentResult.php new file mode 100644 index 00000000..bf3f158b --- /dev/null +++ b/src/Handler/Document/Copy/CopyDocumentResult.php @@ -0,0 +1,28 @@ +hasChildren()) { + return new ChildIdsResult([]); + } + + $list = new Document\Listing(); + $list->setCondition('`path` LIKE ?', [$list->escapeLike($document->getRealFullPath()) . '/%']); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('ASC'); + + return new ChildIdsResult($list->loadIdList()); + } +} diff --git a/src/Handler/Document/Copy/RewriteDocumentIdsHandler.php b/src/Handler/Document/Copy/RewriteDocumentIdsHandler.php new file mode 100644 index 00000000..2c93fd2b --- /dev/null +++ b/src/Handler/Document/Copy/RewriteDocumentIdsHandler.php @@ -0,0 +1,44 @@ +userContext->getAdminUser()?->getId() ?? 0; + $document = Document::getById($documentId); + + if ($document === null) { + return; + } + + $document = Document\Service::rewriteIds($document, ['document' => $idMapping], [ + 'enableInheritance' => $enableInheritance, + ]); + $document->setUserModification($userId); + $document->save(); + } +} diff --git a/src/Handler/Document/DeleteDocumentHandler.php b/src/Handler/Document/DeleteDocumentHandler.php new file mode 100644 index 00000000..da9a4b7c --- /dev/null +++ b/src/Handler/Document/DeleteDocumentHandler.php @@ -0,0 +1,73 @@ +setCondition('`path` LIKE ?', [$list->escapeLike($parentDocument->getRealFullPath()) . '/%']); + $list->setLimit($amount); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('DESC'); + + $documents = $list->load(); + + $deletedItems = []; + foreach ($documents as $document) { + $deletedItems[$document->getId()] = $document->getRealFullPath(); + if ($document->isAllowed('delete') && !$document->isLocked()) { + $document->delete(); + } + } + + return new DeleteDocumentResult($deletedItems); + } + + $document = Document::getById($id); + + if (!$document) { + throw new NotFoundHttpException('Document not found'); + } + + if (!$document->isAllowed('delete')) { + throw new AccessDeniedHttpException('Access denied: missing delete permission'); + } + + if ($document->isLocked()) { + throw new RuntimeException('Prevented deleting document, because it is locked: ID: ' . $document->getId()); + } + + $document->delete(); + + return new DeleteDocumentResult(); + } +} diff --git a/src/Handler/Document/DeleteDocumentResult.php b/src/Handler/Document/DeleteDocumentResult.php new file mode 100644 index 00000000..b11f38a9 --- /dev/null +++ b/src/Handler/Document/DeleteDocumentResult.php @@ -0,0 +1,24 @@ +isAllowed('save') || $email->isAllowed('publish') || $email->isAllowed('unpublish') || $email->isAllowed('delete')) { + $this->editLockService->checkAndAcquire($email->getId(), 'document', AdminEvents::DOCUMENT_GET_IS_LOCKED, $email); + } + + $email = clone $email; + $draftVersion = null; + $email = DocumentVersionHelper::resolveLatestDraft($email, $draftVersion, $this->userContext->getAdminUser()?->getId()); + + $versions = Element\Service::getSafeVersionInfo($email->getVersions()); + $email->setVersions(array_splice($versions, -1, 1)); + $email->setParent(null); + + // unset useless data + $email->setEditables(null); + $email->setChildren(null); + + $data = $email->getObjectVars(); + $data['locked'] = $email->isLocked(); + $data['url'] = $email->getUrl(); + + $this->normalizer->normalize($email, $data, self::class, ['draftVersion' => $draftVersion]); + + return new GetEmailDataResult(email: $email, data: $data, draftVersion: $draftVersion); + } +} diff --git a/src/Handler/Document/Email/GetEmailDataResult.php b/src/Handler/Document/Email/GetEmailDataResult.php new file mode 100644 index 00000000..ca567f34 --- /dev/null +++ b/src/Handler/Document/Email/GetEmailDataResult.php @@ -0,0 +1,30 @@ +sessionService->getDocument($email); + if ($sessionEmail instanceof Email) { + $email = $sessionEmail; + } else { + $email = DocumentVersionHelper::resolveLatestDraft($email, userId: $this->userContext->getAdminUser()?->getId()); + } + } + + $this->mapper->applyPagePayload($payload, $email); + + $result = $this->coordinator->save($email, $payload->task); + + if ($sessionAware) { + $this->sessionService->saveDocument($result->document); + } + + return new SaveEmailResult( + email: $result->document instanceof Email ? $result->document : $email, + task: $result->task, + version: $result->version, + treeData: $result->treeData, + ); + } +} diff --git a/src/Handler/Document/Email/SaveEmailResult.php b/src/Handler/Document/Email/SaveEmailResult.php new file mode 100644 index 00000000..20b595ae --- /dev/null +++ b/src/Handler/Document/Email/SaveEmailResult.php @@ -0,0 +1,31 @@ +setParent(null); + + $data = $folder->getObjectVars(); + $data['locked'] = $folder->isLocked(); + + $this->normalizer->normalize($folder, $data, self::class); + + return new GetFolderDataResult(folder: $folder, data: $data); + } +} diff --git a/src/Handler/Document/Folder/GetFolderDataResult.php b/src/Handler/Document/Folder/GetFolderDataResult.php new file mode 100644 index 00000000..5cee7cd6 --- /dev/null +++ b/src/Handler/Document/Folder/GetFolderDataResult.php @@ -0,0 +1,28 @@ +mapper->applyFolderPayload($payload, $folder); + $result = $this->coordinator->save($folder, 'publish'); + + return new SaveFolderResult( + folder: $result->document instanceof Folder ? $result->document : $folder, + treeData: $result->treeData, + ); + } +} diff --git a/src/Handler/Document/Folder/SaveFolderResult.php b/src/Handler/Document/Folder/SaveFolderResult.php new file mode 100644 index 00000000..c699be9b --- /dev/null +++ b/src/Handler/Document/Folder/SaveFolderResult.php @@ -0,0 +1,28 @@ +setFilter(static fn (DocType $docType) => $docType->getType() === $type); + } + + $docTypes = []; + foreach ($list->getDocTypes() as $docType) { + $docTypes[] = $docType->getObjectVars(); + } + + return new GetDocTypesByTypeResult($docTypes); + } +} diff --git a/src/Handler/Document/GetDocTypesByTypeResult.php b/src/Handler/Document/GetDocTypesByTypeResult.php new file mode 100644 index 00000000..9a2e3073 --- /dev/null +++ b/src/Handler/Document/GetDocTypesByTypeResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $list = new DocType\Listing(); + + $docTypes = []; + foreach ($list->getDocTypes() as $type) { + if ($adminUser->isAllowed($type->getId(), 'docType')) { + $data = $type->getObjectVars(); + $data['writeable'] = $type->isWriteable(); + $docTypes[] = $data; + } + } + + return new GetDocTypesListResult($docTypes, count($docTypes)); + } +} diff --git a/src/Handler/Document/GetDocTypesListResult.php b/src/Handler/Document/GetDocTypesListResult.php new file mode 100644 index 00000000..28a0c175 --- /dev/null +++ b/src/Handler/Document/GetDocTypesListResult.php @@ -0,0 +1,26 @@ +load(); + + $documents = []; + foreach ($childrenList as $childDocument) { + $documentTreeNode = $this->elementService->getElementTreeNodeConfig($childDocument); + // the !isset is for printContainer case, there are no permissions set there + if (!isset($documentTreeNode['permissions']['list']) || $documentTreeNode['permissions']['list'] == 1) { + $documents[] = $documentTreeNode; + } + } + + return new GetDocumentChildrenResult($documents); + } +} diff --git a/src/Handler/Document/GetDocumentChildrenResult.php b/src/Handler/Document/GetDocumentChildrenResult.php new file mode 100644 index 00000000..3c170186 --- /dev/null +++ b/src/Handler/Document/GetDocumentChildrenResult.php @@ -0,0 +1,24 @@ +isAllowed('view')) { + throw new AccessDeniedHttpException(); + } + + if ($document->isAllowed('save') || $document->isAllowed('publish') || $document->isAllowed('unpublish') || $document->isAllowed('delete')) { + $this->editLockService->checkAndAcquire($document->getId(), 'document', AdminEvents::DOCUMENT_GET_IS_LOCKED, $document); + } + + $document = clone $document; + $data = $document->getObjectVars(); + + $this->normalizer->normalize($document, $data, self::class); + + $event = new GenericEvent($this, ['data' => $data, 'document' => $document]); + $this->eventDispatcher->dispatch($event, AdminEvents::DOCUMENT_GET_PRE_SEND_DATA); + $data = $event->getArgument('data'); + + return new GetDocumentDataResult($data); + } +} diff --git a/src/Handler/Document/GetDocumentDataResult.php b/src/Handler/Document/GetDocumentDataResult.php new file mode 100644 index 00000000..df3bd7be --- /dev/null +++ b/src/Handler/Document/GetDocumentDataResult.php @@ -0,0 +1,24 @@ +getId(), $document->getType()); + } +} diff --git a/src/Handler/Document/GetDocumentIdForPathResult.php b/src/Handler/Document/GetDocumentIdForPathResult.php new file mode 100644 index 00000000..1896f870 --- /dev/null +++ b/src/Handler/Document/GetDocumentIdForPathResult.php @@ -0,0 +1,26 @@ +isAllowed('save') || $link->isAllowed('publish') || $link->isAllowed('unpublish') || $link->isAllowed('delete')) { + $this->editLockService->checkAndAcquire($link->getId(), 'document', AdminEvents::DOCUMENT_GET_IS_LOCKED, $link); + } + + $cloned = clone $link; + $cloned->setParent(null); + + $data = $cloned->getObjectVars(); + $data['locked'] = $cloned->isLocked(); + $data['scheduledTasks'] = array_map( + static fn (Task $task) => $task->getObjectVars(), + $cloned->getScheduledTasks() + ); + + if ($cloned->getSourceDocument()) { + $data['sourcePath'] = $cloned->getSourceDocument()->getRealFullPath(); + } + + $this->normalizer->normalize($cloned, $data, self::class); + + return new GetHardlinkDataResult(original: $link, link: $cloned, data: $data); + } +} diff --git a/src/Handler/Document/Hardlink/GetHardlinkDataResult.php b/src/Handler/Document/Hardlink/GetHardlinkDataResult.php new file mode 100644 index 00000000..7d25d7de --- /dev/null +++ b/src/Handler/Document/Hardlink/GetHardlinkDataResult.php @@ -0,0 +1,31 @@ +mapper->applyHardlinkPayload($payload, $link); + $result = $this->coordinator->save($link, $payload->task); + + return new SaveHardlinkResult( + link: $result->document instanceof Hardlink ? $result->document : $link, + task: $result->task, + treeData: $result->treeData, + ); + } +} diff --git a/src/Handler/Document/Hardlink/SaveHardlinkResult.php b/src/Handler/Document/Hardlink/SaveHardlinkResult.php new file mode 100644 index 00000000..c80b74bb --- /dev/null +++ b/src/Handler/Document/Hardlink/SaveHardlinkResult.php @@ -0,0 +1,29 @@ +isAllowed('save') || $link->isAllowed('publish') || $link->isAllowed('unpublish') || $link->isAllowed('delete')) { + $this->editLockService->checkAndAcquire($link->getId(), 'document', AdminEvents::DOCUMENT_GET_IS_LOCKED, $link); + } + + $cloned = clone $link; + $cloned->setElement(null); + $cloned->setParent(null); + + $data = $this->serializer->serialize($cloned->getObjectVars(), 'json', []); + $data = json_decode($data, true); + $data['locked'] = $cloned->isLocked(); + $data['rawHref'] = $cloned->getRawHref(); + $data['scheduledTasks'] = array_map( + static fn (Task $task) => $task->getObjectVars(), + $cloned->getScheduledTasks() + ); + + $this->normalizer->normalize($cloned, $data, self::class); + + return new GetLinkDataResult(original: $link, link: $cloned, data: $data); + } +} diff --git a/src/Handler/Document/Link/GetLinkDataResult.php b/src/Handler/Document/Link/GetLinkDataResult.php new file mode 100644 index 00000000..1517eca6 --- /dev/null +++ b/src/Handler/Document/Link/GetLinkDataResult.php @@ -0,0 +1,31 @@ +mapper->applyLinkPayload($payload, $link); + $result = $this->coordinator->save($link, $payload->task); + + return new SaveLinkResult( + link: $result->document instanceof Link ? $result->document : $link, + task: $result->task, + treeData: $result->treeData, + ); + } +} diff --git a/src/Handler/Document/Link/SaveLinkResult.php b/src/Handler/Document/Link/SaveLinkResult.php new file mode 100644 index 00000000..9abc55fb --- /dev/null +++ b/src/Handler/Document/Link/SaveLinkResult.php @@ -0,0 +1,29 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + $type->delete(); + + return new ManageDocTypesResult(data: []); + } + + if ($xaction === 'update') { + $type = DocType::getById($data['id']); + if (!$type->isWriteable()) { + throw new ConfigWriteException(); + } + $type->setValues($data); + $type->save(); + $responseData = $type->getObjectVars(); + $responseData['writeable'] = $type->isWriteable(); + + return new ManageDocTypesResult(data: $responseData); + } + + if ($xaction === 'create') { + if (!(new DocType())->isWriteable()) { + throw new ConfigWriteException(); + } + unset($data['id']); + $type = DocType::create(); + $type->setValues($data); + $type->save(); + $responseData = $type->getObjectVars(); + $responseData['writeable'] = $type->isWriteable(); + + return new ManageDocTypesResult(data: $responseData); + } + + throw new BadRequestHttpException('Unknown xaction: ' . $xaction); + } +} diff --git a/src/Handler/Document/ManageDocTypesResult.php b/src/Handler/Document/ManageDocTypesResult.php new file mode 100644 index 00000000..a93dbfb8 --- /dev/null +++ b/src/Handler/Document/ManageDocTypesResult.php @@ -0,0 +1,25 @@ +setCondition('(CONCAT(`path`, `key`) = ? OR id IN (SELECT id from documents_page WHERE prettyUrl = ?)) + AND id != ?', [ + $path, $path, $docId, + ]); + + if ($list->getTotalCount() > 0) { + $checkDocument = Document::getById($docId); + $checkSite = Frontend::getSiteForDocument($checkDocument); + $checkSiteId = empty($checkSite) ? 0 : $checkSite->getId(); + + foreach ($list as $document) { + if (empty($document)) { + continue; + } + + $site = Frontend::getSiteForDocument($document); + $siteId = empty($site) ? 0 : $site->getId(); + + if ($siteId === $checkSiteId) { + $success = false; + $messages[] = 'URL path already exists.'; + + break; + } + } + } + } + + return new CheckPrettyUrlResult(success: $success, messages: $messages); + } +} diff --git a/src/Handler/Document/Page/CheckPrettyUrlResult.php b/src/Handler/Document/Page/CheckPrettyUrlResult.php new file mode 100644 index 00000000..86796f0b --- /dev/null +++ b/src/Handler/Document/Page/CheckPrettyUrlResult.php @@ -0,0 +1,27 @@ +setCondition('`type` = ?', ['page']); + + // @todo: this seems completely wrong. + foreach ($list->loadIdList() as $docId) { + $this->messengerBusOpendxpCore->dispatch( + new GeneratePagePreviewMessage($docId, \OpenDxp\Tool::getHostUrl()) + ); + + break; + } + } +} diff --git a/src/Handler/Document/Page/GenerateQrCodeHandler.php b/src/Handler/Document/Page/GenerateQrCodeHandler.php new file mode 100644 index 00000000..73dc7d9a --- /dev/null +++ b/src/Handler/Document/Page/GenerateQrCodeHandler.php @@ -0,0 +1,48 @@ +getUrl(); + + $result = Builder::create() + ->writer(new PngWriter()) + ->data($url) + ->size($download ? 4000 : 500) + ->build(); + + $tmpFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/qr-code-' . uniqid('', false) . '.png'; + $result->saveToFile($tmpFile); + + return $tmpFile; + } +} diff --git a/src/Handler/Document/Page/GetPageDataHandler.php b/src/Handler/Document/Page/GetPageDataHandler.php new file mode 100644 index 00000000..70e82297 --- /dev/null +++ b/src/Handler/Document/Page/GetPageDataHandler.php @@ -0,0 +1,84 @@ +isAllowed('save') || $page->isAllowed('publish') || $page->isAllowed('unpublish') || $page->isAllowed('delete')) { + $this->editLockService->checkAndAcquire($page->getId(), 'document', AdminEvents::DOCUMENT_GET_IS_LOCKED, $page); + } + + $page = clone $page; + $draftVersion = null; + $page = DocumentVersionHelper::resolveLatestDraft($page, $draftVersion, $this->userContext->getAdminUser()?->getId()); + + $pageVersions = Element\Service::getSafeVersionInfo($page->getVersions()); + $page->setVersions(array_splice($pageVersions, -1, 1)); + $page->setParent(null); + + // unset useless data + $page->setEditables(null); + $page->setChildren(null); + + $data = $page->getObjectVars(); + $data['locked'] = $page->isLocked(); + + if ($page->getContentMainDocument()) { + $data['contentMainDocumentPath'] = $page->getContentMainDocument()->getRealFullPath(); + } + + if ($page->getStaticGeneratorEnabled()) { + $data['staticLastGenerated'] = $this->staticPageGenerator->getLastModified($page); + } + + $data['url'] = $page->getUrl(); + $data['scheduledTasks'] = array_map( + static fn (Task $task) => $task->getObjectVars(), + $page->getScheduledTasks() + ); + + $this->normalizer->normalize($page, $data, self::class, ['draftVersion' => $draftVersion]); + + return new GetPageDataResult(page: $page, data: $data, draftVersion: $draftVersion); + } +} diff --git a/src/Handler/Document/Page/GetPageDataResult.php b/src/Handler/Document/Page/GetPageDataResult.php new file mode 100644 index 00000000..6fef999d --- /dev/null +++ b/src/Handler/Document/Page/GetPageDataResult.php @@ -0,0 +1,30 @@ +getPreviewImageFilesystemPath(); + } +} diff --git a/src/Handler/Document/Page/RenderAreabrickIndexEditmodeHandler.php b/src/Handler/Document/Page/RenderAreabrickIndexEditmodeHandler.php new file mode 100644 index 00000000..0d8d015a --- /dev/null +++ b/src/Handler/Document/Page/RenderAreabrickIndexEditmodeHandler.php @@ -0,0 +1,63 @@ +documentId); + if (!$document) { + throw new NotFoundHttpException(); + } + + $document = clone $document; + $document->setEditables([]); + + $this->blockStateStack->loadArray($payload->blockStateStack); + $this->localeService->setLocale($document->getProperty('language')); + + /** @var Document\Editable\Areablock $areablock */ + $areablock = $this->editableRenderer->getEditable($document, 'areablock', $payload->realName, $payload->areaBlockConfig, true); + $areablock->setRealName($payload->realName); + $areablock->setEditmode(true); + $areablock->setDataFromEditmode($payload->areaBrickData); + $htmlCode = trim($areablock->renderIndex($payload->index, true)); + + return new RenderAreabrickIndexEditmodeResult( + document: $document, + editableDefinitions: $this->definitionCollector->getDefinitions(), + htmlCode: $htmlCode, + ); + } +} \ No newline at end of file diff --git a/src/Handler/Document/Page/RenderAreabrickIndexEditmodeResult.php b/src/Handler/Document/Page/RenderAreabrickIndexEditmodeResult.php new file mode 100644 index 00000000..9a452945 --- /dev/null +++ b/src/Handler/Document/Page/RenderAreabrickIndexEditmodeResult.php @@ -0,0 +1,29 @@ +getEditables() as $editable) { + // remove all but target group data + // Hardcoded the TARGET_GROUP_EDITABLE_PREFIX prefix here as we shouldn't remove the bundle specific editables even if bundle is not enabled/installed + if (!preg_match('/^' . preg_quote('persona_ -', '/') . '/', $editable->getName())) { + $doc->removeEditable($editable->getName()); + } + } + + $this->sessionService->saveDocument($doc, useForSave: true); + } +} diff --git a/src/Handler/Document/Page/SavePageHandler.php b/src/Handler/Document/Page/SavePageHandler.php new file mode 100644 index 00000000..cf989073 --- /dev/null +++ b/src/Handler/Document/Page/SavePageHandler.php @@ -0,0 +1,82 @@ +sessionService->getDocument($oldPage); + if ($sessionPage instanceof Page) { + $page = $sessionPage; + } else { + $page = DocumentVersionHelper::resolveLatestDraft($oldPage, userId: $this->userContext->getAdminUser()?->getId()); + } + } else { + $page = $oldPage; + } + + $this->mapper->applyPagePayload($payload, $page); + + $result = $this->coordinator->save($page, $payload->task); + + $this->dispatchEvent( + new DocumentEvent($result->document, ['oldPage' => $oldPage, 'task' => $result->task]), + DocumentEvents::PAGE_POST_SAVE_ACTION, + ); + + if ($sessionAware && !in_array($payload->task, ['publish', 'unpublish'], true)) { + $this->sessionService->saveDocument($result->document); + } + + return new SavePageResult( + page: $result->document instanceof Page ? $result->document : $page, + oldPage: $oldPage, + task: $result->task, + version: $result->version, + treeData: $result->treeData, + ); + } +} diff --git a/src/Handler/Document/Page/SavePageResult.php b/src/Handler/Document/Page/SavePageResult.php new file mode 100644 index 00000000..14d58d0d --- /dev/null +++ b/src/Handler/Document/Page/SavePageResult.php @@ -0,0 +1,32 @@ +id && $payload->type) { + $element = Service::getElementById($payload->type, $payload->id); + } + + if (!$element instanceof ElementInterface) { + throw new NotFoundHttpException(sprintf('Element with type %s and ID %d was not found', $payload->type ?? 'null', $payload->id ?? 0)); + } + + if (!$element->isAllowed('view')) { + throw new AccessDeniedHttpException(sprintf('Access to element with type %s and ID %d is not allowed', $payload->type, $payload->id)); + } + + $this->eventDispatcher->dispatch( + new GenericEvent(null, ['requestParams' => $payload->query, 'element' => $element]), + DocumentEvents::EDITABLE_RENDERLET_PRE_RENDER, + ); + + $attributes = []; + + if ($payload->parentDocumentId) { + $document = Document\PageSnippet::getById((int) $payload->parentDocumentId); + if ($document) { + $attributes = $this->actionRenderer->addDocumentAttributes($document, $attributes); + unset($attributes[DynamicRouter::CONTENT_TEMPLATE]); + } + } + + if ($payload->template) { + $attributes[DynamicRouter::CONTENT_TEMPLATE] = $payload->template; + } + + $query = $payload->query; + foreach (['controller', 'action', 'module', 'bundle'] as $key) { + unset($query[$key]); + } + + if (isset($attributes['_locale'])) { + $this->localeService->setLocale($attributes['_locale']); + } + + return new RenderRenderletResult( + html: $this->editableHandler->renderAction($payload->controller, $attributes, $query), + ); + } +} \ No newline at end of file diff --git a/src/Handler/Document/Renderlet/RenderRenderletResult.php b/src/Handler/Document/Renderlet/RenderRenderletResult.php new file mode 100644 index 00000000..809a007f --- /dev/null +++ b/src/Handler/Document/Renderlet/RenderRenderletResult.php @@ -0,0 +1,25 @@ +eventDispatcher->dispatch($event, AdminEvents::SITE_CUSTOM_SETTINGS); + + return new GetSiteCustomSettingsResult($event->getConfigNodes()); + } +} diff --git a/src/Handler/Document/Site/GetSiteCustomSettingsResult.php b/src/Handler/Document/Site/GetSiteCustomSettingsResult.php new file mode 100644 index 00000000..cf2d6b59 --- /dev/null +++ b/src/Handler/Document/Site/GetSiteCustomSettingsResult.php @@ -0,0 +1,25 @@ +delete(); + } +} diff --git a/src/Handler/Document/Snippet/GetSnippetDataHandler.php b/src/Handler/Document/Snippet/GetSnippetDataHandler.php new file mode 100644 index 00000000..f1f2ec5c --- /dev/null +++ b/src/Handler/Document/Snippet/GetSnippetDataHandler.php @@ -0,0 +1,74 @@ +isAllowed('save') || $snippet->isAllowed('publish') || $snippet->isAllowed('unpublish') || $snippet->isAllowed('delete')) { + $this->editLockService->checkAndAcquire($snippet->getId(), 'document', AdminEvents::DOCUMENT_GET_IS_LOCKED, $snippet); + } + + $snippet = clone $snippet; + $draftVersion = null; + $snippet = DocumentVersionHelper::resolveLatestDraft($snippet, $draftVersion, $this->userContext->getAdminUser()?->getId()); + + $versions = Element\Service::getSafeVersionInfo($snippet->getVersions()); + $snippet->setVersions(array_splice($versions, -1, 1)); + $snippet->setParent(null); + $snippet->setEditables(null); + + $data = $snippet->getObjectVars(); + $data['locked'] = $snippet->isLocked(); + $data['url'] = $snippet->getUrl(); + $data['scheduledTasks'] = array_map( + static fn (Task $task) => $task->getObjectVars(), + $snippet->getScheduledTasks() + ); + + if ($snippet->getContentMainDocument()) { + $data['contentMainDocumentPath'] = $snippet->getContentMainDocument()->getRealFullPath(); + } + + $this->normalizer->normalize($snippet, $data, self::class, ['draftVersion' => $draftVersion]); + + return new GetSnippetDataResult(snippet: $snippet, data: $data, draftVersion: $draftVersion); + } +} diff --git a/src/Handler/Document/Snippet/GetSnippetDataResult.php b/src/Handler/Document/Snippet/GetSnippetDataResult.php new file mode 100644 index 00000000..fa1b498f --- /dev/null +++ b/src/Handler/Document/Snippet/GetSnippetDataResult.php @@ -0,0 +1,30 @@ +sessionService->getDocument($snippet); + if ($sessionSnippet instanceof Snippet) { + $snippet = $sessionSnippet; + } else { + $snippet = DocumentVersionHelper::resolveLatestDraft($snippet, userId: $this->userContext->getAdminUser()?->getId()); + } + } + + $this->mapper->applyPagePayload($payload, $snippet); + + $result = $this->coordinator->save($snippet, $payload->task); + + if ($sessionAware) { + $this->sessionService->saveDocument($result->document); + } + + return new SaveSnippetResult( + snippet: $result->document instanceof Snippet ? $result->document : $snippet, + task: $result->task, + version: $result->version, + treeData: $result->treeData, + ); + } +} diff --git a/src/Handler/Document/Snippet/SaveSnippetResult.php b/src/Handler/Document/Snippet/SaveSnippetResult.php new file mode 100644 index 00000000..ec425da0 --- /dev/null +++ b/src/Handler/Document/Snippet/SaveSnippetResult.php @@ -0,0 +1,31 @@ +getProperty('language'))) { + throw new Exception(sprintf('Source Document(ID:%s) Language(Properties) missing', $sourceDocument->getId())); + } + + if (empty($targetDocument->getProperty('language'))) { + throw new Exception(sprintf('Target Document(ID:%s) Language(Properties) missing', $targetDocument->getId())); + } + + $service = $this->serviceFactory->createDocumentService(); + if ($service->getTranslationSourceId($targetDocument) != $targetDocument->getId()) { + throw new Exception('Target Document already linked to Source Document ID(' . $service->getTranslationSourceId($targetDocument) . '). Please unlink existing relation first.'); + } + + $service->addTranslation($sourceDocument, $targetDocument); + } +} diff --git a/src/Handler/Document/Translation/CheckTranslationLanguageHandler.php b/src/Handler/Document/Translation/CheckTranslationLanguageHandler.php new file mode 100644 index 00000000..b4f0f31d --- /dev/null +++ b/src/Handler/Document/Translation/CheckTranslationLanguageHandler.php @@ -0,0 +1,41 @@ +getProperty('language'); + $found = !empty($language); + $translationLinks = array_keys($this->serviceFactory->createDocumentService()->getTranslations($document)); + + return new CheckTranslationLanguageResult($found, $language ?: null, $translationLinks); + } +} diff --git a/src/Handler/Document/Translation/CheckTranslationLanguageResult.php b/src/Handler/Document/Translation/CheckTranslationLanguageResult.php new file mode 100644 index 00000000..71963729 --- /dev/null +++ b/src/Handler/Document/Translation/CheckTranslationLanguageResult.php @@ -0,0 +1,26 @@ +serviceFactory->createDocumentService(); + $document = $document->getId() === 1 ? $document : $document->getParent(); + + $translations = $service->getTranslations($document); + if (isset($translations[$language])) { + $targetDocument = Document::getById($translations[$language]); + + return new DetermineTranslationParentResult( + true, + $targetDocument?->getRealFullPath(), + $targetDocument?->getId(), + ); + } + + return new DetermineTranslationParentResult(false, null, null); + } +} diff --git a/src/Handler/Document/Translation/DetermineTranslationParentResult.php b/src/Handler/Document/Translation/DetermineTranslationParentResult.php new file mode 100644 index 00000000..a1a9c9b8 --- /dev/null +++ b/src/Handler/Document/Translation/DetermineTranslationParentResult.php @@ -0,0 +1,26 @@ +getChildren() as $child) { + $nodes[] = $this->getTranslationTreeNodeConfig($child, $languages); + } + + return new GetLanguageTreeResult($nodes); + } + + private function getTranslationTreeNodeConfig(Document $document, array $languages, ?array $translations = null): array + { + $service = $this->serviceFactory->createDocumentService(); + $adminUser = $this->userContext->getAdminUser(); + + $config = $this->elementService->getElementTreeNodeConfig($document); + + $translations = $translations ?? $service->getTranslations($document); + + foreach ($languages as $language) { + if ($languageDocumentId = $translations[$language] ?? false) { + $languageDocument = Document::getById((int) $languageDocumentId); + $config[$language] = [ + 'text' => $languageDocument->getKey(), + 'id' => $languageDocument->getId(), + 'type' => $languageDocument->getType(), + 'fullPath' => $languageDocument->getFullPath(), + 'published' => $languageDocument->getPublished(), + 'itemType' => 'document', + 'permissions' => $languageDocument->getUserPermissions($adminUser), + ]; + } elseif (!$document instanceof Document\Folder) { + $config[$language] = [ + 'text' => '--', + 'itemType' => 'empty', + ]; + } + } + + return $config; + } +} diff --git a/src/Handler/Document/Translation/GetLanguageTreeResult.php b/src/Handler/Document/Translation/GetLanguageTreeResult.php new file mode 100644 index 00000000..f7bce8a6 --- /dev/null +++ b/src/Handler/Document/Translation/GetLanguageTreeResult.php @@ -0,0 +1,24 @@ +serviceFactory->createDocumentService(); + $adminUser = $this->userContext->getAdminUser(); + + $locales = Tool::getSupportedLocales(); + + $lang = $document->getProperty('language'); + + $columns = [ + [ + 'xtype' => 'treecolumn', + 'text' => $lang ? $locales[$lang] : '', + 'dataIndex' => 'text', + 'cls' => $lang ? 'x-column-header_' . strtolower($lang) : null, + 'width' => 300, + 'sortable' => false, + ], + ]; + + $translations = $service->getTranslations($document); + + $combinedTranslations = $translations; + + if ($parentDocument = $document->getParent()) { + $parentTranslations = $service->getTranslations($parentDocument); + foreach ($parentTranslations as $language => $languageDocumentId) { + $combinedTranslations[$language] = $translations[$language] ?? $languageDocumentId; + } + } + + foreach ($combinedTranslations as $language => $languageDocumentId) { + $languageDocument = Document::getById($languageDocumentId); + + if ($languageDocument && $languageDocument->isAllowed('list') && $language != $document->getProperty('language')) { + $columns[] = [ + 'text' => $locales[$language], + 'dataIndex' => $language, + 'cls' => 'x-column-header_' . strtolower($language), + 'width' => 300, + 'sortable' => false, + ]; + } + } + + $root = $this->getTranslationTreeNodeConfig($document, array_keys($translations), $translations, $adminUser); + + return new GetLanguageTreeRootResult( + root: $root, + columns: $columns, + languages: array_keys($translations), + ); + } + + private function getTranslationTreeNodeConfig( + Document $document, + array $languages, + ?array $translations, + ?User $adminUser, + ): array { + $service = $this->serviceFactory->createDocumentService(); + + $config = $this->elementService->getElementTreeNodeConfig($document); + + $translations = $translations ?? $service->getTranslations($document); + + foreach ($languages as $language) { + if ($languageDocumentId = $translations[$language] ?? false) { + $languageDocument = Document::getById((int) $languageDocumentId); + $config[$language] = [ + 'text' => $languageDocument->getKey(), + 'id' => $languageDocument->getId(), + 'type' => $languageDocument->getType(), + 'fullPath' => $languageDocument->getFullPath(), + 'published' => $languageDocument->getPublished(), + 'itemType' => 'document', + 'permissions' => $languageDocument->getUserPermissions($adminUser), + ]; + } elseif (!$document instanceof Document\Folder) { + $config[$language] = [ + 'text' => '--', + 'itemType' => 'empty', + ]; + } + } + + return $config; + } +} diff --git a/src/Handler/Document/Translation/GetLanguageTreeRootResult.php b/src/Handler/Document/Translation/GetLanguageTreeRootResult.php new file mode 100644 index 00000000..cf3afe9d --- /dev/null +++ b/src/Handler/Document/Translation/GetLanguageTreeRootResult.php @@ -0,0 +1,26 @@ +serviceFactory->createDocumentService()->removeTranslationLink($sourceDocument, $targetDocument); + } +} diff --git a/src/Handler/Document/TreeGetDocumentChildrenHandler.php b/src/Handler/Document/TreeGetDocumentChildrenHandler.php new file mode 100644 index 00000000..dfcab24c --- /dev/null +++ b/src/Handler/Document/TreeGetDocumentChildrenHandler.php @@ -0,0 +1,122 @@ +userContext->getAdminUser(); + $documents = []; + $cv = []; + if ($document->hasChildren()) { + if ($allParams['view'] ?? null) { + $cv = $this->elementService->getCustomViewById($allParams['view']); + } + + $db = Db::get(); + $list = new Document\Listing(); + $condition = 'parentId = ' . $db->quote($document->getId()); + if (!$adminUser->isAdmin()) { + $userIds = $adminUser->getRoles(); + $currentUserId = $adminUser->getId(); + $userIds[] = $currentUserId; + + $inheritedPermission = $document->getDao()->isInheritingPermission('list', $userIds); + + $anyAllowedRowOrChildren = 'EXISTS(SELECT list FROM users_workspaces_document uwd WHERE userId IN (' . implode(',', $userIds) . ') AND list=1 AND LOCATE(CONCAT(`path`,`key`),cpath)=1 AND + NOT EXISTS(SELECT list FROM users_workspaces_document WHERE userId =' . $currentUserId . ' AND list=0 AND cpath = uwd.cpath))'; + $isDisallowedCurrentRow = 'EXISTS(SELECT list FROM users_workspaces_document WHERE userId IN (' . implode(',', $userIds) . ') AND cid = id AND list=0)'; + + $condition .= ' AND IF(' . $anyAllowedRowOrChildren . ',1,IF(' . $inheritedPermission . ', ' . $isDisallowedCurrentRow . ' = 0, 0)) = 1'; + } + + if ($filter) { + $condition = '(' . $condition . ')' . ' AND CAST(documents.key AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ' . $db->quote($filter); + } + + $list->setCondition($condition); + $list->setOrderKey(['index', 'id']); + $list->setOrder(['asc', 'asc']); + $list->setLimit($limit); + $list->setOffset($offset); + + Service::addTreeFilterJoins($cv, $list); + + $beforeListLoadEvent = new GenericEvent($this, [ + 'list' => $list, + 'context' => $allParams, + ]); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::DOCUMENT_LIST_BEFORE_LIST_LOAD); + + /** @var Document\Listing $list */ + $list = $beforeListLoadEvent->getArgument('list'); + + $result = ($this->childrenHandler)($list); + $documents = $result->documents; + } + + $event = new GenericEvent($this, ['documents' => $documents]); + $this->eventDispatcher->dispatch($event, AdminEvents::DOCUMENT_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA); + $documents = $event->getArgument('documents'); + + return new TreeGetDocumentChildrenResult( + documents: $documents, + offset: $offset, + limit: $limit, + total: $document->getChildAmount($adminUser), + filter: $filter, + paginated: $paginated, + ); + } +} diff --git a/src/Handler/Document/TreeGetDocumentChildrenResult.php b/src/Handler/Document/TreeGetDocumentChildrenResult.php new file mode 100644 index 00000000..34952e4e --- /dev/null +++ b/src/Handler/Document/TreeGetDocumentChildrenResult.php @@ -0,0 +1,29 @@ +getDao()->getCurrentFullPath(); + $oldDocument = Document::getById($id, ['force' => true]); + + $adminUser = $this->userContext->getAdminUser(); + $allowUpdate = true; + + // prevent rename/relocate when newer unpublished version exists + if ($document instanceof Document\PageSnippet) { + $latestVersion = $document->getLatestVersion(); + if ($latestVersion && + $latestVersion->getData()->getModificationDate() != $document->getModificationDate() + ) { + throw new BadRequestHttpException("You can't rename or relocate if there's a newer not published version"); + } + } + + if ($document->isAllowed('settings')) { + if (isset($updateData['parentId'])) { + $parentDocument = Document::getById((int) $updateData['parentId']); + + if ($document->getParentId() !== $parentDocument->getId()) { + if (!$parentDocument->isAllowed('create')) { + throw new RuntimeException('Prevented moving document - no create permission on new parent.'); + } + + $intendedPath = $parentDocument->getRealPath(); + $pKey = $parentDocument->getKey(); + if (!empty($pKey)) { + $intendedPath .= $parentDocument->getKey() . '/'; + } + + $documentWithSamePath = Document::getByPath($intendedPath . $document->getKey()); + + if ($documentWithSamePath != null) { + $allowUpdate = false; + } + + if ($document->isLocked()) { + $allowUpdate = false; + } + } + } + + if ($allowUpdate) { + $blockedVars = ['id', 'controller', 'action', 'module']; + + if (!$document->isAllowed('rename') && isset($updateData['key'])) { + $blockedVars[] = 'key'; + Logger::debug('prevented renaming document because of missing permissions '); + } + + foreach ($updateData as $key => $value) { + if (!in_array($key, $blockedVars)) { + $document->setValue($key, $value); + } + } + + $document->setUserModification($adminUser->getId()); + $document->save(); + + if (isset($updateData['index'])) { + $this->updateIndexesOfDocumentSiblings($document, (int) $updateData['index']); + } + + if ($oldPath && $oldPath != $document->getRealFullPath()) { + $this->firePostMoveEvent($document, $oldDocument, $oldPath); + } + + return new UpdateDocumentResult( + treeData: $this->elementService->getElementTreeNodeConfig($document), + ); + } + + $msg = 'prevented moving document, because document with same path+key already exists' . + ' or the document is locked. ID: ' . $document->getId(); + Logger::debug($msg); + + throw new BadRequestHttpException($msg); + } + + if ($document->isAllowed('rename') && isset($updateData['key'])) { + $document->setKey($updateData['key']); + $document->setUserModification($adminUser->getId()); + $document->save(); + + if ($oldPath && $oldPath != $document->getRealFullPath()) { + $this->firePostMoveEvent($document, $oldDocument, $oldPath); + } + + return new UpdateDocumentResult( + treeData: $this->elementService->getElementTreeNodeConfig($document), + ); + } + + Logger::debug('Prevented update document, because of missing permissions.'); + + throw new BadRequestHttpException('Prevented update document, because of missing permissions.'); + } + + private function firePostMoveEvent(Document $document, Document $oldDocument, string $oldPath): void + { + $arguments = [ + 'oldPath' => $oldPath, + 'oldDocument' => $oldDocument, + ]; + $documentEvent = new DocumentEvent($document, $arguments); + $this->eventDispatcher->dispatch($documentEvent, DocumentEvents::POST_MOVE_ACTION); + } + + private function updateIndexesOfDocumentSiblings(Document $document, int $newIndex): void + { + $updateLatestVersionIndex = function (Document $document, int $newIndex): void { + if ($document instanceof Document\PageSnippet && $latestVersion = $document->getLatestVersion()) { + $document = $latestVersion->loadData(); + $document->setIndex($newIndex); + $latestVersion->save(); + } + }; + + $document->saveIndex($newIndex); + + $list = new Document\Listing(); + $list->setCondition('parentId = ? AND id != ?', [$document->getParentId(), $document->getId()]); + $list->setOrderKey('index'); + $list->setOrder('asc'); + $childrenList = $list->load(); + + $count = 0; + foreach ($childrenList as $child) { + if ($count === $newIndex) { + $count++; + } + $child->saveIndex($count); + $updateLatestVersionIndex($child, $count); + $count++; + } + } +} diff --git a/src/Handler/Document/UpdateDocumentResult.php b/src/Handler/Document/UpdateDocumentResult.php new file mode 100644 index 00000000..256497a5 --- /dev/null +++ b/src/Handler/Document/UpdateDocumentResult.php @@ -0,0 +1,24 @@ + $rootId]); + } + + $event = new SiteCustomSettingsEvent($site); + $this->eventDispatcher->dispatch($event, AdminEvents::SITE_CUSTOM_SETTINGS); + + $customSettings = []; + foreach ($event->getConfigNodes() as $scope => $nodes) { + foreach ($nodes as $node) { + $requestValueName = sprintf('customSettings_%s_%s', $scope, $node['name']); + if (isset($requestCustomSettings[$requestValueName])) { + $value = $requestCustomSettings[$requestValueName]; + if ($node['type'] === SiteCustomConfigNodeType::CHECKBOX->value) { + $value = $value === 'true'; + } + $customSettings[$scope][$node['name']] = $value; + } + } + } + + $site->setDomains($domains); + $site->setMainDomain($mainDomain); + $site->setErrorDocument($errorDocument); + $site->setLocalizedErrorDocuments($localizedErrorDocuments); + $site->setRedirectToMainDomain($redirectToMainDomain); + $site->setCustomSettings(count($customSettings) === 0 ? null : $customSettings); + $site->save(); + + $site->setRootDocument(null); + + return new UpdateSiteResult(siteVars: $site->getObjectVars()); + } +} diff --git a/src/Handler/Document/UpdateSiteResult.php b/src/Handler/Document/UpdateSiteResult.php new file mode 100644 index 00000000..6d9ef8eb --- /dev/null +++ b/src/Handler/Document/UpdateSiteResult.php @@ -0,0 +1,25 @@ +loadData(); + + if (!$docFrom instanceof Document) { + throw new DocumentNotFoundException($fromId); + } + + $versionTo = Version::getById($toId); + $docTo = $versionTo?->loadData(); + + if (!$docTo instanceof Document) { + throw new DocumentNotFoundException($toId); + } + + $comparisonId = uniqid(date('Y-m-d') . '-', true); + $tempFileTemplate = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/version-diff-tmp-' . $comparisonId . '-%s.%s'; + $fromImageFile = sprintf($tempFileTemplate, 'from', 'png'); + $toImageFile = sprintf($tempFileTemplate, 'to', 'png'); + $fromHtmlFile = sprintf($tempFileTemplate, 'from', 'html'); + $toHtmlFile = sprintf($tempFileTemplate, 'to', 'html'); + + file_put_contents($fromHtmlFile, $this->documentRenderer->render($docFrom)); + file_put_contents($toHtmlFile, $this->documentRenderer->render($docTo)); + + $prefix = Config::getSystemConfiguration('documents')['preview_url_prefix'] ?: $requestSchemeAndHost; + + try { + HtmlToImage::convert($prefix . $this->router->generate('opendxp_admin_document_document_diff_versions_html', ['id' => basename($fromHtmlFile)]), $fromImageFile); + HtmlToImage::convert($prefix . $this->router->generate('opendxp_admin_document_document_diff_versions_html', ['id' => basename($toHtmlFile)]), $toImageFile); + } finally { + unlink($fromHtmlFile); + unlink($toHtmlFile); + } + + $image1 = new Imagick($fromImageFile); + $image2 = new Imagick($toImageFile); + + if ($image1->getImageWidth() === $image2->getImageWidth() && $image1->getImageHeight() === $image2->getImageHeight()) { + $diff = $image1->compareImages($image2, Imagick::METRIC_MEANSQUAREERROR); + $diff[0]->setImageFormat('png'); + $image = base64_encode($diff[0]->getImageBlob()); + $diff[0]->clear(); + $diff[0]->destroy(); + $result = new DiffVersionsResult(supported: true, image: $image); + } else { + $result = new DiffVersionsResult( + supported: true, + image1: base64_encode(file_get_contents($fromImageFile)), + image2: base64_encode(file_get_contents($toImageFile)), + ); + } + + $image1->clear(); + $image1->destroy(); + $image2->clear(); + $image2->destroy(); + + unlink($fromImageFile); + unlink($toImageFile); + + return $result; + } +} diff --git a/src/Handler/Document/Version/DiffVersionsResult.php b/src/Handler/Document/Version/DiffVersionsResult.php new file mode 100644 index 00000000..2b0eb16d --- /dev/null +++ b/src/Handler/Document/Version/DiffVersionsResult.php @@ -0,0 +1,28 @@ +userContext->getAdminUser()?->getId() ?? 0; + $version = Version::getById($versionId); + $document = $version?->loadData(); + + if (!$document instanceof Document) { + throw new DocumentNotFoundException($versionId); + } + + $this->sessionService->saveDocument($document); + + $currentDocument = Document::getById($document->getId()); + if (!$currentDocument?->isAllowed('publish')) { + throw new AccessDeniedHttpException('Missing permission to publish document version'); + } + + $document->setPublished(true); + $document->setKey($currentDocument->getKey()); + $document->setPath($currentDocument->getRealPath()); + $document->setUserModification($userId); + $document->save(); + + $treeData = []; + $this->normalizer->normalize($document, $treeData, self::class); + + return new PublishVersionResult(treeData: $treeData); + } +} diff --git a/src/Handler/Document/Version/PublishVersionResult.php b/src/Handler/Document/Version/PublishVersionResult.php new file mode 100644 index 00000000..24068bcc --- /dev/null +++ b/src/Handler/Document/Version/PublishVersionResult.php @@ -0,0 +1,25 @@ +loadData(); + + if (!$document instanceof Document) { + throw new DocumentNotFoundException($versionId); + } + + $this->sessionService->saveDocument($document); + } +} diff --git a/src/Handler/Element/AddNoteHandler.php b/src/Handler/Element/AddNoteHandler.php new file mode 100644 index 00000000..4194946a --- /dev/null +++ b/src/Handler/Element/AddNoteHandler.php @@ -0,0 +1,41 @@ +setCid($cid); + $note->setCtype($ctype); + $note->setDate(time()); + $note->setTitle($title); + $note->setDescription($description); + $note->setType($type); + $note->setLocked(false); + $note->save(); + } +} diff --git a/src/Handler/Element/AnalyzePermissionsHandler.php b/src/Handler/Element/AnalyzePermissionsHandler.php new file mode 100644 index 00000000..184e401d --- /dev/null +++ b/src/Handler/Element/AnalyzePermissionsHandler.php @@ -0,0 +1,43 @@ +setCondition('`type` = ?', ['user']); + $userList = $userList->load(); + } + + $element = Element\Service::getElementById($elementType, $elementId); + $result = Element\PermissionChecker::check($element, $userList); + + return new AnalyzePermissionsResult(data: $result); + } +} diff --git a/src/Handler/Element/AnalyzePermissionsResult.php b/src/Handler/Element/AnalyzePermissionsResult.php new file mode 100644 index 00000000..f7616365 --- /dev/null +++ b/src/Handler/Element/AnalyzePermissionsResult.php @@ -0,0 +1,25 @@ +setCondition( + 'cid = ' . $versions->quote($elementId) . + ' AND date <> ' . $versions->quote($elementModificationdate) . + ' AND ctype = ' . $versions->quote($elementType) + ); + foreach ($versions->load() as $version) { + $version->delete(); + } + } +} diff --git a/src/Handler/Element/DeleteDraftHandler.php b/src/Handler/Element/DeleteDraftHandler.php new file mode 100644 index 00000000..f3b4e151 --- /dev/null +++ b/src/Handler/Element/DeleteDraftHandler.php @@ -0,0 +1,32 @@ +delete(); + } + } +} diff --git a/src/Handler/Element/DeleteNoteHandler.php b/src/Handler/Element/DeleteNoteHandler.php new file mode 100644 index 00000000..a92a97f7 --- /dev/null +++ b/src/Handler/Element/DeleteNoteHandler.php @@ -0,0 +1,36 @@ +getLocked()) { + $note->delete(); + + return true; + } + + return false; + } +} diff --git a/src/Handler/Element/DeleteVersionHandler.php b/src/Handler/Element/DeleteVersionHandler.php new file mode 100644 index 00000000..6309466c --- /dev/null +++ b/src/Handler/Element/DeleteVersionHandler.php @@ -0,0 +1,28 @@ +delete(); + } +} diff --git a/src/Handler/Element/FindUsagesHandler.php b/src/Handler/Element/FindUsagesHandler.php new file mode 100644 index 00000000..6089791a --- /dev/null +++ b/src/Handler/Element/FindUsagesHandler.php @@ -0,0 +1,86 @@ +getDependencies()->getRequiredByTotalCount(); + $results = []; + $hasHidden = false; + + if ($sort !== null) { + $sort = json_decode($sort)[0]; + $orderBy = $sort->property; + $orderDirection = $sort->direction; + } else { + $orderBy = null; + $orderDirection = null; + } + + $queryOffset = $offset; + $queryLimit = $limit; + + while (count($results) < min($limit, $total) && $queryOffset < $total) { + $elements = $element->getDependencies() + ->getRequiredByWithPath($queryOffset, $queryLimit, $orderBy, $orderDirection); + + foreach ($elements as $el) { + $item = Element\Service::getElementById($el['type'], (int) $el['id']); + + if ($item instanceof Element\ElementInterface) { + if ($item->isAllowed('list')) { + $results[] = $el; + } else { + $hasHidden = true; + } + } + } + + $queryOffset += count($elements); + $queryLimit = $limit - count($results); + } + + return new FindUsagesResult( + data: $results, + total: $total, + hasHidden: $hasHidden, + ); + } +} diff --git a/src/Handler/Element/FindUsagesResult.php b/src/Handler/Element/FindUsagesResult.php new file mode 100644 index 00000000..f27421df --- /dev/null +++ b/src/Handler/Element/FindUsagesResult.php @@ -0,0 +1,27 @@ +getDependencies()->isRequired(); + } + } catch (Exception) { + Logger::err('failed to access element with id: ' . $id); + + continue; + } + + $event = null; + $eventName = null; + + if ($element instanceof Asset) { + $event = new AssetDeleteInfoEvent($element); + $eventName = AssetEvents::DELETE_INFO; + } elseif ($element instanceof Document) { + $event = new DocumentDeleteInfoEvent($element); + $eventName = DocumentEvents::DELETE_INFO; + } elseif ($element instanceof AbstractObject) { + $event = new DataObjectDeleteInfoEvent($element); + $eventName = DataObjectEvents::DELETE_INFO; + } + + if ($element->isLocked()) { + $itemResults[] = [ + 'id' => $element->getId(), + 'type' => $element->getType(), + 'key' => $element->getKey(), + 'reason' => 'Element is locked', + 'allowed' => false, + ]; + $errors |= true; + + continue; + } + + $this->eventDispatcher->dispatch($event, $eventName); + + if (!$event->getDeletionAllowed()) { + $itemResults[] = [ + 'id' => $element->getId(), + 'type' => $element->getType(), + 'key' => $element->getKey(), + 'reason' => $event->getReason(), + 'allowed' => false, + ]; + $errors |= true; + + continue; + } + + $itemResults[] = [ + 'id' => $element->getId(), + 'type' => $element->getType(), + 'key' => $element->getKey(), + 'path' => $element->getPath(), + 'allowed' => true, + ]; + + $deleteJobs[] = [[ + 'url' => $this->urlGenerator->generate('opendxp_admin_recyclebin_add'), + 'method' => 'POST', + 'params' => [ + 'type' => $type, + 'id' => $element->getId(), + ], + ]]; + + $hasChildren = $element->hasChildren(); + if (!$hasDependency) { + $hasDependency = $hasChildren; + } + + if ($hasChildren) { + $list = $element::getList(['unpublished' => true]); + $pathColumn = 'path'; + $list->setCondition($pathColumn . ' LIKE ?', [$element->getRealFullPath() . '/%']); + $children = $list->getTotalCount(); + $totalChildren += $children; + + if ($children > 0) { + $deleteObjectsPerRequest = 5; + for ($i = 0, $iMax = ceil($children / $deleteObjectsPerRequest); $i < $iMax; $i++) { + $deleteJobs[] = [[ + 'url' => $baseUrl . '/admin/' . $type . '/delete', + 'method' => 'DELETE', + 'params' => [ + 'step' => $i, + 'amount' => $deleteObjectsPerRequest, + 'type' => 'children', + 'id' => $element->getId(), + ], + ]]; + } + } + } + + $deleteJobs[] = [[ + 'url' => $baseUrl . '/admin/' . $type . '/delete', + 'method' => 'DELETE', + 'params' => [ + 'id' => $element->getId(), + ], + ]]; + } + + $elementKey = false; + if (count($idList) === 1) { + $element = Service::getElementById($type, (int) $idList[0]); + + if ($element instanceof ElementInterface) { + $elementKey = $element->getKey(); + } + } + + return [ + 'hasDependencies' => $hasDependency, + 'children' => $totalChildren, + 'deletejobs' => $deleteJobs, + 'batchDelete' => count($idList) > 1, + 'elementKey' => $elementKey, + 'errors' => $errors, + 'itemResults' => $itemResults, + ]; + } +} diff --git a/src/Handler/Element/GetNicePathHandler.php b/src/Handler/Element/GetNicePathHandler.php new file mode 100644 index 00000000..a6e0c771 --- /dev/null +++ b/src/Handler/Element/GetNicePathHandler.php @@ -0,0 +1,147 @@ +getFieldDefinition($sourceObject, $context); + $result = $this->convertResultWithPathFormatter($sourceObject, $context, $result, $targets); + + if ($loadEditModeData) { + $methodName = 'get' . ucfirst($fieldname); + if ($ownerType === 'object' && method_exists($sourceObject, $methodName)) { + $data = DataObject\Service::useInheritedValues(true, [$sourceObject, $methodName]); + $editModeData = $fd->getDataForEditmode($data, $sourceObject); + // Inherited values show as an empty array + if (is_array($editModeData) && $editModeData !== []) { + foreach ($editModeData as $relationObjectAttribute) { + $relationObjectAttribute['$$nicepath'] = isset($relationObjectAttribute[$idProperty], $result[$relationObjectAttribute[$idProperty]]) + ? $result[$relationObjectAttribute[$idProperty]] + : null; + + $result[$relationObjectAttribute[$idProperty]] = $relationObjectAttribute; + } + } else { + foreach ($result as $resultItemId => $resultItem) { + $result[$resultItemId] = ['$$nicepath' => $resultItem]; + } + } + } else { + Logger::error('Loading edit mode data is not supported for ownertype: ' . $ownerType); + } + } + + return new GetNicePathResult(data: $result); + } + + /** + * @throws Exception + */ + private function getFieldDefinition(DataObject\Concrete $source, array $context): DataObject\ClassDefinition\Data|bool|null + { + $ownerType = $context['containerType']; + $fieldname = $context['fieldname']; + $fd = null; + + if ($ownerType === 'object') { + $subContainerType = $context['subContainerType'] ?? null; + if ($subContainerType) { + $subContainerKey = $context['subContainerKey']; + $subContainer = $source->getClass()->getFieldDefinition($subContainerKey); + if (method_exists($subContainer, 'getFieldDefinition')) { + $fd = $subContainer->getFieldDefinition($fieldname); + } + } else { + $fd = $source->getClass()->getFieldDefinition($fieldname); + } + } elseif ($ownerType === 'localizedfield') { + $localizedfields = $source->getClass()->getFieldDefinition('localizedfields'); + if ($localizedfields instanceof DataObject\ClassDefinition\Data\Localizedfields) { + $fd = $localizedfields->getFieldDefinition($fieldname); + } + } elseif ($ownerType === 'objectbrick') { + $fdBrick = DataObject\Objectbrick\Definition::getByKey($context['containerKey']); + $fd = $fdBrick->getFieldDefinition($fieldname); + } elseif ($ownerType === 'fieldcollection') { + $containerKey = $context['containerKey']; + $fdCollection = DataObject\Fieldcollection\Definition::getByKey($containerKey); + if (($context['subContainerType'] ?? null) === 'localizedfield') { + /** @var DataObject\ClassDefinition\Data\Localizedfields $fdLocalizedFields */ + $fdLocalizedFields = $fdCollection->getFieldDefinition('localizedfields'); + $fd = $fdLocalizedFields->getFieldDefinition($fieldname); + } else { + $fd = $fdCollection->getFieldDefinition($fieldname); + } + } + + return $fd; + } + + /** + * @throws Exception + */ + private function convertResultWithPathFormatter(DataObject\Concrete $source, array $context, array $result, array $targets): array + { + $fd = $this->getFieldDefinition($source, $context); + + if ($fd instanceof DataObject\ClassDefinition\PathFormatterAwareInterface) { + $formatter = $fd->getPathFormatterClass(); + + if (null !== $formatter) { + $pathFormatter = DataObject\ClassDefinition\Helper\PathFormatterResolver::resolvePathFormatter( + $fd->getPathFormatterClass() + ); + + if ($pathFormatter instanceof DataObject\ClassDefinition\PathFormatterInterface) { + $result = $pathFormatter->formatPath($result, $source, $targets, [ + 'fd' => $fd, + 'context' => $context, + ]); + } + } + } + + return $result; + } +} diff --git a/src/Handler/Element/GetNicePathResult.php b/src/Handler/Element/GetNicePathResult.php new file mode 100644 index 00000000..ac7c8750 --- /dev/null +++ b/src/Handler/Element/GetNicePathResult.php @@ -0,0 +1,25 @@ +setLimit($limit); + $list->setOffset($offset); + + if ($sortingSettings['orderKey'] && $sortingSettings['order']) { + $list->setOrderKey($sortingSettings['orderKey']); + $list->setOrder($sortingSettings['order']); + } else { + $list->setOrderKey(['date', 'id']); + $list->setOrder(['DESC', 'DESC']); + } + + $conditions = []; + + if ($filterText) { + $conditions[] = '(' + . '`title` LIKE ' . $list->quote('%' . $filterText . '%') + . ' OR `description` LIKE ' . $list->quote('%' . $filterText . '%') + . ' OR `type` LIKE ' . $list->quote('%' . $filterText . '%') + . ' OR `user` IN (SELECT `id` FROM `users` WHERE `name` LIKE ' . $list->quote('%' . $filterText . '%') . ')' + . " OR DATE_FORMAT(FROM_UNIXTIME(`date`), '%Y-%m-%d') LIKE " . $list->quote('%' . $filterText . '%') + . ')'; + } + + if ($filterJson) { + $db = Db::get(); + $filters = json_decode($filterJson, true) ?? []; + $propertyKey = 'property'; + $comparisonKey = 'operator'; + + foreach ($filters as $filter) { + $operator = '='; + + if ($filter['type'] === 'string') { + $operator = 'LIKE'; + } elseif ($filter['type'] === 'numeric') { + $operator = match ($filter[$comparisonKey] ?? '') { + 'lt' => '<', + 'gt' => '>', + default => '=', + }; + } elseif ($filter['type'] === 'date') { + $operator = match ($filter[$comparisonKey] ?? '') { + 'lt' => '<', + 'gt' => '>', + default => '=', + }; + $filter['value'] = strtotime($filter['value']); + } elseif ($filter[$comparisonKey] === 'list') { + $operator = '='; + } elseif ($filter[$comparisonKey] === 'boolean') { + $operator = '='; + $filter['value'] = (int) $filter['value']; + } + + $value = ($filter['value'] ?? ''); + if ($operator === 'LIKE') { + $value = '%' . $value . '%'; + } + + if ($filter[$propertyKey] === 'user') { + $conditions[] = '`user` IN (SELECT `id` FROM `users` WHERE `name` LIKE ' . $list->quote($value) . ')'; + } elseif ($filter['type'] === 'date' && ($filter[$comparisonKey] ?? '') === 'eq') { + $maxTime = $value + (86400 - 1); + $conditions[] = '`' . $filter[$propertyKey] . '` BETWEEN ' . $db->quote($value) . ' AND ' . $db->quote($maxTime); + } else { + $conditions[] = $db->quoteIdentifier($filter[$propertyKey]) . ' ' . $operator . ' ' . $db->quote($value); + } + } + } + + if ($cid !== null && $ctype !== null) { + $conditions[] = '(cid = ' . $list->quote($cid) . ' AND ctype = ' . $list->quote($ctype) . ')'; + } + + if ($conditions !== []) { + $list->setCondition(implode(' AND ', $conditions)); + } + + $list->load(); + + $notes = []; + foreach ($list->getNotes() as $note) { + $notes[] = Element\Service::getNoteData($note); + } + + return new GetNoteListResult(data: $notes, total: $list->getTotalCount()); + } +} diff --git a/src/Handler/Element/GetNoteListResult.php b/src/Handler/Element/GetNoteListResult.php new file mode 100644 index 00000000..5098e589 --- /dev/null +++ b/src/Handler/Element/GetNoteListResult.php @@ -0,0 +1,26 @@ +setFilter(function (Property\Predefined $predefined) use ($type, $query) { + if (!str_contains($predefined->getCtype(), $type)) { + return false; + } + + return !($query && stripos($this->translator->trans($predefined->getName(), [], 'admin'), (string) $query) === false); + }); + + foreach ($list->getProperties() as $predefined) { + $properties[] = $predefined->getObjectVars(); + } + } + + return new GetPredefinedPropertiesResult(properties: $properties); + } +} diff --git a/src/Handler/Element/GetPredefinedPropertiesResult.php b/src/Handler/Element/GetPredefinedPropertiesResult.php new file mode 100644 index 00000000..2f18b4df --- /dev/null +++ b/src/Handler/Element/GetPredefinedPropertiesResult.php @@ -0,0 +1,25 @@ +getDependencies()->getRequiredBy(); + } +} diff --git a/src/Handler/Element/GetRequiredByDependenciesHandler.php b/src/Handler/Element/GetRequiredByDependenciesHandler.php new file mode 100644 index 00000000..bc1165eb --- /dev/null +++ b/src/Handler/Element/GetRequiredByDependenciesHandler.php @@ -0,0 +1,74 @@ +getDependencies(); + + if ($filterJson) { + $filters = json_decode($filterJson, true) ?? []; + $value = null; + $elements = null; + + foreach ($filters as $filter) { + if ($filter['type'] === 'string') { + $value = ($filter['value'] ?? ''); + } + $elements = $element->getDependencies()->getFilterRequiredByPath($offset, $limit, $value); + } + + if ($elements !== null && count($elements) > 0) { + $result = Model\Element\Service::getFilterRequiredByPathForFrontend($elements); + $result['total'] = count($result['requiredBy']); + + return new GetRequiresDependenciesResult(data: $result); + } + + return new GetRequiresDependenciesResult(data: $elements ?? []); + } + + if ($element instanceof Model\Element\ElementInterface) { + $dependenciesResult = Model\Element\Service::getRequiredByDependenciesForFrontend($dependencies, $offset, $limit); + + $dependenciesResult['start'] = $offset; + $dependenciesResult['limit'] = $limit; + $dependenciesResult['total'] = $dependencies->getRequiredByTotalCount(); + + return new GetRequiresDependenciesResult(data: $dependenciesResult); + } + + return new GetRequiresDependenciesResult(data: false); + } +} diff --git a/src/Handler/Element/GetRequiresDependenciesHandler.php b/src/Handler/Element/GetRequiresDependenciesHandler.php new file mode 100644 index 00000000..ec7e90ec --- /dev/null +++ b/src/Handler/Element/GetRequiresDependenciesHandler.php @@ -0,0 +1,74 @@ +getDependencies(); + + if ($filterJson) { + $filters = json_decode($filterJson, true) ?? []; + $value = null; + $elements = null; + + foreach ($filters as $filter) { + if ($filter['type'] === 'string') { + $value = ($filter['value'] ?? ''); + } + $elements = $element->getDependencies()->getFilterRequiresByPath($offset, $limit, $value); + } + + if ($elements !== null && count($elements) > 0) { + $result = Model\Element\Service::getFilterRequiresForFrontend($elements); + $result['total'] = count($result['requires']); + + return new GetRequiresDependenciesResult(data: $result); + } + + return new GetRequiresDependenciesResult(data: $elements ?? []); + } + + if ($element instanceof Model\Element\ElementInterface) { + $dependenciesResult = Model\Element\Service::getRequiresDependenciesForFrontend($dependencies, $offset, $limit); + + $dependenciesResult['start'] = $offset; + $dependenciesResult['limit'] = $limit; + $dependenciesResult['total'] = $dependencies->getRequiresTotalCount(); + + return new GetRequiresDependenciesResult(data: $dependenciesResult); + } + + return new GetRequiresDependenciesResult(data: false); + } +} diff --git a/src/Handler/Element/GetRequiresDependenciesResult.php b/src/Handler/Element/GetRequiresDependenciesResult.php new file mode 100644 index 00000000..c124391b --- /dev/null +++ b/src/Handler/Element/GetRequiresDependenciesResult.php @@ -0,0 +1,25 @@ +dispatch($event, AdminEvents::RESOLVE_ELEMENT); + $idOrPath = $event->getId(); + $resolvedType = $event->getType(); + + if (is_numeric($idOrPath)) { + $el = Element\Service::getElementById($resolvedType, (int) $idOrPath); + } elseif ($resolvedType === 'document') { + $el = Document\Service::getByUrl($idOrPath); + } else { + $el = Element\Service::getElementByPath($resolvedType, $idOrPath); + } + + if (!$el) { + throw new NotFoundHttpException('Element not found'); + } + + $subtype = null; + if ($el instanceof Asset || $el instanceof Document) { + $subtype = $el->getType(); + } elseif ($el instanceof DataObject\Concrete) { + $subtype = $el->getClassName(); + } elseif ($el instanceof DataObject\Folder) { + $subtype = 'folder'; + } + + return new GetSubtypeResult(subtype: $subtype, id: $el->getId(), type: $type); + } +} diff --git a/src/Handler/Element/GetSubtypeResult.php b/src/Handler/Element/GetSubtypeResult.php new file mode 100644 index 00000000..8e3bc36c --- /dev/null +++ b/src/Handler/Element/GetSubtypeResult.php @@ -0,0 +1,27 @@ +userContext->getAdminUser(); + $allowedTypes = ['asset', 'document', 'object']; + + if (!$id || !in_array($type, $allowedTypes)) { + throw new NotFoundHttpException('Element type not found'); + } + + $element = Model\Element\Service::getElementById($type, $id); + if (!$element) { + throw new NotFoundHttpException($type . ' with id [' . $id . "] doesn't exist"); + } + + if (!$element->isAllowed('versions')) { + throw new AccessDeniedHttpException('Permission denied, ' . $type . ' id [' . $id . ']'); + } + + $schedule = $element->getScheduledTasks(); + $schedules = []; + foreach ($schedule as $task) { + if ($task->getActive()) { + $schedules[$task->getVersion()] = $task->getDate(); + } + } + + $list = new Version\Listing(); + $list->setLoadAutoSave(true); + $list->setCondition('cid = ? AND ctype = ? AND (autoSave=0 OR (autoSave=1 AND userId = ?)) ', [ + $element->getId(), + Element\Service::getElementType($element), + $adminUser->getId(), + ]) + ->setOrderKey('date') + ->setOrder('ASC'); + + $versions = $list->load(); + $versions = Model\Element\Service::getSafeVersionInfo($versions); + $versions = array_reverse($versions); + + foreach ($versions as &$version) { + $version['scheduled'] = null; + if (array_key_exists($version['id'], $schedules)) { + $version['scheduled'] = $schedules[$version['id']]; + } + } + + return new GetVersionsResult(versions: $versions); + } +} diff --git a/src/Handler/Element/GetVersionsResult.php b/src/Handler/Element/GetVersionsResult.php new file mode 100644 index 00000000..ec40668d --- /dev/null +++ b/src/Handler/Element/GetVersionsResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $element = Element\Service::getElementById($type, $id); + $sourceEl = Element\Service::getElementById($sourceType, $sourceId); + $targetEl = Element\Service::getElementById($targetType, $targetId); + + if (!$element || !$sourceEl || !$targetEl) { + throw new NotFoundHttpException('One or more elements not found'); + } + + if ($sourceType !== $targetType || $sourceEl->getType() !== $targetEl->getType()) { + throw new BadRequestHttpException('source-type and target-type do not match'); + } + + if (!$element->isAllowed('save')) { + throw new AccessDeniedHttpException(); + } + + $rewriteConfig = [ + $sourceType => [ + $sourceEl->getId() => $targetEl->getId(), + ], + ]; + + if ($element instanceof Document) { + $element = Document\Service::rewriteIds($element, $rewriteConfig); + } elseif ($element instanceof DataObject\AbstractObject) { + $element = DataObject\Service::rewriteIds($element, $rewriteConfig); + } elseif ($element instanceof Asset) { + $element = Asset\Service::rewriteIds($element, $rewriteConfig); + } + + $element->setUserModification($adminUser->getId()); + $element->save(); + } +} diff --git a/src/Handler/Element/TypePathHandler.php b/src/Handler/Element/TypePathHandler.php new file mode 100644 index 00000000..1bcfcfe6 --- /dev/null +++ b/src/Handler/Element/TypePathHandler.php @@ -0,0 +1,50 @@ +getIndex() : 0, + idPath: Element\Service::getIdPath($element), + typePath: Element\Service::getTypePath($element), + fullpath: $element->getRealFullPath(), + sortIndexPath: $type !== 'asset' ? Element\Service::getSortIndexPath($element) : null, + ); + } +} diff --git a/src/Handler/Element/TypePathResult.php b/src/Handler/Element/TypePathResult.php new file mode 100644 index 00000000..6fd90389 --- /dev/null +++ b/src/Handler/Element/TypePathResult.php @@ -0,0 +1,29 @@ + $elements */ + public function __invoke(array $elements): void + { + foreach ($elements as $element) { + Editlock::unlock((int) $element['id'], $element['type']); + } + } +} diff --git a/src/Handler/Element/UnlockPropagateHandler.php b/src/Handler/Element/UnlockPropagateHandler.php new file mode 100644 index 00000000..caabead8 --- /dev/null +++ b/src/Handler/Element/UnlockPropagateHandler.php @@ -0,0 +1,35 @@ +unlockPropagate(); + + return true; + } +} diff --git a/src/Handler/Element/VersionUpdateHandler.php b/src/Handler/Element/VersionUpdateHandler.php new file mode 100644 index 00000000..25a37713 --- /dev/null +++ b/src/Handler/Element/VersionUpdateHandler.php @@ -0,0 +1,34 @@ +getPublic() || $data['note'] != $version->getNote()) { + $version->setPublic($data['public']); + $version->setNote($data['note']); + $version->save(); + } + } +} diff --git a/src/Handler/Email/CreateBlocklistEntryHandler.php b/src/Handler/Email/CreateBlocklistEntryHandler.php new file mode 100644 index 00000000..62ab0345 --- /dev/null +++ b/src/Handler/Email/CreateBlocklistEntryHandler.php @@ -0,0 +1,34 @@ +setValues($data); + $address->save(); + + return $address->getObjectVars(); + } +} diff --git a/src/Handler/Email/DeleteBlocklistEntryHandler.php b/src/Handler/Email/DeleteBlocklistEntryHandler.php new file mode 100644 index 00000000..552f78ec --- /dev/null +++ b/src/Handler/Email/DeleteBlocklistEntryHandler.php @@ -0,0 +1,29 @@ +delete(); + } +} diff --git a/src/Handler/Email/DeleteEmailLogHandler.php b/src/Handler/Email/DeleteEmailLogHandler.php new file mode 100644 index 00000000..78b47738 --- /dev/null +++ b/src/Handler/Email/DeleteEmailLogHandler.php @@ -0,0 +1,34 @@ +delete(); + } +} diff --git a/src/Handler/Email/GetBlocklistHandler.php b/src/Handler/Email/GetBlocklistHandler.php new file mode 100644 index 00000000..173d2275 --- /dev/null +++ b/src/Handler/Email/GetBlocklistHandler.php @@ -0,0 +1,53 @@ +setLimit($limit); + $list->setOffset($offset); + + if ($sortingSettings['orderKey']) { + $list->setOrderKey($sortingSettings['orderKey']); + $list->setOrder($sortingSettings['order']); + } + + if ($filter !== null) { + $list->setCondition('`address` LIKE ' . $list->quote('%' . $filter . '%')); + } + + $data = $list->load(); + $jsonData = []; + foreach ($data as $entry) { + $jsonData[] = $entry->getObjectVars(); + } + + return new GetBlocklistResult(data: $jsonData, total: $list->getTotalCount()); + } +} diff --git a/src/Handler/Email/GetBlocklistResult.php b/src/Handler/Email/GetBlocklistResult.php new file mode 100644 index 00000000..523ed80b --- /dev/null +++ b/src/Handler/Email/GetBlocklistResult.php @@ -0,0 +1,26 @@ +getTextLog(), + htmlLog: $log->getHtmlLog(), + objectVars: $log->getObjectVars(), + ); + } +} diff --git a/src/Handler/Email/GetEmailLogParamsHandler.php b/src/Handler/Email/GetEmailLogParamsHandler.php new file mode 100644 index 00000000..4350bced --- /dev/null +++ b/src/Handler/Email/GetEmailLogParamsHandler.php @@ -0,0 +1,119 @@ +getParams(); + } catch (\Exception) { + Logger::warning('Could not decode JSON param string'); + $params = []; + } + + foreach ($params as &$entry) { + $this->enhanceLoggingData($entry); + } + + return $params; + } + + private function enhanceLoggingData(?array &$data, ?array &$fullEntry = null): void + { + if (!is_array($data)) { + return; + } + + if (!empty($data['objectClass'])) { + $class = '\\' . ltrim($data['objectClass'], '\\'); + $reflection = new ReflectionClass($class); + + if (!empty($data['objectId']) && $reflection->implementsInterface(ElementInterface::class)) { + $obj = $class::getById($data['objectId']); + $data['objectPath'] = is_null($obj) ? '' : $obj->getRealFullPath(); + + if (stristr($class, '\\OpenDxp\\Model') === false) { + $niceClassName = '\\' . ltrim($reflection->getParentClass()->getName(), '\\'); + } else { + $niceClassName = $class; + } + + $niceClassName = str_replace(['\\OpenDxp\\Model\\', '_'], ['', '\\'], $niceClassName); + + $tmp = explode('\\', $niceClassName); + if (in_array($tmp[0], ['DataObject', 'Document', 'Asset'])) { + $data['objectClassBase'] = $tmp[0]; + $data['objectClassSubType'] = $tmp[1]; + } + } + } + + foreach ($data as &$value) { + if (!is_array($value)) { + continue; + } + + $this->enhanceLoggingData($value, $data); + } + + unset($value); + + if ($data['children'] ?? false) { + foreach ($data['children'] as $key => $entry) { + if (is_string($key)) { + unset($data['children'][$key]); + } + } + $data['iconCls'] = 'opendxp_icon_folder'; + $data['data'] = ['type' => 'simple', 'value' => 'Children (' . count($data['children']) . ')']; + } else { + if (empty($data['iconCls'])) { + if (($data['objectClassBase'] ?? '') === 'DataObject') { + $fullEntry['iconCls'] = 'opendxp_icon_object'; + } elseif (($data['objectClassBase'] ?? '') === 'Asset') { + $fullEntry['iconCls'] = match ($data['objectClassSubType']) { + 'Image' => 'opendxp_icon_image', + 'Video' => 'opendxp_icon_wmv', + 'Text' => 'opendxp_icon_txt', + 'Document' => 'opendxp_icon_pdf', + default => 'opendxp_icon_asset', + }; + } elseif (str_starts_with($data['objectClass'] ?? '', 'Document')) { + $fullEntry['iconCls'] = 'opendxp_icon_' . strtolower($data['objectClassSubType']); + } else { + $data['iconCls'] = 'opendxp_icon_text'; + } + } + + $data['leaf'] = true; + } + } +} diff --git a/src/Handler/Email/GetEmailLogResult.php b/src/Handler/Email/GetEmailLogResult.php new file mode 100644 index 00000000..14864edb --- /dev/null +++ b/src/Handler/Email/GetEmailLogResult.php @@ -0,0 +1,27 @@ +setCondition('documentId = ' . $documentId); + } + + $list->setLimit($limit); + $list->setOffset($offset); + $list->setOrderKey('sentDate'); + $list->setOrder('DESC'); + + if ($filter !== null) { + if ($filter === '*') { + $filter = ''; + } + + $filter = str_replace('%', '*', $filter); + $filter = htmlspecialchars($filter, ENT_QUOTES); + + if (strpos($filter, '@')) { + $parts = explode(' ', $filter); + $parts = array_map(static function ($part) { + if (strpos($part, '@')) { + return '"' . $part . '"'; + } + + return $part; + }, $parts); + $filter = implode(' ', $parts); + } + + if (str_starts_with($filter, '@')) { + $filter = str_replace('@', '', $filter); + } + + $condition = '( MATCH (`from`,`to`,`cc`,`bcc`,`subject`,`params`) AGAINST (' . $list->quote($filter) . ' IN BOOLEAN MODE) )'; + + if ($documentId !== null) { + $condition .= 'AND documentId = ' . $documentId; + } + + $list->setCondition($condition); + } + + $data = $list->load(); + $jsonData = []; + + foreach ($data as $entry) { + $tmp = $entry->getObjectVars(); + unset($tmp['bodyHtml'], $tmp['bodyText']); + $jsonData[] = $tmp; + } + + return new GetEmailLogsResult(data: $jsonData, total: $list->getTotalCount()); + } +} diff --git a/src/Handler/Email/GetEmailLogsResult.php b/src/Handler/Email/GetEmailLogsResult.php new file mode 100644 index 00000000..675c5a82 --- /dev/null +++ b/src/Handler/Email/GetEmailLogsResult.php @@ -0,0 +1,26 @@ + $fieldOverrides Keys: 'from', 'to', 'cc', 'bcc', 'replyto' + */ + public function __invoke(int $id, array $fieldOverrides): void + { + $emailLog = Tool\Email\Log::getById($id); + if (!$emailLog instanceof Tool\Email\Log) { + throw new NotFoundHttpException('Email log with ID ' . $id . ' not found.'); + } + + $mail = new Mail(); + $mail->preventDebugInformationAppending(); + $mail->setIgnoreDebugMode(true); + + if (!empty($fieldOverrides['to'])) { + $emailLog->setTo(null); + $emailLog->setCc(null); + $emailLog->setBcc(null); + } else { + $mail->disableLogging(); + } + + if ($html = $emailLog->getHtmlLog()) { + $mail->html($html); + } + + if ($text = $emailLog->getTextLog()) { + $mail->text($text); + } + + foreach (['From', 'To', 'Cc', 'Bcc', 'ReplyTo'] as $field) { + $overrideKey = strtolower($field); + if (!empty($fieldOverrides[$overrideKey])) { + $values = $fieldOverrides[$overrideKey]; + } else { + $getter = 'get' . $field; + $values = $emailLog->{$getter}(); + } + + $values = \OpenDxp\Helper\Mail::parseEmailAddressField($values); + + if ($values) { + [$value] = $values; + $mail->{'add' . $field}(new Address($value['email'], $value['name'])); + } + } + + $mail->subject($emailLog->getSubject()); + + if ($emailLog->getDocumentId()) { + $mail->setDocument($emailLog->getDocumentId()); + } + + try { + $params = $emailLog->getParams(); + } catch (\Exception) { + Logger::warning('Could not decode JSON param string'); + $params = []; + } + + foreach ($params as $entry) { + $data = null; + $hasChildren = isset($entry['children']) && is_array($entry['children']); + + if ($hasChildren) { + $childData = []; + foreach ($entry['children'] as $childParam) { + $childData[$childParam['key']] = $this->parseLoggingParamObject($childParam); + } + $data = $childData; + } else { + $data = $this->parseLoggingParamObject($entry); + } + + $mail->setParam($entry['key'], $data); + } + + $mail->send(); + } + + private function parseLoggingParamObject(array $params): mixed + { + if ($params['data']['type'] === 'object') { + $class = '\\' . ltrim($params['data']['objectClass'], '\\'); + $reflection = new ReflectionClass($class); + + if (!empty($params['data']['objectId']) && $reflection->implementsInterface(ElementInterface::class)) { + $obj = $class::getById($params['data']['objectId']); + if (!is_null($obj)) { + return $obj; + } + } + + return null; + } + + return $params['data']['value']; + } +} diff --git a/src/Handler/Email/SendTestEmailHandler.php b/src/Handler/Email/SendTestEmailHandler.php new file mode 100644 index 00000000..bcf58349 --- /dev/null +++ b/src/Handler/Email/SendTestEmailHandler.php @@ -0,0 +1,77 @@ +text(strip_tags($content ?? '')); + } elseif ($emailType === 'html') { + $mail->html($content ?? ''); + } elseif ($emailType === 'document') { + $doc = Document::getByPath($documentPath ?? ''); + + if (!$doc instanceof Document\Email) { + throw new BadRequestHttpException('Email document not found!'); + } + + $mail->setDocument($doc); + + if ($mailParameters) { + foreach ($mailParameters as $mailParam) { + if ($mailParam['key']) { + $mail->setParam($mailParam['key'], $mailParam['value']); + } + } + } + } + + if ($from) { + $addressArray = \OpenDxp\Helper\Mail::parseEmailAddressField($from); + if ($addressArray) { + [$cleanedFromAddress] = $addressArray; + $mail->from(new Address($cleanedFromAddress['email'], $cleanedFromAddress['name'])); + } + } + + $toAddresses = \OpenDxp\Helper\Mail::parseEmailAddressField($to); + foreach ($toAddresses as $cleanedToAddress) { + $mail->addTo($cleanedToAddress['email'], $cleanedToAddress['name']); + } + + $mail->subject($subject); + $mail->setIgnoreDebugMode(true); + $mail->send(); + } +} diff --git a/src/Handler/Email/UpdateBlocklistEntryHandler.php b/src/Handler/Email/UpdateBlocklistEntryHandler.php new file mode 100644 index 00000000..c67038b3 --- /dev/null +++ b/src/Handler/Email/UpdateBlocklistEntryHandler.php @@ -0,0 +1,32 @@ +setValues($data); + $address->save(); + + return $address->getObjectVars(); + } +} diff --git a/src/Handler/Login/GenerateTwoFactorSetupHandler.php b/src/Handler/Login/GenerateTwoFactorSetupHandler.php new file mode 100644 index 00000000..0a2e5179 --- /dev/null +++ b/src/Handler/Login/GenerateTwoFactorSetupHandler.php @@ -0,0 +1,55 @@ +userContext->getAdminUser(); + $proxyUser = $this->userContext->getAdminUserProxy(); + + $secret = $this->twoFactor->generateSecret(); + + $user->setTwoFactorAuthentication('enabled', true); + $user->setTwoFactorAuthentication('type', 'google'); + $user->setTwoFactorAuthentication('secret', $secret); + + $url = $this->twoFactor->getQRContent($proxyUser); + + $qrResult = Builder::create() + ->writer(new PngWriter()) + ->data($url) + ->size(200) + ->build(); + + return new GenerateTwoFactorSetupResult( + secret: $secret, + qrDataUri: $qrResult->getDataUri(), + ); + } +} diff --git a/src/Handler/Login/GenerateTwoFactorSetupResult.php b/src/Handler/Login/GenerateTwoFactorSetupResult.php new file mode 100644 index 00000000..0d1ccb0f --- /dev/null +++ b/src/Handler/Login/GenerateTwoFactorSetupResult.php @@ -0,0 +1,25 @@ +resetPasswordLimiter->create($clientIp); + if (false === $limiter->consume(1)->isAccepted()) { + return new LostPasswordResult(error: 'user_reset_password_too_many_attempts'); + } + + if (!$user->isActive()) { + return new LostPasswordResult(error: 'user_inactive'); + } + + if (!$user->getEmail()) { + return new LostPasswordResult(error: 'user_no_email_address'); + } + + if (!$user->getPassword()) { + return new LostPasswordResult(error: 'user_no_password'); + } + + $token = Authentication::generateTokenByUser($user); + + try { + if (!$domain) { + throw new Exception('No main domain set in system settings, unable to generate reset password link'); + } + + $context = $this->router->getContext(); + $context->setHost($domain); + + $loginUrl = $this->router->generate('opendxp_admin_login_check', [ + 'token' => $token, + 'reset' => 'true', + ], UrlGeneratorInterface::ABSOLUTE_URL); + + $event = new LostPasswordEvent($user, $loginUrl); + $this->eventDispatcher->dispatch($event, AdminEvents::LOGIN_LOSTPASSWORD); + + if ($event->getSendMail()) { + $mail = Tool::getMail([$user->getEmail()], 'OpenDXP lost password service'); + $mail->setIgnoreDebugMode(true); + $mail->text("Login to OpenDXP and change your password using the following link. This temporary login link will expire in 24 hours: \r\n\r\n" . $loginUrl); + $mail->send(); + } + + return new LostPasswordResult( + error: null, + eventResponse: $event->hasResponse() ? $event->getResponse() : null, + ); + } catch (Exception $e) { + Logger::error('Error sending password recovery email: ' . $e->getMessage()); + + return new LostPasswordResult(error: 'lost_password_email_error'); + } + } +} diff --git a/src/Handler/Login/LostPasswordResult.php b/src/Handler/Login/LostPasswordResult.php new file mode 100644 index 00000000..71db170b --- /dev/null +++ b/src/Handler/Login/LostPasswordResult.php @@ -0,0 +1,27 @@ +userContext->getAdminUser(); + $proxyUser = $this->userContext->getAdminUserProxy(); + + $user->setTwoFactorAuthentication('enabled', true); + $user->setTwoFactorAuthentication('type', 'google'); + $user->setTwoFactorAuthentication('secret', $secret); + + if (!$this->twoFactor->checkCode($proxyUser, $authCode)) { + throw new Exception('2fa_wrong'); + } + + $user->save(); + } +} diff --git a/src/Handler/Misc/GetIconListHandler.php b/src/Handler/Misc/GetIconListHandler.php new file mode 100644 index 00000000..36db9d75 --- /dev/null +++ b/src/Handler/Misc/GetIconListHandler.php @@ -0,0 +1,83 @@ + FileSystemHelper::scanDirectory($iconDir . '/flat-color-icons/'), + 'white' => FileSystemHelper::scanDirectory($iconDir . '/flat-white-icons/'), + 'twemoji' => FileSystemHelper::scanDirectory($iconDir . '/twemoji/'), + 'flags' => $this->getFlags(), + default => [] + }; + + $source = match ($type) { + 'color', 'white' => + 'based on the ' . + 'Material Design Icons', + 'twemoji' => + 'based on the ' . + 'Twemoji icons', + default => '' + }; + + $extraInfo = null; + if ($type === 'twemoji') { + $extraInfo = 'ℹ Click on icon with green border to display all its related variants. Click on the letter to display flags with the clicked initial'; + } + + $iconsCss = file_get_contents($publicDir . '/css/icons.css'); + + return new GetIconListResult( + icons: $icons, + iconsCss: $iconsCss !== false ? $iconsCss : '', + type: $type, + extraInfo: $extraInfo, + source: $source, + ); + } + + private function getFlags(): array + { + $locales = Tool::getSupportedLocales(); + $languageOptions = []; + foreach (array_keys($locales) as $short) { + if (!empty($short)) { + $flag = AdminTool::getLanguageFlagFile($short, true, false); + if ($flag) { + $languageOptions[] = $flag; + } + } + } + + $languageOptions = array_unique($languageOptions); + sort($languageOptions); + + return $languageOptions; + } +} diff --git a/src/Handler/Misc/GetIconListResult.php b/src/Handler/Misc/GetIconListResult.php new file mode 100644 index 00000000..69432876 --- /dev/null +++ b/src/Handler/Misc/GetIconListResult.php @@ -0,0 +1,29 @@ +lazyInitialize('admin', $language); + + $translations = []; + + $fallbackLanguages = []; + if (null !== Locale::getRegion($language)) { + $fallbackLanguages[] = Locale::getPrimaryLanguage($language); + } + if ($language !== 'en') { + $fallbackLanguages[] = 'en'; + } + + foreach (['admin', 'admin_ext'] as $domain) { + $translations = array_replace($translations, $translator->getCatalogue($language)->all($domain)); + + foreach ($fallbackLanguages as $fallbackLanguage) { + $translator->lazyInitialize($domain, $fallbackLanguage); + foreach ($translator->getCatalogue($fallbackLanguage)->all($domain) as $key => $value) { + if (empty($translations[$key])) { + $translations[$key] = $value; + } + } + } + } + + return new GetJsonTranslationsResult(translations: $translations); + } +} diff --git a/src/Handler/Misc/GetJsonTranslationsResult.php b/src/Handler/Misc/GetJsonTranslationsResult.php new file mode 100644 index 00000000..3b1ac99b --- /dev/null +++ b/src/Handler/Misc/GetJsonTranslationsResult.php @@ -0,0 +1,25 @@ +deleteAll((int) $this->userContext->getAdminUser()?->getId()); + } +} diff --git a/src/Handler/Notification/DeleteNotificationHandler.php b/src/Handler/Notification/DeleteNotificationHandler.php new file mode 100644 index 00000000..8e568fff --- /dev/null +++ b/src/Handler/Notification/DeleteNotificationHandler.php @@ -0,0 +1,31 @@ +delete($id, (int) $this->userContext->getAdminUser()?->getId()); + } +} diff --git a/src/Handler/Notification/FindAllNotificationsHandler.php b/src/Handler/Notification/FindAllNotificationsHandler.php new file mode 100644 index 00000000..f7b2c0d2 --- /dev/null +++ b/src/Handler/Notification/FindAllNotificationsHandler.php @@ -0,0 +1,56 @@ + (int) $this->userContext->getAdminUser()?->getId()]; + + $parser = new NotificationServiceFilterParser($request); + foreach ($parser->parse() as $key => $val) { + $filter[$key] = $val; + } + + $options = [ + 'offset' => $offset, + 'limit' => $limit, + ]; + + $result = $service->findAll($filter, $options); + + $data = []; + foreach ($result['data'] as $notification) { + $data[] = $service->format($notification); + } + + return new FindAllNotificationsResult(data: $data, total: (int) $result['total']); + } +} diff --git a/src/Handler/Notification/FindAllNotificationsResult.php b/src/Handler/Notification/FindAllNotificationsResult.php new file mode 100644 index 00000000..78e95a81 --- /dev/null +++ b/src/Handler/Notification/FindAllNotificationsResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser()?->getId() ?? 0; + $result = $service->findLastUnread($userId, $lastUpdate); + $unread = $service->countAllUnread($userId); + + $data = []; + foreach ($result['data'] as $notification) { + $data[] = $service->format($notification); + } + + return new FindLastUnreadNotificationsResult( + data: $data, + total: (int) $result['total'], + unread: $unread, + ); + } +} diff --git a/src/Handler/Notification/FindLastUnreadNotificationsResult.php b/src/Handler/Notification/FindLastUnreadNotificationsResult.php new file mode 100644 index 00000000..1fd3efb5 --- /dev/null +++ b/src/Handler/Notification/FindLastUnreadNotificationsResult.php @@ -0,0 +1,27 @@ +userContext->getAdminUser()?->getId() ?? 0; + try { + $notification = $service->findAndMarkAsRead($id, $userId); + } catch (UnexpectedValueException $e) { + throw new NotFoundHttpException(sprintf('Notification with id %d not found', $id), $e); + } + + $data = $service->format($notification); + + return new FindNotificationResult(data: $data); + } +} diff --git a/src/Handler/Notification/FindNotificationResult.php b/src/Handler/Notification/FindNotificationResult.php new file mode 100644 index 00000000..30d30178 --- /dev/null +++ b/src/Handler/Notification/FindNotificationResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $data = []; + $group = $translator->trans('group', [], 'admin'); + + foreach ($service->findAll($adminUser) as $recipient) { + $prefix = $recipient->getType() === 'role' ? $group . ' - ' : ''; + + $data[] = [ + 'id' => $recipient->getId(), + 'text' => $prefix . $recipient->getName(), + ]; + } + + return new GetRecipientsResult(data: $data); + } +} diff --git a/src/Handler/Notification/GetRecipientsResult.php b/src/Handler/Notification/GetRecipientsResult.php new file mode 100644 index 00000000..87b5c09e --- /dev/null +++ b/src/Handler/Notification/GetRecipientsResult.php @@ -0,0 +1,25 @@ +findAndMarkAsRead($id, (int) $this->userContext->getAdminUser()?->getId()); + } +} diff --git a/src/Handler/Notification/SendNotificationHandler.php b/src/Handler/Notification/SendNotificationHandler.php new file mode 100644 index 00000000..74aab687 --- /dev/null +++ b/src/Handler/Notification/SendNotificationHandler.php @@ -0,0 +1,44 @@ +userContext->getAdminUser()?->getId(); + + if (User::getById($recipientId) instanceof User) { + $service->sendToUser($recipientId, $fromUserId, $title, $message, $element); + } else { + $service->sendToGroup($recipientId, $fromUserId, $title, $message, $element); + } + } +} diff --git a/src/Handler/Portal/AddWidgetHandler.php b/src/Handler/Portal/AddWidgetHandler.php new file mode 100644 index 00000000..2a9d501b --- /dev/null +++ b/src/Handler/Portal/AddWidgetHandler.php @@ -0,0 +1,51 @@ +dashboardFactory->create(); + + $config = $dashboard->getDashboard($dashboardId); + + $nextId = 0; + foreach ($config['positions'] as $col) { + foreach ($col as $row) { + $nextId = ($row['id'] > $nextId ? $row['id'] : $nextId); + } + } + + $nextId += 1; + $config['positions'][0][] = [ + 'id' => $nextId, + 'type' => $type, + 'config' => null, + ]; + + $dashboard->saveDashboard($dashboardId, $config); + + return new AddWidgetResult(id: $nextId); + } +} diff --git a/src/Handler/Portal/AddWidgetResult.php b/src/Handler/Portal/AddWidgetResult.php new file mode 100644 index 00000000..470b09b2 --- /dev/null +++ b/src/Handler/Portal/AddWidgetResult.php @@ -0,0 +1,24 @@ +dashboardFactory->create(); + + $dashboards = $dashboard->getAllDashboards(); + if (isset($dashboards[$key])) { + throw new \InvalidArgumentException('name_already_in_use'); + } + + $dashboard->saveDashboard($key); + } +} diff --git a/src/Handler/Portal/DeleteDashboardHandler.php b/src/Handler/Portal/DeleteDashboardHandler.php new file mode 100644 index 00000000..5e5b5eef --- /dev/null +++ b/src/Handler/Portal/DeleteDashboardHandler.php @@ -0,0 +1,32 @@ +dashboardFactory->create(); + $dashboard->deleteDashboard($key); + } +} diff --git a/src/Handler/Portal/GetDashboardConfigurationHandler.php b/src/Handler/Portal/GetDashboardConfigurationHandler.php new file mode 100644 index 00000000..8406f890 --- /dev/null +++ b/src/Handler/Portal/GetDashboardConfigurationHandler.php @@ -0,0 +1,33 @@ +dashboardFactory->create(); + + return new GetDashboardConfigurationResult(config: $dashboard->getDashboard($key ?? 'welcome')); + } +} diff --git a/src/Handler/Portal/GetDashboardConfigurationResult.php b/src/Handler/Portal/GetDashboardConfigurationResult.php new file mode 100644 index 00000000..dc836a80 --- /dev/null +++ b/src/Handler/Portal/GetDashboardConfigurationResult.php @@ -0,0 +1,24 @@ +dashboardFactory->create(); + + $dashboards = []; + foreach (array_keys($dashboard->getAllDashboards()) as $key) { + if ($key !== 'welcome') { + $dashboards[] = $key; + } + } + + return new GetDashboardListResult(dashboards: $dashboards); + } +} diff --git a/src/Handler/Portal/GetDashboardListResult.php b/src/Handler/Portal/GetDashboardListResult.php new file mode 100644 index 00000000..ce0178ed --- /dev/null +++ b/src/Handler/Portal/GetDashboardListResult.php @@ -0,0 +1,24 @@ +fetchOne( + 'SELECT COUNT(*) AS count FROM objects WHERE modificationDate > ? AND modificationDate < ?', + [$start, $end] + ); + $a = $db->fetchOne( + 'SELECT COUNT(*) AS count FROM assets WHERE modificationDate > ? AND modificationDate < ?', + [$start, $end] + ); + $d = $db->fetchOne( + 'SELECT COUNT(*) AS count FROM documents WHERE modificationDate > ? AND modificationDate < ?', + [$start, $end] + ); + + $date = new DateTime(); + $date->setTimestamp($start); + + $data[] = [ + 'timestamp' => $start, + 'datetext' => $date->format('Y-m-d'), + 'objects' => (int) $o, + 'documents' => (int) $d, + 'assets' => (int) $a, + ]; + } + + return new GetModificationStatisticsResult(data: array_reverse($data)); + } +} diff --git a/src/Handler/Portal/GetModificationStatisticsResult.php b/src/Handler/Portal/GetModificationStatisticsResult.php new file mode 100644 index 00000000..c64820c7 --- /dev/null +++ b/src/Handler/Portal/GetModificationStatisticsResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser()?->getId() ?? 0; + $list = Asset::getList([ + 'limit' => 10, + 'order' => 'DESC', + 'orderKey' => 'modificationDate', + 'condition' => 'userModification = ' . $userId, + ]); + + $assets = []; + foreach ($list as $doc) { + /** @var Asset $doc */ + if ($doc->isAllowed('view')) { + $assets[] = [ + 'id' => $doc->getId(), + 'type' => $doc->getType(), + 'path' => $doc->getRealFullPath(), + 'date' => $doc->getModificationDate(), + ]; + } + } + + return new GetModifiedAssetsResult(assets: $assets); + } +} diff --git a/src/Handler/Portal/GetModifiedAssetsResult.php b/src/Handler/Portal/GetModifiedAssetsResult.php new file mode 100644 index 00000000..1622ba54 --- /dev/null +++ b/src/Handler/Portal/GetModifiedAssetsResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser()?->getId() ?? 0; + $list = Document::getList([ + 'limit' => 10, + 'order' => 'DESC', + 'orderKey' => 'modificationDate', + 'condition' => 'userModification = ' . $userId, + ]); + + $documents = []; + foreach ($list as $doc) { + if ($doc->isAllowed('view')) { + $documents[] = [ + 'id' => $doc->getId(), + 'type' => $doc->getType(), + 'path' => $doc->getRealFullPath(), + 'date' => $doc->getModificationDate(), + ]; + } + } + + return new GetModifiedDocumentsResult(documents: $documents); + } +} diff --git a/src/Handler/Portal/GetModifiedDocumentsResult.php b/src/Handler/Portal/GetModifiedDocumentsResult.php new file mode 100644 index 00000000..c04b5282 --- /dev/null +++ b/src/Handler/Portal/GetModifiedDocumentsResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser()?->getId() ?? 0; + $list = DataObject::getList([ + 'limit' => 10, + 'order' => 'DESC', + 'orderKey' => 'modificationDate', + 'condition' => 'userModification = ' . $userId, + ]); + + $objects = []; + foreach ($list as $object) { + if ($object->isAllowed('view')) { + $objects[] = [ + 'id' => $object->getId(), + 'type' => $object->getType(), + 'path' => $object->getRealFullPath(), + 'date' => $object->getModificationDate(), + ]; + } + } + + return new GetModifiedObjectsResult(objects: $objects); + } +} diff --git a/src/Handler/Portal/GetModifiedObjectsResult.php b/src/Handler/Portal/GetModifiedObjectsResult.php new file mode 100644 index 00000000..b8236fec --- /dev/null +++ b/src/Handler/Portal/GetModifiedObjectsResult.php @@ -0,0 +1,25 @@ +dashboardFactory->create(); + + $config = $dashboard->getDashboard($dashboardId); + $newConfig = [[], []]; + $colCount = 0; + + foreach ($config['positions'] as $col) { + foreach ($col as $row) { + if ($row['id'] !== $widgetId) { + $newConfig[$colCount][] = $row; + } + } + $colCount++; + } + + $config['positions'] = $newConfig; + $dashboard->saveDashboard($dashboardId, $config); + } +} diff --git a/src/Handler/Portal/ReorderWidgetHandler.php b/src/Handler/Portal/ReorderWidgetHandler.php new file mode 100644 index 00000000..eef4ff5b --- /dev/null +++ b/src/Handler/Portal/ReorderWidgetHandler.php @@ -0,0 +1,52 @@ +dashboardFactory->create(); + + $config = $dashboard->getDashboard($dashboardId); + $newConfig = [[], []]; + $colCount = 0; + $toMove = null; + + foreach ($config['positions'] as $col) { + foreach ($col as $item) { + if ($item['id'] !== $widgetId) { + $newConfig[$colCount][] = $item; + } else { + $toMove = $item; + } + } + $colCount++; + } + + array_splice($newConfig[$column], $row, 0, [$toMove]); + + $config['positions'] = $newConfig; + $dashboard->saveDashboard($dashboardId, $config); + } +} diff --git a/src/Handler/Portal/UpdatePortletConfigHandler.php b/src/Handler/Portal/UpdatePortletConfigHandler.php new file mode 100644 index 00000000..b91b3274 --- /dev/null +++ b/src/Handler/Portal/UpdatePortletConfigHandler.php @@ -0,0 +1,43 @@ +dashboardFactory->create(); + + $config = $dashboard->getDashboard($dashboardKey); + foreach ($config['positions'] as &$col) { + foreach ($col as &$portlet) { + if ($portlet['id'] === $portletId) { + $portlet['config'] = $configuration; + break; + } + } + } + + $dashboard->saveDashboard($dashboardKey, $config); + } +} diff --git a/src/Handler/Recyclebin/AddToRecyclebinHandler.php b/src/Handler/Recyclebin/AddToRecyclebinHandler.php new file mode 100644 index 00000000..f12c85cb --- /dev/null +++ b/src/Handler/Recyclebin/AddToRecyclebinHandler.php @@ -0,0 +1,48 @@ +userContext->getAdminUser(); + $element = Service::getElementById($type, $id); + + if (!$element) { + return; + } + + $list = $element::getList(['unpublished' => true]); + $list->setCondition('`path` LIKE ' . $list->quote($list->escapeLike($element->getRealFullPath()) . '/%')); + $children = $list->getTotalCount(); + + if ($children <= 100) { + Recyclebin\Item::create($element, $adminUser); + } + } +} diff --git a/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php b/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php new file mode 100644 index 00000000..8d15d1b6 --- /dev/null +++ b/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php @@ -0,0 +1,31 @@ +delete(); + } + } +} \ No newline at end of file diff --git a/src/Handler/Recyclebin/ListRecyclebinHandler.php b/src/Handler/Recyclebin/ListRecyclebinHandler.php new file mode 100644 index 00000000..9e9ec778 --- /dev/null +++ b/src/Handler/Recyclebin/ListRecyclebinHandler.php @@ -0,0 +1,109 @@ +setLimit($limit); + $list->setOffset($offset); + $list->setOrderKey($orderKey); + $list->setOrder($order); + + $conditionFilters = []; + + if ($filterFullText) { + $conditionFilters[] = '`path` LIKE ' . $list->quote('%' . $list->escapeLike($filterFullText) . '%'); + } + + foreach ($filters as $filter) { + $operator = '='; + + $filterField = $filter['property']; + $filterOperator = $filter['operator']; + + if ($filter['type'] === 'string') { + $operator = 'LIKE'; + } elseif ($filter['type'] === 'numeric') { + if ($filterOperator === 'lt') { + $operator = '<'; + } elseif ($filterOperator === 'gt') { + $operator = '>'; + } elseif ($filterOperator === 'eq') { + $operator = '='; + } + } elseif ($filter['type'] === 'date') { + if ($filterOperator === 'lt') { + $operator = '<'; + } elseif ($filterOperator === 'gt') { + $operator = '>'; + } elseif ($filterOperator === 'eq') { + $operator = '='; + } + $filter['value'] = strtotime($filter['value']); + } elseif ($filter['type'] === 'list') { + $operator = '='; + } elseif ($filter['type'] === 'boolean') { + $operator = '='; + $filter['value'] = (int) $filter['value']; + } + + $value = ($filter['value'] ?? ''); + if ($operator === 'LIKE') { + $value = '%' . $value . '%'; + } + + $field = $db->quoteIdentifier($filterField); + if (($filter['field'] ?? false) === 'fullpath') { + $field = 'CONCAT(`path`,filename)'; + } + + if ($filter['type'] === 'date' && $operator === '=') { + $maxTime = $value + (86400 - 1); + $condition = $field . ' BETWEEN ' . $db->quote($value) . ' AND ' . $db->quote($maxTime); + $conditionFilters[] = $condition; + } else { + $conditionFilters[] = $field . $operator . ' ' . $db->quote($value); + } + } + + if ($conditionFilters !== []) { + $list->setCondition(implode(' AND ', $conditionFilters)); + } + + $items = $list->load(); + $data = []; + foreach ($items as $item) { + $data[] = $item->getObjectVars(); + } + + return new ListRecyclebinResult(data: $data, total: $list->getTotalCount()); + } +} diff --git a/src/Handler/Recyclebin/ListRecyclebinResult.php b/src/Handler/Recyclebin/ListRecyclebinResult.php new file mode 100644 index 00000000..c5596407 --- /dev/null +++ b/src/Handler/Recyclebin/ListRecyclebinResult.php @@ -0,0 +1,26 @@ +restore(); + } +} diff --git a/src/Handler/Settings/AddThumbnailHandler.php b/src/Handler/Settings/AddThumbnailHandler.php new file mode 100644 index 00000000..ace75079 --- /dev/null +++ b/src/Handler/Settings/AddThumbnailHandler.php @@ -0,0 +1,46 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + $pipe->setName($name); + $pipe->save(); + + return new AddThumbnailResult(id: $pipe->getName(), created: true); + } + + if (!$pipe->isWriteable()) { + throw new ConfigWriteException(); + } + + return new AddThumbnailResult(id: $pipe->getName(), created: false); + } +} diff --git a/src/Handler/Settings/AddThumbnailResult.php b/src/Handler/Settings/AddThumbnailResult.php new file mode 100644 index 00000000..58393b44 --- /dev/null +++ b/src/Handler/Settings/AddThumbnailResult.php @@ -0,0 +1,26 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + $pipe->setName($name); + $pipe->save(); + + return new AddVideoThumbnailResult(id: $pipe->getName(), created: true); + } + + if (!$pipe->isWriteable()) { + throw new ConfigWriteException(); + } + + return new AddVideoThumbnailResult(id: $pipe->getName(), created: false); + } +} diff --git a/src/Handler/Settings/AddVideoThumbnailResult.php b/src/Handler/Settings/AddVideoThumbnailResult.php new file mode 100644 index 00000000..04902dc4 --- /dev/null +++ b/src/Handler/Settings/AddVideoThumbnailResult.php @@ -0,0 +1,26 @@ +cache->clearAll(); + + if ($this->filesystem->exists(OPENDXP_CACHE_DIRECTORY)) { + $this->filesystem->remove(OPENDXP_CACHE_DIRECTORY); + } + + $this->filesystem->dumpFile(OPENDXP_CACHE_DIRECTORY . '/.gitkeep', ''); + + $this->eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR); + } +} diff --git a/src/Handler/Settings/ClearOutputCacheHandler.php b/src/Handler/Settings/ClearOutputCacheHandler.php new file mode 100644 index 00000000..d1e69007 --- /dev/null +++ b/src/Handler/Settings/ClearOutputCacheHandler.php @@ -0,0 +1,35 @@ +eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR_FULLPAGE_CACHE); + } +} diff --git a/src/Handler/Settings/ClearSymfonyCacheHandler.php b/src/Handler/Settings/ClearSymfonyCacheHandler.php new file mode 100644 index 00000000..75965eab --- /dev/null +++ b/src/Handler/Settings/ClearSymfonyCacheHandler.php @@ -0,0 +1,47 @@ +kernel->getEnvironment() === $environment) { + foreach ($this->eventDispatcher->getListeners(KernelEvents::TERMINATE) as $listener) { + $this->eventDispatcher->removeListener(KernelEvents::TERMINATE, $listener); + } + + foreach ($this->eventDispatcher->getListeners(KernelEvents::EXCEPTION) as $listener) { + $this->eventDispatcher->removeListener(KernelEvents::EXCEPTION, $listener); + } + } + + $this->cacheClearer->clear($environment); + } +} diff --git a/src/Handler/Settings/ClearTemporaryFilesHandler.php b/src/Handler/Settings/ClearTemporaryFilesHandler.php new file mode 100644 index 00000000..ad7f9d49 --- /dev/null +++ b/src/Handler/Settings/ClearTemporaryFilesHandler.php @@ -0,0 +1,44 @@ +deleteDirectory('/'); + Db::get()->executeStatement('TRUNCATE TABLE assets_image_thumbnail_cache'); + + Tool\Storage::get('asset_cache')->deleteDirectory('/'); + + // system files + FileSystemHelper::recursiveDelete(OPENDXP_SYSTEM_TEMP_DIRECTORY, false); + + $this->eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR_TEMPORARY_FILES); + } +} diff --git a/src/Handler/Settings/CreatePredefinedMetadataHandler.php b/src/Handler/Settings/CreatePredefinedMetadataHandler.php new file mode 100644 index 00000000..78e58387 --- /dev/null +++ b/src/Handler/Settings/CreatePredefinedMetadataHandler.php @@ -0,0 +1,52 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + $metadata = Metadata\Predefined::create(); + $metadata->setValues($data); + + $existingItem = Metadata\Predefined\Listing::getByKeyAndLanguage( + $metadata->getName(), + $metadata->getLanguage(), + $metadata->getTargetSubtype() + ); + + if ($existingItem) { + throw new BadRequestHttpException('rule_violation'); + } + + $metadata->save(); + + $responseData = $metadata->getObjectVars(); + $responseData['writeable'] = $metadata->isWriteable(); + + return new CreatePredefinedMetadataResult(data: $responseData); + } +} diff --git a/src/Handler/Settings/CreatePredefinedMetadataResult.php b/src/Handler/Settings/CreatePredefinedMetadataResult.php new file mode 100644 index 00000000..ecfe2bea --- /dev/null +++ b/src/Handler/Settings/CreatePredefinedMetadataResult.php @@ -0,0 +1,25 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + $property = Property\Predefined::create(); + $property->setValues($data); + $property->save(); + + $responseData = $property->getObjectVars(); + $responseData['writeable'] = $property->isWriteable(); + + return new CreatePredefinedPropertyResult(data: $responseData); + } +} diff --git a/src/Handler/Settings/CreatePredefinedPropertyResult.php b/src/Handler/Settings/CreatePredefinedPropertyResult.php new file mode 100644 index 00000000..919de52e --- /dev/null +++ b/src/Handler/Settings/CreatePredefinedPropertyResult.php @@ -0,0 +1,25 @@ +setValues($data); + $setting->save(); + + return new CreateWebsiteSettingResult(data: $setting->getObjectVars()); + } +} diff --git a/src/Handler/Settings/CreateWebsiteSettingResult.php b/src/Handler/Settings/CreateWebsiteSettingResult.php new file mode 100644 index 00000000..4d921c58 --- /dev/null +++ b/src/Handler/Settings/CreateWebsiteSettingResult.php @@ -0,0 +1,25 @@ +fileExists(self::LOGO_PATH)) { + $storage->delete(self::LOGO_PATH); + } + } +} diff --git a/src/Handler/Settings/DeletePredefinedMetadataHandler.php b/src/Handler/Settings/DeletePredefinedMetadataHandler.php new file mode 100644 index 00000000..c1dce578 --- /dev/null +++ b/src/Handler/Settings/DeletePredefinedMetadataHandler.php @@ -0,0 +1,35 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + $metadata->delete(); + } +} diff --git a/src/Handler/Settings/DeletePredefinedPropertyHandler.php b/src/Handler/Settings/DeletePredefinedPropertyHandler.php new file mode 100644 index 00000000..094ddf6e --- /dev/null +++ b/src/Handler/Settings/DeletePredefinedPropertyHandler.php @@ -0,0 +1,35 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + $property->delete(); + } +} diff --git a/src/Handler/Settings/DeleteThumbnailHandler.php b/src/Handler/Settings/DeleteThumbnailHandler.php new file mode 100644 index 00000000..4fb64a68 --- /dev/null +++ b/src/Handler/Settings/DeleteThumbnailHandler.php @@ -0,0 +1,35 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + $pipe->delete(); + } +} diff --git a/src/Handler/Settings/DeleteVideoThumbnailHandler.php b/src/Handler/Settings/DeleteVideoThumbnailHandler.php new file mode 100644 index 00000000..abcba483 --- /dev/null +++ b/src/Handler/Settings/DeleteVideoThumbnailHandler.php @@ -0,0 +1,35 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + $pipe->delete(); + } +} diff --git a/src/Handler/Settings/DeleteWebsiteSettingHandler.php b/src/Handler/Settings/DeleteWebsiteSettingHandler.php new file mode 100644 index 00000000..4c73cea9 --- /dev/null +++ b/src/Handler/Settings/DeleteWebsiteSettingHandler.php @@ -0,0 +1,34 @@ +delete(); + } +} diff --git a/src/Handler/Settings/DisplayCustomLogoHandler.php b/src/Handler/Settings/DisplayCustomLogoHandler.php new file mode 100644 index 00000000..a6d08234 --- /dev/null +++ b/src/Handler/Settings/DisplayCustomLogoHandler.php @@ -0,0 +1,45 @@ +fileExists(self::LOGO_PATH)) { + try { + $mime = $storage->mimeType(self::LOGO_PATH); + $stream = $storage->readStream(self::LOGO_PATH); + } catch (Exception) { + // keep default stream and mime on storage error + } + } + + return new DisplayCustomLogoResult(mime: $mime, stream: $stream); + } +} diff --git a/src/Handler/Settings/DisplayCustomLogoResult.php b/src/Handler/Settings/DisplayCustomLogoResult.php new file mode 100644 index 00000000..acfae3ce --- /dev/null +++ b/src/Handler/Settings/DisplayCustomLogoResult.php @@ -0,0 +1,26 @@ +config->getAdminSystemSettingsConfig()); + } +} diff --git a/src/Handler/Settings/GetAppearanceSettingsResult.php b/src/Handler/Settings/GetAppearanceSettingsResult.php new file mode 100644 index 00000000..ed3a3905 --- /dev/null +++ b/src/Handler/Settings/GetAppearanceSettingsResult.php @@ -0,0 +1,23 @@ + $lang, 'display' => $locales[$lang]]; + } + } + + usort($langs, static fn ($a, $b) => strcmp($a['display'], $b['display'])); + + return new GetAvailableAdminLanguagesResult(langs: $langs); + } +} diff --git a/src/Handler/Settings/GetAvailableAdminLanguagesResult.php b/src/Handler/Settings/GetAvailableAdminLanguagesResult.php new file mode 100644 index 00000000..bdec0cb8 --- /dev/null +++ b/src/Handler/Settings/GetAvailableAdminLanguagesResult.php @@ -0,0 +1,23 @@ + 'password_hash', 'value' => 'password_hash']]; + + foreach (hash_algos() as $algorithm) { + $options[] = [ + 'key' => $algorithm . ' (' . $this->translator->trans('deprecated', [], 'admin') . ')', + 'value' => $algorithm, + ]; + } + + return new GetAvailableAlgorithmsResult(options: $options); + } +} diff --git a/src/Handler/Settings/GetAvailableAlgorithmsResult.php b/src/Handler/Settings/GetAvailableAlgorithmsResult.php new file mode 100644 index 00000000..06b49a50 --- /dev/null +++ b/src/Handler/Settings/GetAvailableAlgorithmsResult.php @@ -0,0 +1,23 @@ +localeService->getDisplayRegions(); + asort($countries); + + $options = []; + foreach ($countries as $short => $translation) { + if (strlen((string) $short) === 2) { + $options[] = ['key' => $translation . ' (' . $short . ')', 'value' => $short]; + } + } + + return new GetAvailableCountriesResult(options: $options); + } +} diff --git a/src/Handler/Settings/GetAvailableCountriesResult.php b/src/Handler/Settings/GetAvailableCountriesResult.php new file mode 100644 index 00000000..c3042288 --- /dev/null +++ b/src/Handler/Settings/GetAvailableCountriesResult.php @@ -0,0 +1,23 @@ +load(); + $sites = []; + + if (!$excludeMainSite) { + $sites[] = [ + 'id' => 0, + 'rootId' => 1, + 'domains' => '', + 'rootPath' => '/', + 'domain' => $this->translator->trans('main_site', [], 'admin'), + ]; + } + + foreach ($sitesObjects as $site) { + if ($site->getRootDocument()) { + if ($site->getMainDomain()) { + $sites[] = [ + 'id' => $site->getId(), + 'rootId' => $site->getRootId(), + 'domains' => implode(',', $site->getDomains()), + 'rootPath' => $site->getRootPath(), + 'domain' => $site->getMainDomain(), + ]; + } + } else { + $site->delete(); + } + } + + return new GetAvailableSitesResult(sites: $sites); + } +} diff --git a/src/Handler/Settings/GetAvailableSitesResult.php b/src/Handler/Settings/GetAvailableSitesResult.php new file mode 100644 index 00000000..fef3e4fb --- /dev/null +++ b/src/Handler/Settings/GetAvailableSitesResult.php @@ -0,0 +1,23 @@ +setFilter(fn (Asset\Image\Thumbnail\Config $config) => $config->isDownloadable()); + + foreach ($list->getThumbnails() as $item) { + $thumbnails[] = [ + 'id' => $item->getName(), + 'text' => $item->getName(), + ]; + } + + return new GetDownloadableThumbnailsResult(thumbnails: $thumbnails); + } +} diff --git a/src/Handler/Settings/GetDownloadableThumbnailsResult.php b/src/Handler/Settings/GetDownloadableThumbnailsResult.php new file mode 100644 index 00000000..5740215b --- /dev/null +++ b/src/Handler/Settings/GetDownloadableThumbnailsResult.php @@ -0,0 +1,25 @@ +getGroup() ?? ''; + if ($group === 'default' || $group === $itemGroup) { + $item->expand(); + $data = $item->getObjectVars(); + $data['writeable'] = $item->isWriteable(); + $result[] = $data; + } + } + + return new GetFilteredPredefinedMetadataResult(data: $result); + } +} diff --git a/src/Handler/Settings/GetFilteredPredefinedMetadataResult.php b/src/Handler/Settings/GetFilteredPredefinedMetadataResult.php new file mode 100644 index 00000000..19bcc2fc --- /dev/null +++ b/src/Handler/Settings/GetFilteredPredefinedMetadataResult.php @@ -0,0 +1,23 @@ +setFilter(function (Metadata\Predefined $predefined) use ($filter) { + foreach ($predefined->getObjectVars() as $value) { + if (stripos((string) $value, $filter) !== false) { + return true; + } + } + + return false; + }); + } + + $properties = []; + foreach ($list->getDefinitions() as $metadata) { + $metadata->expand(); + $data = $metadata->getObjectVars(); + $data['writeable'] = $metadata->isWriteable(); + $properties[] = $data; + } + + return new GetPredefinedMetadataListResult(data: $properties, total: $list->getTotalCount()); + } +} diff --git a/src/Handler/Settings/GetPredefinedMetadataListResult.php b/src/Handler/Settings/GetPredefinedMetadataListResult.php new file mode 100644 index 00000000..c5176613 --- /dev/null +++ b/src/Handler/Settings/GetPredefinedMetadataListResult.php @@ -0,0 +1,26 @@ +setFilter(function (Property\Predefined $predefined) use ($filter) { + foreach ($predefined->getObjectVars() as $value) { + if ($value) { + $cellValues = is_array($value) ? $value : [$value]; + + foreach ($cellValues as $cellValue) { + if (stripos((string) $cellValue, $filter) !== false) { + return true; + } + } + } + } + + return false; + }); + } + + $properties = []; + foreach ($list->getProperties() as $property) { + $data = $property->getObjectVars(); + $data['writeable'] = $property->isWriteable(); + $properties[] = $data; + } + + return new GetPredefinedPropertiesListResult(data: $properties, total: $list->getTotalCount()); + } +} diff --git a/src/Handler/Settings/GetPredefinedPropertiesListResult.php b/src/Handler/Settings/GetPredefinedPropertiesListResult.php new file mode 100644 index 00000000..c7e5cd2a --- /dev/null +++ b/src/Handler/Settings/GetPredefinedPropertiesListResult.php @@ -0,0 +1,26 @@ +config->getSystemSettingsConfig(); + + // If required languages is empty it's the same as if all languages are required. Therefore, we + // need to overwrite the value with the valid languages value to have all languages required + if (empty($config['general']['required_languages'])) { + $config['general']['required_languages'] = $config['general']['valid_languages']; + } + + $values = [ + 'general' => $config['general'], + 'documents' => $config['documents'], + 'assets' => $config['assets'], + 'objects' => $config['objects'], + 'email' => $config['email'], + 'writeable' => $config['writeable'], + ]; + + $locales = Tool::getSupportedLocales(); + $languageOptions = []; + $validLanguages = []; + + foreach ($locales as $short => $translation) { + if (!empty($short)) { + $languageOptions[] = ['language' => $short, 'display' => $translation . " ($short)"]; + $validLanguages[] = $short; + } + } + + foreach ($values['general']['valid_languages'] as $existingValue) { + if (!in_array($existingValue, $validLanguages, true)) { + $languageOptions[] = ['language' => $existingValue, 'display' => $existingValue]; + } + } + + return new GetSystemSettingsResult(values: $values, languages: $languageOptions); + } +} diff --git a/src/Handler/Settings/GetSystemSettingsResult.php b/src/Handler/Settings/GetSystemSettingsResult.php new file mode 100644 index 00000000..6757e31c --- /dev/null +++ b/src/Handler/Settings/GetSystemSettingsResult.php @@ -0,0 +1,26 @@ +getObjectVars(); + $data['writeable'] = $pipe->isWriteable(); + + return new GetThumbnailResult(data: $data); + } +} diff --git a/src/Handler/Settings/GetThumbnailResult.php b/src/Handler/Settings/GetThumbnailResult.php new file mode 100644 index 00000000..21e9eea0 --- /dev/null +++ b/src/Handler/Settings/GetThumbnailResult.php @@ -0,0 +1,25 @@ +getThumbnails() as $item) { + if ($item->getGroup()) { + if (empty($groups[$item->getGroup()])) { + $groups[$item->getGroup()] = [ + 'id' => 'group_' . $item->getName(), + 'text' => htmlspecialchars($item->getGroup()), + 'expandable' => true, + 'leaf' => false, + 'allowChildren' => true, + 'iconCls' => 'opendxp_icon_folder', + 'group' => $item->getGroup(), + 'children' => [], + ]; + } + $groups[$item->getGroup()]['children'][] = [ + 'id' => $item->getName(), + 'text' => $item->getName(), + 'leaf' => true, + 'iconCls' => 'opendxp_icon_thumbnails', + 'cls' => 'opendxp_treenode_disabled', + 'writeable' => $item->isWriteable(), + ]; + } else { + $thumbnails[] = [ + 'id' => $item->getName(), + 'text' => $item->getName(), + 'leaf' => true, + 'iconCls' => 'opendxp_icon_thumbnails', + 'cls' => 'opendxp_treenode_disabled', + 'writeable' => $item->isWriteable(), + ]; + } + } + + foreach ($groups as $group) { + $thumbnails[] = $group; + } + + return new GetThumbnailTreeResult(nodes: $thumbnails); + } +} diff --git a/src/Handler/Settings/GetThumbnailTreeResult.php b/src/Handler/Settings/GetThumbnailTreeResult.php new file mode 100644 index 00000000..df8eaadc --- /dev/null +++ b/src/Handler/Settings/GetThumbnailTreeResult.php @@ -0,0 +1,25 @@ +getObjectVars(); + $data['writeable'] = $pipe->isWriteable(); + + return new GetVideoThumbnailResult(data: $data); + } +} diff --git a/src/Handler/Settings/GetVideoThumbnailListHandler.php b/src/Handler/Settings/GetVideoThumbnailListHandler.php new file mode 100644 index 00000000..8ac1cb0f --- /dev/null +++ b/src/Handler/Settings/GetVideoThumbnailListHandler.php @@ -0,0 +1,40 @@ + 'opendxp-system-treepreview', 'text' => 'original'], + ]; + $list = new Asset\Video\Thumbnail\Config\Listing(); + + foreach ($list->getThumbnails() as $item) { + $thumbnails[] = [ + 'id' => $item->getName(), + 'text' => $item->getName(), + ]; + } + + return new GetVideoThumbnailListResult(thumbnails: $thumbnails); + } +} diff --git a/src/Handler/Settings/GetVideoThumbnailListResult.php b/src/Handler/Settings/GetVideoThumbnailListResult.php new file mode 100644 index 00000000..47d6a81e --- /dev/null +++ b/src/Handler/Settings/GetVideoThumbnailListResult.php @@ -0,0 +1,25 @@ +getThumbnails() as $item) { + if ($item->getGroup()) { + if (empty($groups[$item->getGroup()])) { + $groups[$item->getGroup()] = [ + 'id' => 'group_' . $item->getName(), + 'text' => htmlspecialchars($item->getGroup()), + 'expandable' => true, + 'leaf' => false, + 'allowChildren' => true, + 'iconCls' => 'opendxp_icon_folder', + 'group' => $item->getGroup(), + 'children' => [], + ]; + } + $groups[$item->getGroup()]['children'][] = [ + 'id' => $item->getName(), + 'text' => $item->getName(), + 'leaf' => true, + 'iconCls' => 'opendxp_icon_videothumbnails', + 'cls' => 'opendxp_treenode_disabled', + 'writeable' => $item->isWriteable(), + ]; + } else { + $thumbnails[] = [ + 'id' => $item->getName(), + 'text' => $item->getName(), + 'leaf' => true, + 'iconCls' => 'opendxp_icon_videothumbnails', + 'cls' => 'opendxp_treenode_disabled', + 'writeable' => $item->isWriteable(), + ]; + } + } + + foreach ($groups as $group) { + $thumbnails[] = $group; + } + + return new GetVideoThumbnailTreeResult(nodes: $thumbnails); + } +} diff --git a/src/Handler/Settings/GetVideoThumbnailTreeResult.php b/src/Handler/Settings/GetVideoThumbnailTreeResult.php new file mode 100644 index 00000000..311cf058 --- /dev/null +++ b/src/Handler/Settings/GetVideoThumbnailTreeResult.php @@ -0,0 +1,25 @@ +setLimit($limit); + $list->setOffset($offset); + + if ($orderKey) { + $list->setOrderKey($orderKey); + $list->setOrder($order); + } else { + $list->setOrderKey('name'); + $list->setOrder('asc'); + } + + if ($filter) { + $list->setCondition('`name` LIKE ' . $list->quote('%' . $filter . '%')); + } + + $totalCount = $list->getTotalCount(); + $items = $list->load(); + + $settings = []; + foreach ($items as $item) { + $settings[] = $this->buildEditModeData($item); + } + + return new GetWebsiteSettingsListResult(data: $settings, total: $totalCount); + } + + /** + * @return array{id: ?int, name: string, language: string, type: string, data: mixed, siteId: ?int, creationDate: ?int, modificationDate: ?int} + */ + private function buildEditModeData(WebsiteSetting $item): array + { + $resultItem = [ + 'id' => $item->getId(), + 'name' => $item->getName(), + 'language' => $item->getLanguage(), + 'type' => $item->getType(), + 'data' => null, + 'siteId' => $item->getSiteId(), + 'creationDate' => $item->getCreationDate(), + 'modificationDate' => $item->getModificationDate(), + ]; + + switch ($item->getType()) { + case 'document': + case 'asset': + case 'object': + $element = $item->getData(); + if ($element) { + $resultItem['data'] = $element->getRealFullPath(); + } + + break; + default: + $resultItem['data'] = $item->getData(); + + break; + } + + return $resultItem; + } +} diff --git a/src/Handler/Settings/GetWebsiteSettingsListResult.php b/src/Handler/Settings/GetWebsiteSettingsListResult.php new file mode 100644 index 00000000..08a4f567 --- /dev/null +++ b/src/Handler/Settings/GetWebsiteSettingsListResult.php @@ -0,0 +1,26 @@ +config->save($values); + ($this->clearSymfonyCache)($env); + $this->stopMessengerWorkers(); + + $clearOpenDxpCache = $this->clearOpenDxpCache; + $this->eventDispatcher->addListener(KernelEvents::TERMINATE, static function (TerminateEvent $event) use ($clearOpenDxpCache): void { + // delay to ensure messenger:stop-workers signal has been processed before cache is cleared + sleep(2); + $clearOpenDxpCache(); + }); + } +} diff --git a/src/Handler/Settings/SaveSystemSettingsHandler.php b/src/Handler/Settings/SaveSystemSettingsHandler.php new file mode 100644 index 00000000..cfe167f8 --- /dev/null +++ b/src/Handler/Settings/SaveSystemSettingsHandler.php @@ -0,0 +1,50 @@ +config->save($values); + ($this->clearSymfonyCache)($env); + $this->stopMessengerWorkers(); + + $clearOpenDxpCache = $this->clearOpenDxpCache; + $this->eventDispatcher->addListener(KernelEvents::TERMINATE, static function (TerminateEvent $event) use ($clearOpenDxpCache): void { + // delay to ensure messenger:stop-workers signal has been processed before cache is cleared + sleep(2); + $clearOpenDxpCache(); + }); + } +} diff --git a/src/Handler/Settings/ThumbnailAdapterCheckHandler.php b/src/Handler/Settings/ThumbnailAdapterCheckHandler.php new file mode 100644 index 00000000..039fa46f --- /dev/null +++ b/src/Handler/Settings/ThumbnailAdapterCheckHandler.php @@ -0,0 +1,42 @@ +' . + $this->translator->trans('important_use_imagick_pecl_extensions_for_best_results_gd_is_just_a_fallback_with_less_quality', [], 'admin') . + ''; + } + + return new ThumbnailAdapterCheckResult(content: $content); + } +} diff --git a/src/Handler/Settings/ThumbnailAdapterCheckResult.php b/src/Handler/Settings/ThumbnailAdapterCheckResult.php new file mode 100644 index 00000000..a8553998 --- /dev/null +++ b/src/Handler/Settings/ThumbnailAdapterCheckResult.php @@ -0,0 +1,23 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + $metadata->setValues($data); + + $existingItem = Metadata\Predefined\Listing::getByKeyAndLanguage( + $metadata->getName(), + $metadata->getLanguage(), + $metadata->getTargetSubtype() + ); + + if ($existingItem && $existingItem->getId() !== $metadata->getId()) { + throw new BadRequestHttpException('predefined_metadata_definitions_error_name_exists_msg'); + } + + $metadata->minimize(); + $metadata->save(); + $metadata->expand(); + + $responseData = $metadata->getObjectVars(); + $responseData['writeable'] = $metadata->isWriteable(); + + return new UpdatePredefinedMetadataResult(data: $responseData); + } +} diff --git a/src/Handler/Settings/UpdatePredefinedMetadataResult.php b/src/Handler/Settings/UpdatePredefinedMetadataResult.php new file mode 100644 index 00000000..2735f8df --- /dev/null +++ b/src/Handler/Settings/UpdatePredefinedMetadataResult.php @@ -0,0 +1,25 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + if (is_array($data['ctype'])) { + $data['ctype'] = implode(',', $data['ctype']); + } + + $property->setValues($data); + $property->save(); + + $responseData = $property->getObjectVars(); + $responseData['writeable'] = $property->isWriteable(); + + return new UpdatePredefinedPropertyResult(data: $responseData); + } +} diff --git a/src/Handler/Settings/UpdatePredefinedPropertyResult.php b/src/Handler/Settings/UpdatePredefinedPropertyResult.php new file mode 100644 index 00000000..30c3f200 --- /dev/null +++ b/src/Handler/Settings/UpdatePredefinedPropertyResult.php @@ -0,0 +1,25 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + foreach ($settingsData as $key => $value) { + $setter = 'set' . ucfirst($key); + if (method_exists($pipe, $setter)) { + $pipe->$setter($value); + } + } + + $pipe->resetItems(); + + uksort($mediaData, static function ($a, $b) use ($mediaOrder) { + if ($a === 'default') { + return -1; + } + + return ($mediaOrder[$a] < $mediaOrder[$b]) ? -1 : 1; + }); + + foreach ($mediaData as $mediaName => $items) { + if (preg_match('/["<>]/', $mediaName)) { + throw new Exception('Invalid media query name'); + } + + foreach ($items as $item) { + $type = $item['type']; + unset($item['type']); + + $pipe->addItem($type, $item, $mediaName); + } + } + + $pipe->save(); + } +} diff --git a/src/Handler/Settings/UpdateVideoThumbnailHandler.php b/src/Handler/Settings/UpdateVideoThumbnailHandler.php new file mode 100644 index 00000000..cdd1892c --- /dev/null +++ b/src/Handler/Settings/UpdateVideoThumbnailHandler.php @@ -0,0 +1,65 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + + foreach ($settingsData as $key => $value) { + $setter = 'set' . ucfirst($key); + if (method_exists($pipe, $setter)) { + $pipe->$setter($value); + } + } + + $pipe->resetItems(); + + uksort($mediaData, static function ($a, $b) use ($mediaOrder) { + if ($a === 'default') { + return -1; + } + + return ($mediaOrder[$a] < $mediaOrder[$b]) ? -1 : 1; + }); + + foreach ($mediaData as $mediaName => $items) { + foreach ($items as $item) { + $type = $item['type']; + unset($item['type']); + + $pipe->addItem($type, $item, htmlspecialchars($mediaName)); + } + } + + $pipe->save(); + } +} diff --git a/src/Handler/Settings/UpdateWebsiteSettingHandler.php b/src/Handler/Settings/UpdateWebsiteSettingHandler.php new file mode 100644 index 00000000..8d3b4db2 --- /dev/null +++ b/src/Handler/Settings/UpdateWebsiteSettingHandler.php @@ -0,0 +1,86 @@ +getType()) { + case 'document': + case 'asset': + case 'object': + if (isset($data['data'])) { + $element = Element\Service::getElementByPath($setting->getType(), $data['data']); + $data['data'] = $element; + } + + break; + } + + $setting->setValues($data); + $setting->save(); + + return new UpdateWebsiteSettingResult(data: $this->buildEditModeData($setting)); + } + + /** + * @return array{id: ?int, name: string, language: string, type: string, data: mixed, siteId: ?int, creationDate: ?int, modificationDate: ?int} + */ + private function buildEditModeData(WebsiteSetting $item): array + { + $resultItem = [ + 'id' => $item->getId(), + 'name' => $item->getName(), + 'language' => $item->getLanguage(), + 'type' => $item->getType(), + 'data' => null, + 'siteId' => $item->getSiteId(), + 'creationDate' => $item->getCreationDate(), + 'modificationDate' => $item->getModificationDate(), + ]; + + switch ($item->getType()) { + case 'document': + case 'asset': + case 'object': + $element = $item->getData(); + if ($element) { + $resultItem['data'] = $element->getRealFullPath(); + } + + break; + default: + $resultItem['data'] = $item->getData(); + + break; + } + + return $resultItem; + } +} diff --git a/src/Handler/Settings/UpdateWebsiteSettingResult.php b/src/Handler/Settings/UpdateWebsiteSettingResult.php new file mode 100644 index 00000000..9a681aca --- /dev/null +++ b/src/Handler/Settings/UpdateWebsiteSettingResult.php @@ -0,0 +1,25 @@ +writeStream(self::LOGO_PATH, fopen($pathname, 'rb')); + } +} diff --git a/src/Handler/Tags/AddTagHandler.php b/src/Handler/Tags/AddTagHandler.php new file mode 100644 index 00000000..37f1322f --- /dev/null +++ b/src/Handler/Tags/AddTagHandler.php @@ -0,0 +1,33 @@ +setName($name); + $tag->setParentId($parentId); + $tag->save(); + + return new AddTagResult(id: $tag->getId()); + } +} diff --git a/src/Handler/Tags/AddTagResult.php b/src/Handler/Tags/AddTagResult.php new file mode 100644 index 00000000..fc89a5bb --- /dev/null +++ b/src/Handler/Tags/AddTagResult.php @@ -0,0 +1,25 @@ +getId()); + } +} diff --git a/src/Handler/Tags/AddTagToElementResult.php b/src/Handler/Tags/AddTagToElementResult.php new file mode 100644 index 00000000..0ea01245 --- /dev/null +++ b/src/Handler/Tags/AddTagToElementResult.php @@ -0,0 +1,25 @@ +delete(); + } +} diff --git a/src/Handler/Tags/DoBatchAssignmentHandler.php b/src/Handler/Tags/DoBatchAssignmentHandler.php new file mode 100644 index 00000000..4a742284 --- /dev/null +++ b/src/Handler/Tags/DoBatchAssignmentHandler.php @@ -0,0 +1,28 @@ +userContext->getAdminUser(); + $userIds = null; + if (!$adminUser?->isAdmin()) { + $userIds = $adminUser?->getRoles() ?? []; + $userIds[] = $adminUser?->getId(); + } + $idList = []; + + switch ($elementType) { + case 'object': + $object = DataObject::getById($elementId); + if ($object) { + $idList = $this->getSubObjectIds($object, $userIds); + } + break; + + case 'asset': + $asset = Asset::getById($elementId); + if ($asset) { + $idList = $this->getSubAssetIds($asset, $userIds); + } + break; + + case 'document': + $document = Document::getById($elementId); + if ($document) { + $idList = $this->getSubDocumentIds($document, $userIds); + } + break; + } + + $size = 2; + $offset = 0; + $idListParts = []; + while ($offset < count($idList)) { + $idListParts[] = array_slice($idList, $offset, $size); + $offset += $size; + } + + return new GetBatchAssignmentJobsResult(idListParts: $idListParts, totalCount: count($idList)); + } + + /** + * @param int[]|null $userIds + * + * @return int[] + */ + private function getSubObjectIds(DataObject\AbstractObject $object, ?array $userIds): array + { + $childrenList = new DataObject\Listing(); + $condition = '`path` LIKE ?'; + if ($userIds !== null) { + $condition .= ' AND ( + (SELECT `view` FROM users_workspaces_object WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`,`key`),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + OR + (SELECT `view` FROM users_workspaces_object WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`,`key`))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + )'; + } + + $childrenList->setCondition($condition, $childrenList->escapeLike($object->getRealFullPath()) . '/%'); + + $beforeListLoadEvent = new GenericEvent(null, [ + 'list' => $childrenList, + 'context' => [], + ]); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::OBJECT_LIST_BEFORE_LIST_LOAD); + /** @var DataObject\Listing $childrenList */ + $childrenList = $beforeListLoadEvent->getArgument('list'); + + return $childrenList->loadIdList(); + } + + /** + * @param int[]|null $userIds + * + * @return int[] + */ + private function getSubAssetIds(Asset $asset, ?array $userIds): array + { + $childrenList = new Asset\Listing(); + $condition = '`path` LIKE ?'; + if ($userIds !== null) { + $condition .= ' AND ( + (SELECT `view` FROM users_workspaces_asset WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`,filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + OR + (SELECT `view` FROM users_workspaces_asset WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`,filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + )'; + } + + $childrenList->setCondition($condition, $childrenList->escapeLike($asset->getRealFullPath()) . '/%'); + + $beforeListLoadEvent = new GenericEvent(null, [ + 'list' => $childrenList, + 'context' => [], + ]); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD); + /** @var Asset\Listing $childrenList */ + $childrenList = $beforeListLoadEvent->getArgument('list'); + + return $childrenList->loadIdList(); + } + + /** + * @param int[]|null $userIds + * + * @return int[] + */ + private function getSubDocumentIds(Document $document, ?array $userIds): array + { + $childrenList = new Document\Listing(); + $condition = '`path` LIKE ?'; + if ($userIds !== null) { + $condition .= ' AND ( + (SELECT `view` FROM users_workspaces_document WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(CONCAT(`path`,`key`),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + OR + (SELECT `view` FROM users_workspaces_document WHERE userId IN (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(`path`,`key`))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1 + )'; + } + + $childrenList->setCondition($condition, $childrenList->escapeLike($document->getRealFullPath()) . '/%'); + + $beforeListLoadEvent = new GenericEvent(null, [ + 'list' => $childrenList, + 'context' => [], + ]); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::DOCUMENT_LIST_BEFORE_LIST_LOAD); + /** @var Document\Listing $childrenList */ + $childrenList = $beforeListLoadEvent->getArgument('list'); + + return $childrenList->loadIdList(); + } +} diff --git a/src/Handler/Tags/GetBatchAssignmentJobsResult.php b/src/Handler/Tags/GetBatchAssignmentJobsResult.php new file mode 100644 index 00000000..435cea78 --- /dev/null +++ b/src/Handler/Tags/GetBatchAssignmentJobsResult.php @@ -0,0 +1,26 @@ +getId()] = $assignedTag; + } + } + + $tagList = new Tag\Listing(); + if ($node) { + $tagList->setCondition('parentId = ?', $node); + } else { + $tagList->setCondition('ISNULL(parentId) OR parentId = 0'); + } + $tagList->setOrderKey('name'); + + $recursiveChildren = false; + if (!empty($filter)) { + $filterIds = [0]; + $filterTagList = new Tag\Listing(); + $filterTagList->setCondition('LOWER(`name`) LIKE ?', ['%' . $filterTagList->escapeLike(mb_strtolower($filter)) . '%']); + foreach ($filterTagList->load() as $filterTag) { + if ($filterTag->getParentId() === 0) { + $filterIds[] = $filterTag->getId(); + } else { + $ids = explode('/', $filterTag->getIdPath()); + if (isset($ids[1])) { + $filterIds[] = (int)$ids[1]; + } + } + } + + $filterIds = array_unique($filterIds); + $tagList->setCondition('id IN(' . implode(',', $filterIds) . ')'); + $recursiveChildren = true; + } + + $tags = []; + foreach ($tagList->load() as $tag) { + $tags[] = $this->convertTagToArray($tag, $showSelection, $assignedTagIds, true, $recursiveChildren); + } + + return new GetTagTreeChildrenResult(tags: $tags); + } + + private function convertTagToArray(Tag $tag, bool $showSelection, array $assignedTagIds, bool $loadChildren = false, bool $recursiveChildren = false): array + { + $hasChildren = $tag->hasChildren(); + + $tagArray = [ + 'id' => $tag->getId(), + 'text' => $tag->getName(), + 'path' => $tag->getNamePath(), + 'expandable' => $hasChildren, + 'leaf' => !$hasChildren, + 'iconCls' => 'opendxp_icon_element_tags', + 'qtipCfg' => [ + 'title' => 'ID: ' . $tag->getId(), + ], + ]; + + if ($showSelection) { + $tagArray['checked'] = isset($assignedTagIds[$tag->getId()]); + } + + if ($hasChildren && $loadChildren) { + $children = $tag->getChildren(); + $loadChildren = $recursiveChildren; + foreach ($children as $child) { + $tagArray['children'][] = $this->convertTagToArray($child, $showSelection, $assignedTagIds, $loadChildren, $recursiveChildren); + } + } + + return $tagArray; + } +} diff --git a/src/Handler/Tags/GetTagTreeChildrenResult.php b/src/Handler/Tags/GetTagTreeChildrenResult.php new file mode 100644 index 00000000..59dfd5f9 --- /dev/null +++ b/src/Handler/Tags/GetTagTreeChildrenResult.php @@ -0,0 +1,25 @@ +convertTagToArray($assignedTag); + } + + return new GetTagsForElementResult(tags: $assignedTagArray); + } + + private function convertTagToArray(Tag $tag): array + { + $hasChildren = $tag->hasChildren(); + + return [ + 'id' => $tag->getId(), + 'text' => $tag->getName(), + 'path' => $tag->getNamePath(), + 'expandable' => $hasChildren, + 'leaf' => !$hasChildren, + 'iconCls' => 'opendxp_icon_element_tags', + 'qtipCfg' => [ + 'title' => 'ID: ' . $tag->getId(), + ], + ]; + } +} diff --git a/src/Handler/Tags/GetTagsForElementResult.php b/src/Handler/Tags/GetTagsForElementResult.php new file mode 100644 index 00000000..9fc75cf1 --- /dev/null +++ b/src/Handler/Tags/GetTagsForElementResult.php @@ -0,0 +1,25 @@ +getId()); + } +} diff --git a/src/Handler/Tags/RemoveTagFromElementResult.php b/src/Handler/Tags/RemoveTagFromElementResult.php new file mode 100644 index 00000000..0f9ee064 --- /dev/null +++ b/src/Handler/Tags/RemoveTagFromElementResult.php @@ -0,0 +1,25 @@ +setParentId($parentId); + } + + if ($name !== null) { + $tag->setName($name); + } + + $tag->save(); + } +} diff --git a/src/Handler/Translation/AddAdminTranslationKeysHandler.php b/src/Handler/Translation/AddAdminTranslationKeysHandler.php new file mode 100644 index 00000000..6a04919b --- /dev/null +++ b/src/Handler/Translation/AddAdminTranslationKeysHandler.php @@ -0,0 +1,59 @@ +setDomain(Translation::DOMAIN_ADMIN); + $t->setKey($translationData); + $t->setCreationDate(time()); + $t->setModificationDate(time()); + + foreach ($availableLanguages as $lang) { + $t->addTranslation($lang, ''); + } + + try { + $t->save(); + } catch (Exception $e) { + Logger::log((string) $e); + } + } + } + } +} diff --git a/src/Handler/Translation/BuildContentExportJobsHandler.php b/src/Handler/Translation/BuildContentExportJobsHandler.php new file mode 100644 index 00000000..8459e627 --- /dev/null +++ b/src/Handler/Translation/BuildContentExportJobsHandler.php @@ -0,0 +1,116 @@ + $element['id'], + 'type' => $element['type'], + ]; + + $el = null; + + if ($element['children']) { + $el = Element\Service::getElementById($element['type'], (int) $element['id']); + $baseClass = Element\Service::getBaseClassNameForElement($element['type']); + $listClass = '\\OpenDxp\\Model\\' . $baseClass . '\\Listing'; + $list = new $listClass(); + $list->setUnpublished(true); + if ($el instanceof AbstractObject) { + $list->setObjectTypes( + [DataObject::OBJECT_TYPE_VARIANT, + DataObject::OBJECT_TYPE_OBJECT, + DataObject::OBJECT_TYPE_FOLDER, ] + ); + } + $list->setCondition( + 'path LIKE ?', + [$list->escapeLike($el->getRealFullPath() . ($el->getRealFullPath() !== '/' ? '/' : '')) . '%'] + ); + $children = $list->load(); + + foreach ($children as $child) { + $childId = $child->getId(); + $elements[$element['type'] . '_' . $childId] = [ + 'id' => $childId, + 'type' => $element['type'], + ]; + + if (isset($element['relations']) && $element['relations']) { + $childDependencies = $child->getDependencies()->getRequires(); + foreach ($childDependencies as $cd) { + if ($cd['type'] === 'object' || $cd['type'] === 'document') { + $elements[$cd['type'] . '_' . $cd['id']] = $cd; + } + } + } + } + } + + if (isset($element['relations']) && $element['relations']) { + if (!$el instanceof Element\ElementInterface) { + $el = Element\Service::getElementById($element['type'], (int) $element['id']); + } + + $dependencies = $el->getDependencies()->getRequires(); + foreach ($dependencies as $dependency) { + if ($dependency['type'] === 'object' || $dependency['type'] === 'document') { + $elements[$dependency['type'] . '_' . $dependency['id']] = $dependency; + } + } + } + } + } + + $elements = array_values($elements); + + $elements = array_chunk($elements, $elementsPerJob); + foreach ($elements as $chunk) { + $jobs[] = [[ + 'url' => $jobUrl, + 'method' => 'POST', + 'params' => [ + 'id' => $exportId, + 'source' => $source, + 'target' => $target, + 'data' => json_encode($chunk, JSON_THROW_ON_ERROR), + ], + ]]; + } + + return new BuildContentExportJobsResult(jobs: $jobs, exportId: $exportId); + } +} diff --git a/src/Handler/Translation/BuildContentExportJobsResult.php b/src/Handler/Translation/BuildContentExportJobsResult.php new file mode 100644 index 00000000..f5d125a8 --- /dev/null +++ b/src/Handler/Translation/BuildContentExportJobsResult.php @@ -0,0 +1,26 @@ +setDomain($domain); + $list->cleanup(); + + Cache::clearTags(['translator', 'translate']); + } +} diff --git a/src/Handler/Translation/CreateTranslationHandler.php b/src/Handler/Translation/CreateTranslationHandler.php new file mode 100644 index 00000000..1d6aff2c --- /dev/null +++ b/src/Handler/Translation/CreateTranslationHandler.php @@ -0,0 +1,60 @@ +userContext->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); + if (Translation::getByKey($data['key'], $domain)) { + throw new BadRequestHttpException('identifier_already_exists'); + } + + $t = new Translation(); + $t->setDomain($domain); + $t->setKey($data['key']); + $t->setCreationDate(time()); + $t->setModificationDate(time()); + $t->setType($data['type'] ?? null); + + foreach ($validLanguages as $lang) { + $t->addTranslation($lang, ''); + } + + $t->save(); + + return new CreateTranslationResult( + key: $t->getKey(), + creationDate: $t->getCreationDate(), + modificationDate: $t->getModificationDate(), + type: $t->getType(), + translations: $t->getTranslations(), + ); + } +} diff --git a/src/Handler/Translation/CreateTranslationResult.php b/src/Handler/Translation/CreateTranslationResult.php new file mode 100644 index 00000000..c6e93fd3 --- /dev/null +++ b/src/Handler/Translation/CreateTranslationResult.php @@ -0,0 +1,29 @@ +delete(); + } + } +} diff --git a/src/Handler/Translation/ExportTranslationsHandler.php b/src/Handler/Translation/ExportTranslationsHandler.php new file mode 100644 index 00000000..cd6218e8 --- /dev/null +++ b/src/Handler/Translation/ExportTranslationsHandler.php @@ -0,0 +1,149 @@ +userContext->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); + $translation = new Translation(); + $translation->setDomain($domain); + $tableName = $translation->getDao()->getDatabaseTableName(); + + $list = new Translation\Listing(); + $list->setDomain($domain); + + $joins = []; + + $list->setOrder('asc'); + $list->setOrderKey($tableName . '.key', false); + + $filterParameters = [ + 'filter' => $filter, + 'searchString' => $searchString, + ]; + + $conditions = $this->getGridFilterCondition($filterParameters, $tableName, false, $allowedLanguages); + if ($conditions !== []) { + $list->setCondition($conditions['condition'], $conditions['params']); + } + + $filters = $this->getGridFilterCondition($filterParameters, $tableName, true, $allowedLanguages); + + if ($filters) { + $joins = [...$joins, ...$filters['joins']]; + } + + $this->extendTranslationQuery($joins, $list, $tableName, $filters); + + try { + $list->load(); + } catch (SyntaxErrorException) { + throw new InvalidArgumentException('Check your arguments.'); + } + + $translations = []; + $translationObjects = $list->getTranslations(); + + if ($translationObjects === []) { + if ($admin) { + $t = new Translation(); + $t->setDomain(Translation::DOMAIN_ADMIN); + $languages = Tool\Admin::getLanguages(); + } else { + $t = new Translation(); + $languages = $allowedLanguages; + } + + foreach ($languages as $language) { + $t->addTranslation($language, ''); + } + + $translationObjects[] = $t; + } + + foreach ($translationObjects as $t) { + $row = $t->getTranslations(); + $row = Element\Service::escapeCsvRecord($row); + $translations[] = ['key' => $t->getKey(), 'creationDate' => $t->getCreationDate(), 'modificationDate' => $t->getModificationDate(), ...$row]; + } + + $columns = array_keys($translations[0]); + + if ($admin) { + $languages = Tool\Admin::getLanguages(); + } else { + $languages = $allowedLanguages; + } + + foreach ($languages as $l) { + if (!in_array($l, $columns)) { + $columns[] = $l; + } + } + + foreach ($columns as $key => $column) { + if (strtolower(trim($column)) !== 'key' && !in_array($column, $languages)) { + unset($columns[$key]); + } + } + $columns = array_values($columns); + + $headerRow = []; + foreach ($columns as $value) { + $headerRow[] = '"' . $value . '"'; + } + $csv = implode(';', $headerRow) . "\r\n"; + + foreach ($translations as $t) { + $tempRow = []; + foreach ($columns as $key) { + $value = $t[$key] ?? null; + if (is_string($value)) { + $value = Text::removeLineBreaks($value); + $value = str_replace('"', '"', $value); + $tempRow[$key] = '"' . $value . '"'; + } else { + $tempRow[$key] = $value; + } + } + $csv .= implode(';', $tempRow) . "\r\n"; + } + + return new ExportTranslationsResult(csv: $csv, domain: $domain ?? ''); + } +} diff --git a/src/Handler/Translation/ExportTranslationsResult.php b/src/Handler/Translation/ExportTranslationsResult.php new file mode 100644 index 00000000..0a2a0a16 --- /dev/null +++ b/src/Handler/Translation/ExportTranslationsResult.php @@ -0,0 +1,26 @@ + ['name' => $domain], + $translation->getDao()->getAvailableDomains(), + ); + + return new GetTranslationDomainsResult(domains: $domains); + } +} diff --git a/src/Handler/Translation/GetTranslationDomainsResult.php b/src/Handler/Translation/GetTranslationDomainsResult.php new file mode 100644 index 00000000..3786ab94 --- /dev/null +++ b/src/Handler/Translation/GetTranslationDomainsResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); + $translation = new Translation(); + $translation->setDomain($domain); + $tableName = $translation->getDao()->getDatabaseTableName(); + + $list = new Translation\Listing(); + $list->setDomain($domain); + $list->setOrder('asc'); + $list->setOrderKey($tableName . '.key', false); + $list->setLanguages($validLanguages); + + $sortingSettings = QueryParams::extractSortingSettings($requestParams); + + $joins = []; + + if ($orderKey = $sortingSettings['orderKey']) { + if (in_array(trim($orderKey, '_'), $validLanguages)) { + $orderKey = trim($orderKey, '_'); + $joins[] = [ + 'language' => $orderKey, + ]; + $list->setOrderKey($orderKey); + } elseif ($list->isValidOrderKey($sortingSettings['orderKey'])) { + $list->setOrderKey($tableName . '.' . $sortingSettings['orderKey'], false); + } + } + if ($sortingSettings['order']) { + $list->setOrder($sortingSettings['order']); + } + + $list->setLimit($limit); + $list->setOffset($offset); + + $filterParameters = [ + 'filter' => $filter, + 'searchString' => $searchString, + ]; + + $conditions = $this->getGridFilterCondition($filterParameters, $tableName, false, $validLanguages); + $filters = $this->getGridFilterCondition($filterParameters, $tableName, true, $validLanguages); + + if ($filters) { + $joins = [...$joins, ...$filters['joins']]; + } + + if ($conditions !== []) { + $list->setCondition($conditions['condition'], $conditions['params']); + } + + $this->extendTranslationQuery($joins, $list, $tableName, $filters); + + $translations = []; + foreach ($list->getTranslations() as $t) { + if ($searchString && !strpos($searchString, (string) $t->getKey()) && !$t = Translation::getByKey($t->getKey(), $domain)) { + continue; + } + + $prefixed = []; + foreach ($t->getTranslations() as $lang => $trans) { + $prefixed['_' . $lang] = $trans; + } + + $translations[] = [ + ...$prefixed, + 'key' => $t->getKey(), + 'creationDate' => $t->getCreationDate(), + 'modificationDate' => $t->getModificationDate(), + 'type' => $t->getType(), + ]; + } + + return new GetTranslationsResult( + translations: $translations, + total: $list->getTotalCount(), + ); + } +} diff --git a/src/Handler/Translation/GetTranslationsResult.php b/src/Handler/Translation/GetTranslationsResult.php new file mode 100644 index 00000000..0527943c --- /dev/null +++ b/src/Handler/Translation/GetTranslationsResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + + return new GetWebsiteTranslationLanguagesResult( + view: $user->getAllowedLanguagesForViewingWebsiteTranslations(), + edit: $user->getAllowedLanguagesForEditingWebsiteTranslations(), + ); + } +} diff --git a/src/Handler/Translation/GetWebsiteTranslationLanguagesResult.php b/src/Handler/Translation/GetWebsiteTranslationLanguagesResult.php new file mode 100644 index 00000000..4c6ef3dc --- /dev/null +++ b/src/Handler/Translation/GetWebsiteTranslationLanguagesResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser()->getAllowedLanguagesForEditingWebsiteTranslations(); + $delta = Translation::importTranslationsFromFile( + $tmpFile, + $domain, + $overwrite, + $allowedLanguages, + $dialect + ); + + if (is_file($tmpFile)) { + @unlink($tmpFile); + } + + if ($enrichDelta) { + $enrichedDelta = []; + foreach ($delta as $item) { + $lg = $item['lg']; + $currentLocale = $this->localeService->findLocale(); + $item['lgname'] = Locale::getDisplayLanguage($lg, $currentLocale); + $item['icon'] = str_replace('{language}', $lg, $flagUrlTemplate); + $item['current'] = $item['text']; + $enrichedDelta[] = $item; + } + + return new ImportTranslationsResult(delta: $enrichedDelta); + } + + return new ImportTranslationsResult(delta: []); + } +} diff --git a/src/Handler/Translation/ImportTranslationsResult.php b/src/Handler/Translation/ImportTranslationsResult.php new file mode 100644 index 00000000..1a363d60 --- /dev/null +++ b/src/Handler/Translation/ImportTranslationsResult.php @@ -0,0 +1,25 @@ +addTranslation($data['lg'], $newValue); + $t->setModificationDate(time()); + $t->save(); + } + } +} diff --git a/src/Handler/Translation/TranslationQueryTrait.php b/src/Handler/Translation/TranslationQueryTrait.php new file mode 100644 index 00000000..3d54b2f4 --- /dev/null +++ b/src/Handler/Translation/TranslationQueryTrait.php @@ -0,0 +1,175 @@ +onCreateQueryBuilder( + function (DoctrineQueryBuilder $select) use ($joins, $tableName, $filters): void { + $db = \OpenDxp\Db::get(); + + $alreadyJoined = []; + + foreach ($joins as $join) { + $fieldname = $join['language']; + + if (isset($alreadyJoined[$fieldname])) { + continue; + } + $alreadyJoined[$fieldname] = 1; + + $select->addSelect($fieldname . '.text AS ' . $fieldname); + $select->leftJoin( + $tableName, + $tableName, + $fieldname, + '(' + . $fieldname . '.key = ' . $tableName . '.key' + . ' and ' . $fieldname . '.language = ' . $db->quote($fieldname) + . ')' + ); + } + + $havings = $filters['conditions']; + if ($havings) { + $havings = implode(' AND ', $havings); + $select->having($havings); + } + } + ); + } + } + + protected function getGridFilterCondition(array $filterParameters, string $tableName, bool $languageMode, array $validLanguages): array + { + $placeHolderCount = 0; + $joins = []; + $conditions = []; + + $db = \OpenDxp\Db::get(); + $conditionFilters = []; + + $filterJson = $filterParameters['filter']; + if ($filterJson) { + $propertyField = 'property'; + $operatorField = 'operator'; + + $filters = json_decode($filterJson, true); + foreach ($filters as $filter) { + $operator = '='; + $field = null; + $value = null; + + $fieldname = $filter[$propertyField]; + if (in_array(ltrim($fieldname, '_'), $validLanguages)) { + $fieldname = ltrim($fieldname, '_'); + } + $fieldname = str_replace('--', '', $fieldname); + if (!$languageMode && in_array($fieldname, $validLanguages)) { + continue; + } + if ($languageMode && !in_array($fieldname, $validLanguages)) { + continue; + } + + if (!$languageMode) { + $fieldname = $tableName . '.' . $fieldname; + } + + if (!empty($filter['value'])) { + if ($filter['type'] === 'string') { + $operator = 'LIKE'; + $field = $fieldname; + $value = '%' . $filter['value'] . '%'; + } elseif ($filter['type'] === 'date' || + (in_array($fieldname, ['modificationDate', 'creationDate']))) { + if ($filter[$operatorField] === 'lt') { + $operator = '<'; + } elseif ($filter[$operatorField] === 'gt') { + $operator = '>'; + } elseif ($filter[$operatorField] === 'eq') { + $operator = '='; + $fieldname = "UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))"; + } + $filter['value'] = strtotime($filter['value']); + $field = $fieldname; + $value = $filter['value']; + } + } + + if ($field && $value) { + $condition = $db->quoteIdentifier($field) . ' ' . $operator . ' ' . $db->quote($value); + + if ($languageMode) { + $conditions[$fieldname] = $condition; + $joins[] = [ + 'language' => $fieldname, + ]; + } else { + $placeHolderName = self::FILTER_PLACEHOLDER_NAME . $placeHolderCount; + $placeHolderCount++; + $conditionFilters[] = [ + 'condition' => $field . ' ' . $operator . ' :' . $placeHolderName, + 'field' => $placeHolderName, + 'value' => $value, + ]; + } + } + } + } + + if (!empty($filterParameters['searchString'])) { + $conditionFilters[] = [ + 'condition' => '(lower(' . $tableName . '.key) LIKE :filterTerm OR lower(' . $tableName . '.text) LIKE :filterTerm)', + 'field' => 'filterTerm', + 'value' => '%' . mb_strtolower($filterParameters['searchString']) . '%', + ]; + } + + if ($languageMode) { + return [ + 'joins' => $joins, + 'conditions' => $conditions, + ]; + } + + if ($conditionFilters !== []) { + $conditions = []; + $params = []; + foreach ($conditionFilters as $conditionFilter) { + $conditions[] = $conditionFilter['condition']; + $params[$conditionFilter['field']] = $conditionFilter['value']; + } + + $conditionFilters = [ + 'condition' => implode(' AND ', $conditions), + 'params' => $params, + ]; + } + + return $conditionFilters; + } +} diff --git a/src/Handler/Translation/UpdateTranslationHandler.php b/src/Handler/Translation/UpdateTranslationHandler.php new file mode 100644 index 00000000..a4266078 --- /dev/null +++ b/src/Handler/Translation/UpdateTranslationHandler.php @@ -0,0 +1,58 @@ + $value) { + $key = preg_replace('/^_/', '', $key, 1); + if (!in_array($key, ['key', 'type'])) { + $t->addTranslation($key, $value); + } + } + + if ($data['key']) { + $t->setKey($data['key']); + } + + if ($data['type']) { + $t->setType($data['type']); + } + + $t->setModificationDate(time()); + $t->save(); + + return new UpdateTranslationResult( + key: $t->getKey(), + creationDate: $t->getCreationDate(), + modificationDate: $t->getModificationDate(), + type: $t->getType(), + translations: $t->getTranslations(), + ); + } +} diff --git a/src/Handler/Translation/UpdateTranslationResult.php b/src/Handler/Translation/UpdateTranslationResult.php new file mode 100644 index 00000000..83deac47 --- /dev/null +++ b/src/Handler/Translation/UpdateTranslationResult.php @@ -0,0 +1,29 @@ +getPathname()); + + $filename = uniqid('import_translations-', false); + $importFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . $filename; + $this->filesystem->dumpFile($importFile, $tmpData); + + $dialect = AdminTool::determineCsvDialect($importFile); + + if (!empty($dialect->lineterminator) && empty(preg_match('/[a-f0-9]{2}/i', $dialect->lineterminator))) { + $dialect->lineterminator = bin2hex($dialect->lineterminator); + } + + return new UploadTranslationImportFileResult( + importFile: $importFile, + dialect: $dialect, + ); + } +} diff --git a/src/Handler/Translation/UploadTranslationImportFileResult.php b/src/Handler/Translation/UploadTranslationImportFileResult.php new file mode 100644 index 00000000..7fa3b4ed --- /dev/null +++ b/src/Handler/Translation/UploadTranslationImportFileResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser()?->isAdmin() ?? false; + $className = User\Service::getClassNameForType($type); + $user = $className::create([ + 'parentId' => $parentId, + 'name' => $name, + 'password' => '', + 'active' => $active, + ]); + + if ($referenceId !== null) { + $rObject = $className::getById($referenceId); + if ($rObject && ($type === 'user' || $type === 'role')) { + $user->setParentId($rObject->getParentId()); + if ($rObject->getClasses()) { + $user->setClasses(implode(',', $rObject->getClasses())); + } + if ($rObject->getDocTypes()) { + $user->setDocTypes(implode(',', $rObject->getDocTypes())); + } + $keys = ['asset', 'document', 'object']; + foreach ($keys as $key) { + $getter = 'getWorkspaces' . ucfirst($key); + $setter = 'setWorkspaces' . ucfirst($key); + $workspaces = $rObject->$getter(); + $clonedWorkspaces = []; + if (is_array($workspaces)) { + /** @var User\Workspace\AbstractWorkspace $workspace */ + foreach ($workspaces as $workspace) { + $vars = $workspace->getObjectVars(); + if ($key === 'object') { + $workspaceClass = \OpenDxp\Model\User\Workspace\DataObject::class; + } else { + $workspaceClass = '\\OpenDxp\\Model\\User\\Workspace\\' . ucfirst($key); + } + $newWorkspace = new $workspaceClass(); + foreach ($vars as $varKey => $varValue) { + $newWorkspace->setObjectVar($varKey, $varValue); + } + $newWorkspace->setUserId($user->getId()); + $clonedWorkspaces[] = $newWorkspace; + } + } + + $user->$setter($clonedWorkspaces); + } + $user->setPerspectives($rObject->getPerspectives()); + $user->setPermissions($rObject->getPermissions()); + if ($type === 'user') { + $user->setAdmin(false); + if ($currentUserIsAdmin) { + $user->setAdmin($rObject->getAdmin()); + } + $user->setActive($rObject->getActive()); + $user->setRoles($rObject->getRoles()); + $user->setWelcomeScreen($rObject->getWelcomescreen()); + $user->setMemorizeTabs($rObject->getMemorizeTabs()); + $user->setCloseWarning($rObject->getCloseWarning()); + } + $user->setWebsiteTranslationLanguagesView($rObject->getWebsiteTranslationLanguagesView()); + $user->setWebsiteTranslationLanguagesEdit($rObject->getWebsiteTranslationLanguagesEdit()); + $user->save(); + } + } + + return new AddUserResult(id: $user->getId()); + } +} diff --git a/src/Handler/User/AddUserResult.php b/src/Handler/User/AddUserResult.php new file mode 100644 index 00000000..c677329d --- /dev/null +++ b/src/Handler/User/AddUserResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + $currentUserId = (int) $adminUser?->getId(); + + $user = User\AbstractUser::getById($id); + if (!$user) { + throw new NotFoundHttpException('User not found'); + } + + if (($user instanceof User\Folder && !$adminUser?->isAdmin()) + || ($user instanceof User && $user->isAdmin() && !$adminUser?->isAdmin()) + ) { + throw new AccessDeniedHttpException('You are not allowed to delete this user'); + } + + if ($user instanceof User\Role\Folder) { + $list = [$user]; + $this->populateChildNodes($user, $list, true, $currentUserId); + $listCount = count($list); + for ($i = $listCount - 1; $i >= 0; $i--) { + $list[$i]->delete(); + } + } elseif ($user->getId()) { + $user->delete(); + } + } + + /** + * @throws Exception + */ + private function populateChildNodes(User\AbstractUser $node, array &$currentList, bool $roleMode, int $currentUserId): void + { + $list = $roleMode ? new User\Role\Listing() : new User\Listing(); + $list->setCondition('parentId = ?', $node->getId()); + $list->setOrder('ASC'); + $list->setOrderKey('name'); + $list->load(); + + $childList = $roleMode ? $list->getRoles() : $list->getUsers(); + + foreach ($childList as $child) { + if ($child->getId() === $currentUserId) { + throw new Exception('Cannot delete current user'); + } + if ($child->getId() && $currentUserId && $child->getName() !== 'system') { + $currentList[] = $child; + $this->populateChildNodes($child, $currentList, $roleMode, $currentUserId); + } + } + } +} diff --git a/src/Handler/User/DeleteUserImageHandler.php b/src/Handler/User/DeleteUserImageHandler.php new file mode 100644 index 00000000..55fa1f94 --- /dev/null +++ b/src/Handler/User/DeleteUserImageHandler.php @@ -0,0 +1,50 @@ +userContext->getAdminUser(); + $targetUserId ??= (int) $adminUser?->getId(); + + $userObj = User::getById($targetUserId); + if (!$userObj) { + throw new NotFoundHttpException('User not found'); + } + + if (!$adminUser?->isAdmin()) { + if ($userObj->isAdmin()) { + throw new AccessDeniedHttpException('Only admin users are allowed to modify admin users'); + } + if ($adminUser?->getId() !== $userObj->getId()) { + throw new AccessDeniedHttpException('Only admin users are allowed to modify users other than themselves'); + } + } + + $userObj->setImage(null); + } +} diff --git a/src/Handler/User/Disable2FaHandler.php b/src/Handler/User/Disable2FaHandler.php new file mode 100644 index 00000000..a4507533 --- /dev/null +++ b/src/Handler/User/Disable2FaHandler.php @@ -0,0 +1,42 @@ +userContext->getAdminUser(); + if (!$user instanceof User) { + throw new BadRequestHttpException('No user found'); + } + + if ($user->getTwoFactorAuthentication('required')) { + throw new BadRequestHttpException('Two-factor authentication is required and cannot be disabled.'); + } + + $user->setTwoFactorAuthentication([]); + $user->save(); + } +} diff --git a/src/Handler/User/GetCurrentUserHandler.php b/src/Handler/User/GetCurrentUserHandler.php new file mode 100644 index 00000000..c48c121f --- /dev/null +++ b/src/Handler/User/GetCurrentUserHandler.php @@ -0,0 +1,54 @@ +userContext->getAdminUser(); + $list = new User\Permission\Definition\Listing(); + $definitions = $list->load(); + + foreach ($definitions as $definition) { + $user->setPermission($definition->getKey(), $user->isAllowed($definition->getKey())); + } + + $userData = $user->getObjectVars(); + $contentLanguages = Tool\Admin::reorderWebsiteLanguages($user, Tool::getValidLanguages()); + $userData['contentLanguages'] = $contentLanguages; + $userData['keyBindings'] = UserHelper::getDefaultKeyBindings($user); + + unset($userData['password'], $userData['passwordRecoveryToken']); + $userData['twoFactorAuthentication'] = $user->getTwoFactorAuthentication(); + unset($userData['twoFactorAuthentication']['secret']); + $userData['twoFactorAuthentication']['isActive'] = $user->getTwoFactorAuthentication('enabled') && $user->getTwoFactorAuthentication('secret'); + $userData['hasImage'] = $user->hasImage(); + $userData['isPasswordReset'] = $isPasswordReset; + $userData['validLocales'] = Tool::getSupportedJSLocales(); + + return new GetCurrentUserResult(userData: $userData); + } +} diff --git a/src/Handler/User/GetCurrentUserResult.php b/src/Handler/User/GetCurrentUserResult.php new file mode 100644 index 00000000..b8350e1c --- /dev/null +++ b/src/Handler/User/GetCurrentUserResult.php @@ -0,0 +1,25 @@ +getId(), + admin: $user->isAdmin(), + active: $user->isActive(), + permissionInfo: [ + 'assets' => $user->isAllowed('assets'), + 'documents' => $user->isAllowed('documents'), + 'objects' => $user->isAllowed('objects'), + ], + ); + } +} diff --git a/src/Handler/User/GetMinimalUserResult.php b/src/Handler/User/GetMinimalUserResult.php new file mode 100644 index 00000000..503795c9 --- /dev/null +++ b/src/Handler/User/GetMinimalUserResult.php @@ -0,0 +1,28 @@ +{'getWorkspaces' . ucfirst($type)}(); + foreach ($workspaces as $wKey => $workspace) { + $el = Element\Service::getElementById($type, $workspace->getCid()); + if ($el) { + $workspaceVars = $workspace->getObjectVars(); + $workspaceVars['path'] = $el->getRealFullPath(); + $workspaces[$wKey] = $workspaceVars; + } + } + $role->{'setWorkspaces' . ucfirst($type)}($workspaces); + } + + $replaceFn = static fn ($value) => $value->getObjectVars(); + + // get available permissions + $availableUserPermissionsList = new User\Permission\Definition\Listing(); + $availableUserPermissionsList->setOrderKey('category'); + $availableUserPermissions = $availableUserPermissionsList->load(); + $availableUserPermissions = array_map($replaceFn, $availableUserPermissions); + + $availablePerspectives = Config::getAvailablePerspectives(null); + + return new GetRoleResult( + role: $role->getObjectVars(), + permissions: $role->generatePermissionList(), + classes: $role->getClasses(), + docTypes: $role->getDocTypes(), + availablePermissions: $availableUserPermissions, + availablePerspectives: $availablePerspectives, + validLanguages: Tool::getValidLanguages(), + ); + } +} diff --git a/src/Handler/User/GetRoleResult.php b/src/Handler/User/GetRoleResult.php new file mode 100644 index 00000000..2659143b --- /dev/null +++ b/src/Handler/User/GetRoleResult.php @@ -0,0 +1,31 @@ +setCondition('parentId = ?', $parentId); + $list->load(); + + $roles = []; + foreach ($list->getItems() as $role) { + $roles[] = $this->buildRoleTreeNodeConfig($role); + } + + return $roles; + } + + private function buildRoleTreeNodeConfig(User\Role|User\Role\Folder $role): array + { + $tmpUser = [ + 'id' => $role->getId(), + 'text' => $role->getName(), + 'elementType' => 'role', + 'qtipCfg' => [ + 'title' => 'ID: ' . $role->getId(), + ], + ]; + + if ($role instanceof User\Role\Folder) { + $tmpUser['leaf'] = false; + $tmpUser['iconCls'] = 'opendxp_icon_folder'; + $tmpUser['expanded'] = true; + $tmpUser['allowChildren'] = true; + + if ($role->hasChildren()) { + $tmpUser['expanded'] = false; + } else { + $tmpUser['loaded'] = true; + } + } else { + $tmpUser['leaf'] = true; + $tmpUser['iconCls'] = 'opendxp_icon_roles'; + $tmpUser['allowChildren'] = false; + } + + return $tmpUser; + } +} diff --git a/src/Handler/User/GetRolesHandler.php b/src/Handler/User/GetRolesHandler.php new file mode 100644 index 00000000..b4aa87a9 --- /dev/null +++ b/src/Handler/User/GetRolesHandler.php @@ -0,0 +1,42 @@ +setCondition('`type` = "role"'); + $list->load(); + + $roles = []; + foreach ($list->getRoles() as $role) { + if ($permission === null || in_array($permission, $role->getPermissions())) { + $roles[] = [ + 'id' => $role->getId(), + 'label' => $role->getName(), + ]; + } + } + + return $roles; + } +} diff --git a/src/Handler/User/GetTokenLoginLinkHandler.php b/src/Handler/User/GetTokenLoginLinkHandler.php new file mode 100644 index 00000000..836931c9 --- /dev/null +++ b/src/Handler/User/GetTokenLoginLinkHandler.php @@ -0,0 +1,57 @@ +translator->trans('login_token_invalid_user_error', [], 'admin')); + } + + $adminUser = $this->userContext->getAdminUser(); + if ($user->isAdmin() && !$adminUser?->isAdmin()) { + throw new AccessDeniedHttpException($this->translator->trans('login_token_as_admin_non_admin_user_error', [], 'admin')); + } + + if (empty($user->getPassword())) { + throw new AccessDeniedHttpException($this->translator->trans('login_token_no_password_error', [], 'admin')); + } + + $token = Tool\Authentication::generateTokenByUser($user); + $link = $this->loginUrlGenerator->generate(['token' => $token]); + + return new GetTokenLoginLinkResult(link: $link); + } +} diff --git a/src/Handler/User/GetTokenLoginLinkResult.php b/src/Handler/User/GetTokenLoginLinkResult.php new file mode 100644 index 00000000..7b2cfdf7 --- /dev/null +++ b/src/Handler/User/GetTokenLoginLinkResult.php @@ -0,0 +1,25 @@ +userContext->getAdminUser(); + if ($user->isAdmin() && !$adminUser?->isAdmin()) { + throw new AccessDeniedHttpException('Only admin users are allowed to modify admin users'); + } + // workspaces + $types = ['asset', 'document', 'object']; + foreach ($types as $type) { + /** @var Workspace\Document[]|Workspace\Asset[]|Workspace\DataObject[] $workspaces */ + $workspaces = $user->{'getWorkspaces' . ucfirst($type)}(); + foreach ($workspaces as $wKey => $workspace) { + $el = Element\Service::getElementById($type, $workspace->getCid()); + if ($el) { + $workspaceVars = $workspace->getObjectVars(); + $workspaceVars['path'] = $el->getRealFullPath(); + $workspaces[$wKey] = $workspaceVars; + } + } + $user->{'setWorkspaces' . ucfirst($type)}($workspaces); + } + + // object <=> user dependencies + $userObjects = DataObject\Service::getObjectsReferencingUser($user->getId()); + $userObjectData = []; + $hasHidden = false; + + foreach ($userObjects as $o) { + if ($o->isAllowed('list')) { + $userObjectData[] = [ + 'path' => $o->getRealFullPath(), + 'id' => $o->getId(), + 'subtype' => $o->getClass()->getName(), + ]; + } else { + $hasHidden = true; + } + } + + // get available permissions + $availableUserPermissionsList = new User\Permission\Definition\Listing(); + $availableUserPermissionsList->setOrderKey('category'); + $availableUserPermissions = $availableUserPermissionsList->load(); + + $availableUserPermissionsData = []; + foreach ($availableUserPermissions as $availableUserPermission) { + $availableUserPermissionsData[] = $availableUserPermission->getObjectVars(); + } + + // get available roles + $list = new User\Role\Listing(); + $list->setCondition('`type` = ?', ['role']); + $list->load(); + + $roles = []; + foreach ($list->getItems() as $role) { + $roles[] = [$role->getId(), $role->getName()]; + } + + // unset confidential information + $userData = $user->getObjectVars(); + $userData['roles'] = $user->getRoles(); + $userData['docTypes'] = $user->getDocTypes(); + $contentLanguages = Tool\Admin::reorderWebsiteLanguages($user, Tool::getValidLanguages()); + $userData['contentLanguages'] = $contentLanguages; + $userData['twoFactorAuthentication']['isActive'] = ($user->getTwoFactorAuthentication('enabled') || $user->getTwoFactorAuthentication('secret')); + unset($userData['password'], $userData['passwordRecoveryToken'], $userData['twoFactorAuthentication']['secret']); + $userData['hasImage'] = $user->hasImage(); + + $availablePerspectives = Config::getAvailablePerspectives(null); + + return new GetUserResult( + userData: $userData, + roles: $roles, + permissions: $user->generatePermissionList(), + availablePermissions: $availableUserPermissionsData, + availablePerspectives: $availablePerspectives, + validLanguages: Tool::getValidLanguages(), + validLocales: Tool::getSupportedJSLocales(), + objectDependencies: [ + 'hasHidden' => $hasHidden, + 'dependencies' => $userObjectData, + ], + ); + } +} diff --git a/src/Handler/User/GetUserImageHandler.php b/src/Handler/User/GetUserImageHandler.php new file mode 100644 index 00000000..a9e89d54 --- /dev/null +++ b/src/Handler/User/GetUserImageHandler.php @@ -0,0 +1,39 @@ +userContext->getAdminUser()?->getId(); + + $user = User::getById($targetUserId); + if (!$user) { + throw new NotFoundHttpException('User not found'); + } + + return $user->getImage(); + } +} diff --git a/src/Handler/User/GetUserResult.php b/src/Handler/User/GetUserResult.php new file mode 100644 index 00000000..099db2dd --- /dev/null +++ b/src/Handler/User/GetUserResult.php @@ -0,0 +1,32 @@ +setCondition('parentId = ?', $parentId); + $list->setOrder('ASC'); + $list->setOrderKey('name'); + $list->load(); + + $users = []; + foreach ($list->getUsers() as $user) { + if ($user->getId() && $user->getName() !== 'system') { + $users[] = $this->buildTreeNodeConfig($user); + } + } + + return $users; + } + + private function buildTreeNodeConfig(User|User\Folder $user): array + { + $tmpUser = [ + 'id' => $user->getId(), + 'text' => $user->getName(), + 'elementType' => 'user', + 'type' => $user->getType(), + 'qtipCfg' => [ + 'title' => 'ID: ' . $user->getId(), + ], + ]; + + if ($user instanceof User\Folder) { + $tmpUser['leaf'] = false; + $tmpUser['iconCls'] = 'opendxp_icon_folder'; + $tmpUser['expanded'] = true; + $tmpUser['allowChildren'] = true; + + if ($user->hasChildren()) { + $tmpUser['expanded'] = false; + } else { + $tmpUser['loaded'] = true; + } + } else { + $tmpUser['leaf'] = true; + $tmpUser['iconCls'] = 'opendxp_icon_user'; + if (!$user->getActive()) { + $tmpUser['cls'] = ' opendxp_unpublished'; + } + $tmpUser['allowChildren'] = false; + $tmpUser['admin'] = $user->isAdmin(); + } + + return $tmpUser; + } +} diff --git a/src/Handler/User/GetUsersHandler.php b/src/Handler/User/GetUsersHandler.php new file mode 100644 index 00000000..d60d304a --- /dev/null +++ b/src/Handler/User/GetUsersHandler.php @@ -0,0 +1,53 @@ +userContext->getAdminUser()?->getId(); + $list = new User\Listing(); + + $conditions = ['type = "user"']; + + if (!$includeCurrentUser) { + $conditions[] = 'id != ' . $currentUserId; + } + + $list->setCondition(implode(' AND ', $conditions)); + $list->load(); + + $users = []; + foreach ($list->getUsers() as $user) { + if (!$permission || $user->isAllowed($permission)) { + $users[] = [ + 'id' => $user->getId(), + 'label' => $user->getUsername(), + ]; + } + } + + return $users; + } +} diff --git a/src/Handler/User/Reset2FaSecretHandler.php b/src/Handler/User/Reset2FaSecretHandler.php new file mode 100644 index 00000000..b767e6b7 --- /dev/null +++ b/src/Handler/User/Reset2FaSecretHandler.php @@ -0,0 +1,36 @@ +setTwoFactorAuthentication('enabled', false); + $user->setTwoFactorAuthentication('secret', ''); + $user->save(); + } +} diff --git a/src/Handler/User/ResetMy2FaSecretHandler.php b/src/Handler/User/ResetMy2FaSecretHandler.php new file mode 100644 index 00000000..50f6df92 --- /dev/null +++ b/src/Handler/User/ResetMy2FaSecretHandler.php @@ -0,0 +1,33 @@ +userContext->getAdminUser(); + $user->setTwoFactorAuthentication('required', true); + $user->setTwoFactorAuthentication('enabled', false); + $user->setTwoFactorAuthentication('secret', ''); + $user->save(); + } +} diff --git a/src/Handler/User/SearchUsersHandler.php b/src/Handler/User/SearchUsersHandler.php new file mode 100644 index 00000000..23be879b --- /dev/null +++ b/src/Handler/User/SearchUsersHandler.php @@ -0,0 +1,48 @@ +setCondition('name LIKE ? OR firstname LIKE ? OR lastname LIKE ? OR email LIKE ? OR id = ?', [$q, $q, $q, $q, $query]); + $list->setOrder('ASC'); + $list->setOrderKey('name'); + + $users = []; + foreach ($list->getUsers() as $user) { + if ($user->getId() && $user->getName() !== 'system') { + $users[] = [ + 'id' => $user->getId(), + 'name' => $user->getName(), + 'email' => $user->getEmail(), + 'firstname' => $user->getFirstname(), + 'lastname' => $user->getLastname(), + ]; + } + } + + return $users; + } +} diff --git a/src/Handler/User/SendInvitationLinkHandler.php b/src/Handler/User/SendInvitationLinkHandler.php new file mode 100644 index 00000000..8ee110bc --- /dev/null +++ b/src/Handler/User/SendInvitationLinkHandler.php @@ -0,0 +1,91 @@ +'); + } + + if (!$user->getActive()) { + $message .= 'User is not active
'; + } + + if (!$user->getEmail()) { + $message .= 'User has no email address
'; + } + + if (empty($message)) { + if (!$domain) { + return new SendInvitationLinkResult(success: false, message: 'No main domain set in system settings, unable to generate login invitation link'); + } + + if (!$user->getPassword()) { + $user->setPassword(bin2hex(random_bytes(16))); + $user->save(); + } + + $token = Tool\Authentication::generateTokenByUser($user); + + $context = $this->router->getContext(); + $context->setHost($domain); + + $loginUrl = $this->loginUrlGenerator->generate(['token' => $token, 'reset' => true]); + + try { + $mail = Tool::getMail([$user->getEmail()], 'OpenDXP login invitation for ' . Tool::getHostname()); + $mail->setIgnoreDebugMode(true); + $mail->text("Login to OpenDXP and change your password using the following link. This temporary login link will expire in 24 hours: \r\n\r\n" . $loginUrl); + $mail->send(); + + $success = true; + $message = sprintf($this->translator->trans('invitation_link_sent', [], 'admin_ext'), $user->getEmail()); + } catch (\Symfony\Component\HttpKernel\Exception\BadRequestHttpException $e) { + $message .= $e->getMessage() . '
'; + } catch (Exception) { + $message .= 'could not send email'; + } + } + + return new SendInvitationLinkResult(success: $success, message: $message); + } +} diff --git a/src/Handler/User/SendInvitationLinkResult.php b/src/Handler/User/SendInvitationLinkResult.php new file mode 100644 index 00000000..4e53d56b --- /dev/null +++ b/src/Handler/User/SendInvitationLinkResult.php @@ -0,0 +1,26 @@ +userContext->getAdminUser(); + if ($user === null || $user->getId() !== $requestedUserId) { + throw new BadRequestHttpException('User ID mismatch'); + } + unset($values['name'], $values['id'], $values['admin'], $values['permissions'], $values['roles'], $values['active']); + + if (!empty($values['new_password'])) { + $oldPasswordCheck = false; + + if ($isPasswordReset) { + $oldPasswordCheck = true; + } elseif (!empty($values['old_password'])) { + $errors = $this->validator->validate($values['old_password'], [new UserPassword()]); + + if (count($errors) === 0) { + $oldPasswordCheck = true; + } + } + + if (strlen($values['new_password']) < 10) { + throw new Exception('Passwords have to be at least 10 characters long'); + } + + if ($oldPasswordCheck && $values['new_password'] == $values['retype_password']) { + if (Tool\Authentication::verifyPassword($user, $values['new_password'])) { + throw new Exception('The new password cannot be the same as the old one'); + } + + $values['password'] = Tool\Authentication::getPasswordHash($user->getName(), $values['new_password']); + } else { + if (!$oldPasswordCheck) { + throw new BadRequestHttpException('incorrect_password'); + } + + throw new BadRequestHttpException('password_cannot_be_changed'); + } + } + + $user->setValues($values); + + if ($keyBindingsJson !== null) { + $keyBindings = json_decode($keyBindingsJson, true); + $tmpArray = []; + foreach ($keyBindings as $item) { + $tmpArray[] = json_decode($item, true); + } + $tmpArray = array_values(array_filter($tmpArray)); + $tmpArray = json_encode($tmpArray); + + $user->setKeyBindings($tmpArray); + } + + $user->save(); + } +} diff --git a/src/Handler/User/UpdateUserHandler.php b/src/Handler/User/UpdateUserHandler.php new file mode 100644 index 00000000..b92c49f1 --- /dev/null +++ b/src/Handler/User/UpdateUserHandler.php @@ -0,0 +1,138 @@ +userContext->getAdminUser(); + $currentUserIsAdmin = $adminUser?->isAdmin() ?? false; + + /** @var User\UserRole|null $user */ + $user = User\UserRole::getById($id); + if (!$user) { + throw new NotFoundHttpException('User not found'); + } + + if ($user instanceof User && $user->isAdmin() && !$currentUserIsAdmin) { + throw new AccessDeniedHttpException('Only admin users are allowed to modify admin users'); + } + if ($values !== null) { + if (!empty($values['password'])) { + if (strlen($values['password']) < 10) { + throw new Exception('Passwords have to be at least 10 characters long'); + } + $values['password'] = Tool\Authentication::getPasswordHash($user->getName(), $values['password']); + } + + // check if there are permissions transmitted, if so reset them all to false (they will be set later) + foreach ($values as $key => $value) { + if (str_starts_with($key, 'permission_')) { + $user->setAllAclToFalse(); + + break; + } + } + + if ($user instanceof User && isset($values['2fa_required'])) { + $user->setTwoFactorAuthentication('required', (bool) $values['2fa_required']); + } + + $user->setValues($values); + + // only admins are allowed to create admin users + if ($user instanceof User && !$currentUserIsAdmin) { + $user->setAdmin(false); + } + + // check for permissions + $availableUserPermissionsList = new User\Permission\Definition\Listing(); + $availableUserPermissions = $availableUserPermissionsList->load(); + + foreach ($availableUserPermissions as $permission) { + if (isset($values['permission_' . $permission->getKey()])) { + $user->setPermission($permission->getKey(), (bool) $values['permission_' . $permission->getKey()]); + } + } + + // check for workspaces + if ($workspaces !== null) { + $processedPaths = ['object' => [], 'asset' => [], 'document' => []]; + foreach ($workspaces as $type => $spaces) { + $newWorkspaces = []; + foreach ($spaces as $space) { + if (in_array($space['path'], $processedPaths[$type])) { + throw new Exception('Error saving workspaces as multiple entries found for path "' . $space['path'] . '" in ' . $this->translator->trans((string) $type, [], 'admin') . 's'); + } + + $element = Element\Service::getElementByPath($type, $space['path']); + if ($element) { + $className = '\\OpenDxp\\Model\\User\\Workspace\\' . Element\Service::getBaseClassNameForElement($type); + $workspace = new $className(); + $workspace->setValues($space); + + $workspace->setCid($element->getId()); + $workspace->setCpath($element->getRealFullPath()); + $workspace->setUserId($user->getId()); + + $newWorkspaces[] = $workspace; + $processedPaths[$type][] = $space['path']; + } + } + $user->{'setWorkspaces' . ucfirst($type)}($newWorkspaces); + } + } + } + + if ($user instanceof User && $keyBindingsJson !== null) { + $keyBindings = json_decode($keyBindingsJson, true); + $tmpArray = []; + foreach ($keyBindings as $item) { + $tmpArray[] = json_decode($item, true); + } + $tmpArray = array_values(array_filter($tmpArray)); + $tmpArray = json_encode($tmpArray); + + $user->setKeyBindings($tmpArray); + } + + $user->save(); + } +} diff --git a/src/Handler/User/UploadUserImageHandler.php b/src/Handler/User/UploadUserImageHandler.php new file mode 100644 index 00000000..4e28a1f1 --- /dev/null +++ b/src/Handler/User/UploadUserImageHandler.php @@ -0,0 +1,61 @@ +userContext->getAdminUser(); + $targetUserId ??= (int) $adminUser?->getId(); + + $userObj = User::getById($targetUserId); + if (!$userObj) { + throw new NotFoundHttpException('User not found'); + } + + if (!$adminUser?->isAdmin()) { + if ($userObj->isAdmin()) { + throw new AccessDeniedHttpException('Only admin users are allowed to modify admin users'); + } + if ($adminUser?->getId() !== $userObj->getId()) { + throw new AccessDeniedHttpException('Only admin users are allowed to modify users other than themselves'); + } + } + + $assetType = Asset::getTypeFromMimeMapping($avatarFile->getMimeType(), $avatarFile->getFileName()); + if ($assetType !== 'image') { + throw new BadRequestHttpException('Unsupported file format.'); + } + + $userObj->setImage($avatarFile->getPathname()); + } +} diff --git a/src/Handler/Workflow/GetModalCustomHtmlHandler.php b/src/Handler/Workflow/GetModalCustomHtmlHandler.php new file mode 100644 index 00000000..603d602e --- /dev/null +++ b/src/Handler/Workflow/GetModalCustomHtmlHandler.php @@ -0,0 +1,86 @@ +elementResolver->resolve($ctype, $cid); + + $workflow = $this->workflowRegistry->get($element, $workflowName); + + if ($isGlobalAction) { + $globalAction = $this->workflowManager->getGlobalAction($workflow->getName(), $transition); + if ($globalAction) { + return new GetModalCustomHtmlResult( + customHtml: $this->buildCustomHtml($globalAction->getCustomHtmlService(), $element), + ); + } + } elseif ($workflow->can($element, $transition)) { + $enabledTransitions = $workflow->getEnabledTransitions($element); + $matchedTransition = null; + foreach ($enabledTransitions as $_transition) { + if ($_transition->getName() === $transition) { + $matchedTransition = $_transition; + } + } + + if ($matchedTransition instanceof Transition) { + return new GetModalCustomHtmlResult( + customHtml: $this->buildCustomHtml($matchedTransition->getCustomHtmlService(), $element), + ); + } + } + + throw new BadRequestHttpException('error validating the action on this element, element cannot perform this action'); + } + + private function buildCustomHtml(?CustomHtmlServiceInterface $customHtmlService, ConcreteObject|Document|Asset $element): array + { + $customHtml = []; + if ($customHtmlService) { + foreach (['top', 'center', 'bottom'] as $position) { + $customHtml[$position] = $customHtmlService->renderHtmlForRequestedPosition($element, $position); + } + } + + return $customHtml; + } +} diff --git a/src/Handler/Workflow/GetModalCustomHtmlResult.php b/src/Handler/Workflow/GetModalCustomHtmlResult.php new file mode 100644 index 00000000..92886821 --- /dev/null +++ b/src/Handler/Workflow/GetModalCustomHtmlResult.php @@ -0,0 +1,25 @@ +elementResolver->resolve($ctype, $cid); + + $data = []; + + foreach ($this->workflowManager->getAllWorkflowsForSubject($element) as $workflow) { + $workflowConfig = $this->workflowManager->getWorkflowConfig($workflow->getName()); + + $svg = null; + $msg = ''; + + try { + $svg = $this->getWorkflowSvg($workflow, $element); + } catch (InvalidArgumentException $e) { + $msg = $e->getMessage(); + } + + $url = $this->router->generate( + 'opendxp_admin_workflow_show_graph', + [ + 'cid' => $cid, + 'ctype' => $ctype, + 'workflow' => $workflow->getName(), + ] + ); + + $allowedTransitions = $this->actionsButtonService->getAllowedTransitions($workflow, $element); + $globalActions = $this->actionsButtonService->getGlobalActions($workflow, $element); + + $data[] = [ + 'workflowName' => $this->translator->trans($workflowConfig->getLabel(), [], 'admin'), + 'placeInfo' => $this->placeStatusInfo->getAllPalacesHtml($element, $workflow->getName()), + 'graph' => $msg ?: '
' . $svg . '
', + 'allowedTransitions' => $allowedTransitions, + 'globalActions' => $globalActions, + ]; + } + + return new GetWorkflowDetailsResult(data: $data); + } + + private function getWorkflowSvg(WorkflowInterface $workflow, ConcreteObject|Document|Asset $element): string + { + $marking = $workflow->getMarking($element); + + $php = Console::getExecutable('php'); + $dot = Console::getExecutable('dot'); + + if (!$php) { + throw new InvalidArgumentException($this->translator->trans('workflow_cmd_not_found', ['php'], 'admin')); + } + + if (!$dot) { + throw new InvalidArgumentException($this->translator->trans('workflow_cmd_not_found', ['dot'], 'admin')); + } + + $cmd = $php . ' ' . OPENDXP_PROJECT_ROOT . '/bin/console opendxp:workflow:dump ${WNAME} ${WPLACES} | ${DOT} -Tsvg'; + $params = [ + 'WNAME' => $workflow->getName(), + 'WPLACES' => implode(' ', array_keys($marking->getPlaces())), + 'DOT' => $dot, + ]; + + Console::addLowProcessPriority($cmd); + $process = Process::fromShellCommandline($cmd); + $process->run(null, $params); + + return $process->getOutput(); + } +} diff --git a/src/Handler/Workflow/GetWorkflowDetailsResult.php b/src/Handler/Workflow/GetWorkflowDetailsResult.php new file mode 100644 index 00000000..87de7055 --- /dev/null +++ b/src/Handler/Workflow/GetWorkflowDetailsResult.php @@ -0,0 +1,25 @@ +elementResolver->resolve($ctype, $cid); + + $workflow = $this->workflowManager->getWorkflowIfExists($element, $workflowName); + + if (empty($workflow)) { + return new GetWorkflowFormResult( + message: 'workflow not found', + notesEnabled: false, + notesRequired: false, + additionalFields: [], + ); + } + + $enabledTransitions = $workflow->getEnabledTransitions($element); + $transition = null; + foreach ($enabledTransitions as $_transition) { + if ($_transition->getName() === $transitionName) { + $transition = $_transition; + } + } + + if (!$transition instanceof Transition) { + return new GetWorkflowFormResult( + message: sprintf('transition %s currently not allowed', $transitionName), + notesEnabled: false, + notesRequired: false, + additionalFields: [], + ); + } + + return new GetWorkflowFormResult( + message: '', + notesEnabled: false, + notesRequired: $transition->getNotesCommentRequired(), + additionalFields: [], + ); + } +} diff --git a/src/Handler/Workflow/GetWorkflowFormResult.php b/src/Handler/Workflow/GetWorkflowFormResult.php new file mode 100644 index 00000000..12cb4666 --- /dev/null +++ b/src/Handler/Workflow/GetWorkflowFormResult.php @@ -0,0 +1,28 @@ +elementResolver->resolve($ctype, $cid); + + $workflow = $this->workflowManager->getWorkflowByName($workflowName); + $marking = $workflow->getMarking($element); + + $php = Console::getExecutable('php'); + $dot = Console::getExecutable('dot'); + + if (!$php) { + throw new InvalidArgumentException($this->translator->trans('workflow_cmd_not_found', ['php'], 'admin')); + } + + if (!$dot) { + throw new InvalidArgumentException($this->translator->trans('workflow_cmd_not_found', ['dot'], 'admin')); + } + + $cmd = $php . ' ' . OPENDXP_PROJECT_ROOT . '/bin/console opendxp:workflow:dump ${WNAME} ${WPLACES} | ${DOT} -Tsvg'; + $params = [ + 'WNAME' => $workflow->getName(), + 'WPLACES' => implode(' ', array_keys($marking->getPlaces())), + 'DOT' => $dot, + ]; + + Console::addLowProcessPriority($cmd); + $process = Process::fromShellCommandline($cmd); + $process->run(null, $params); + + return $process->getOutput(); + } +} diff --git a/src/Handler/Workflow/SubmitGlobalActionHandler.php b/src/Handler/Workflow/SubmitGlobalActionHandler.php new file mode 100644 index 00000000..6a167e79 --- /dev/null +++ b/src/Handler/Workflow/SubmitGlobalActionHandler.php @@ -0,0 +1,53 @@ +elementResolver->resolve($ctype, $cid); + + $workflow = $this->workflowRegistry->get($element, $workflowName); + + $globalAction = $this->workflowManager->getGlobalAction($workflowName, $transition); + $saveSubject = !$globalAction || $globalAction->getSaveSubject(); + + $this->workflowManager->applyGlobalAction($workflow, $element, $transition, $workflowOptions, $saveSubject); + } +} diff --git a/src/Handler/Workflow/SubmitWorkflowTransitionHandler.php b/src/Handler/Workflow/SubmitWorkflowTransitionHandler.php new file mode 100644 index 00000000..46d6a8e3 --- /dev/null +++ b/src/Handler/Workflow/SubmitWorkflowTransitionHandler.php @@ -0,0 +1,62 @@ +elementResolver->resolve($ctype, $cid); + + $workflow = $this->workflowRegistry->get($element, $workflowName); + + if (!$workflow->can($element, $transition)) { + $blockTransitionList = $workflow->buildTransitionBlockerList($element, $transition); + $reasons = array_map( + static fn ($item) => $item->getMessage(), + iterator_to_array($blockTransitionList->getIterator(), true) + ); + + return new SubmitWorkflowTransitionResult(blocked: true, blockerReasons: $reasons); + } + + $this->workflowManager->applyWithAdditionalData($workflow, $element, $transition, $workflowOptions, true); + + return new SubmitWorkflowTransitionResult(blocked: false, blockerReasons: []); + } +} diff --git a/src/Handler/Workflow/SubmitWorkflowTransitionResult.php b/src/Handler/Workflow/SubmitWorkflowTransitionResult.php new file mode 100644 index 00000000..d0c3fe6e --- /dev/null +++ b/src/Handler/Workflow/SubmitWorkflowTransitionResult.php @@ -0,0 +1,27 @@ +getLatestVersion($adminUserId); + if ($latestVersion) { + $latestObj = $latestVersion->loadData(); + if ($latestObj instanceof Concrete) { + $draftVersion = $latestVersion; + + return $latestObj; + } + } + + return $object; + } +} diff --git a/src/Helper/DocumentVersionHelper.php b/src/Helper/DocumentVersionHelper.php new file mode 100644 index 00000000..95f580b0 --- /dev/null +++ b/src/Helper/DocumentVersionHelper.php @@ -0,0 +1,49 @@ +getLatestVersion($userId); + if ($latestVersion) { + $latestDoc = $latestVersion->loadData(); + if ($latestDoc instanceof PageSnippet) { + $draftVersion = $latestVersion; + + return $latestDoc; + } + } + + return $document; + } +} diff --git a/src/Http/Result/FileResult.php b/src/Http/Result/FileResult.php new file mode 100644 index 00000000..8473b01a --- /dev/null +++ b/src/Http/Result/FileResult.php @@ -0,0 +1,27 @@ +dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); + $adminStyle = $event->getAdminStyle(); + + $data['general']['iconCls'] = $adminStyle->getElementIconClass() !== false ? $adminStyle->getElementIconClass() : null; + if (!$data['general']['iconCls']) { + $data['general']['icon'] = $adminStyle->getElementIcon() !== false ? $adminStyle->getElementIcon() : null; + } else { + $data['general']['icon'] = null; + } + if ($adminStyle->getElementCssClass() !== false) { + if (!isset($data['general']['cls'])) { + $data['general']['cls'] = ''; + } + $data['general']['cls'] .= $adminStyle->getElementCssClass() . ' '; + } + $data['general']['qtipCfg'] = $adminStyle->getElementQtipConfig(); + $elementText = $adminStyle->getElementText(); + if ($elementText !== null) { + $data['general']['text'] = $elementText; + } + } +} diff --git a/src/Normalizer/DataObject/CustomLayoutNormalizer.php b/src/Normalizer/DataObject/CustomLayoutNormalizer.php new file mode 100644 index 00000000..83b0a780 --- /dev/null +++ b/src/Normalizer/DataObject/CustomLayoutNormalizer.php @@ -0,0 +1,63 @@ +getClass()->getFieldDefinitions()), true); + + if (is_array($layoutArray)) { + $this->injectValuesForCustomLayout($layoutArray, $classFieldDefinitions); + } + + $data['layout'] = $layoutArray; + } + + private function injectValuesForCustomLayout(array &$layout, array $classFieldDefinitions): void + { + foreach ($layout['children'] as &$child) { + if ($child['datatype'] === 'layout') { + $this->injectValuesForCustomLayout($child, $classFieldDefinitions); + } else { + foreach ($classFieldDefinitions[$child['name']] as $key => $value) { + if (array_key_exists($key, $child) && ($child[$key] === null || $child[$key] === '' || (is_array($child[$key]) && empty($child[$key])))) { + $child[$key] = $value; + } + } + } + } + } +} diff --git a/src/Normalizer/DataObject/DraftNormalizer.php b/src/Normalizer/DataObject/DraftNormalizer.php new file mode 100644 index 00000000..91bad9dc --- /dev/null +++ b/src/Normalizer/DataObject/DraftNormalizer.php @@ -0,0 +1,50 @@ +getId(), ['force' => true]); + if ($fresh->getModificationDate() < $draftVersion->getDate()) { + $data['draft'] = [ + 'id' => $draftVersion->getId(), + 'modificationDate' => $draftVersion->getDate(), + 'isAutoSave' => $draftVersion->isAutoSave(), + ]; + } + } +} diff --git a/src/Normalizer/DataObject/TreeStyleNormalizer.php b/src/Normalizer/DataObject/TreeStyleNormalizer.php new file mode 100644 index 00000000..9c68e1ba --- /dev/null +++ b/src/Normalizer/DataObject/TreeStyleNormalizer.php @@ -0,0 +1,58 @@ +dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); + $adminStyle = $event->getAdminStyle(); + + $data['iconCls'] = $adminStyle->getElementIconClass() !== false ? $adminStyle->getElementIconClass() : null; + if (!$data['iconCls']) { + $data['icon'] = $adminStyle->getElementIcon() !== false ? $adminStyle->getElementIcon() : null; + } else { + $data['icon'] = null; + } + if ($adminStyle->getElementCssClass() !== false) { + $data['cls'] = ($data['cls'] ?? '') . $adminStyle->getElementCssClass() . ' '; + } + $data['qtipCfg'] = $adminStyle->getElementQtipConfig(); + $elementText = $adminStyle->getElementText(); + if ($elementText !== null) { + $data['text'] = $elementText; + } + } +} diff --git a/src/Normalizer/DataObject/UserNamesNormalizer.php b/src/Normalizer/DataObject/UserNamesNormalizer.php new file mode 100644 index 00000000..11528dad --- /dev/null +++ b/src/Normalizer/DataObject/UserNamesNormalizer.php @@ -0,0 +1,46 @@ +resolveUserName($element->getUserOwner()); + $modificationName = $element->getUserOwner() === $element->getUserModification() + ? $ownerName + : $this->resolveUserName($element->getUserModification()); + + $data['general']['userOwnerUsername'] = $ownerName['userName']; + $data['general']['userOwnerFullname'] = $ownerName['fullName']; + $data['general']['userModificationUsername'] = $modificationName['userName']; + $data['general']['userModificationFullname'] = $modificationName['fullName']; + } +} diff --git a/src/Normalizer/Document/AdminStyleNormalizer.php b/src/Normalizer/Document/AdminStyleNormalizer.php new file mode 100644 index 00000000..7df6d8ae --- /dev/null +++ b/src/Normalizer/Document/AdminStyleNormalizer.php @@ -0,0 +1,55 @@ +dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); + $adminStyle = $event->getAdminStyle(); + + $data['iconCls'] = $adminStyle->getElementIconClass() !== false ? $adminStyle->getElementIconClass() : null; + $data['icon'] = !$data['iconCls'] && $adminStyle->getElementIcon() !== false ? $adminStyle->getElementIcon() : null; + + if ($adminStyle->getElementCssClass() !== false) { + $data['cls'] = ($data['cls'] ?? '') . $adminStyle->getElementCssClass() . ' '; + } + + $data['qtipCfg'] = $adminStyle->getElementQtipConfig(); + + $elementText = $adminStyle->getElementText(); + if ($elementText !== null) { + $data['text'] = $elementText; + } + } +} diff --git a/src/Normalizer/Document/DocumentMetaNormalizer.php b/src/Normalizer/Document/DocumentMetaNormalizer.php new file mode 100644 index 00000000..a7780f0d --- /dev/null +++ b/src/Normalizer/Document/DocumentMetaNormalizer.php @@ -0,0 +1,59 @@ +getId(), ['force' => true]); + + $data['versionDate'] = $fresh->getModificationDate(); + $data['userPermissions'] = $element->getUserPermissions(); + $data['idPath'] = ElementService::getIdPath($element); + $data['php'] = [ + 'classes' => [$element::class, ...array_values(class_parents($element))], + 'interfaces' => array_values(class_implements($element)), + ]; + } +} diff --git a/src/Normalizer/Document/DraftNormalizer.php b/src/Normalizer/Document/DraftNormalizer.php new file mode 100644 index 00000000..dc4b5197 --- /dev/null +++ b/src/Normalizer/Document/DraftNormalizer.php @@ -0,0 +1,52 @@ +getId(), ['force' => true]); + if ($fresh->getModificationDate() < $draftVersion->getDate()) { + $data['draft'] = [ + 'id' => $draftVersion->getId(), + 'modificationDate' => $draftVersion->getDate(), + 'isAutoSave' => $draftVersion->isAutoSave(), + ]; + } + } +} diff --git a/src/Normalizer/Document/PropertiesNormalizer.php b/src/Normalizer/Document/PropertiesNormalizer.php new file mode 100644 index 00000000..73cd2d59 --- /dev/null +++ b/src/Normalizer/Document/PropertiesNormalizer.php @@ -0,0 +1,51 @@ +getProperties()); + } +} diff --git a/src/Normalizer/Document/TranslationNormalizer.php b/src/Normalizer/Document/TranslationNormalizer.php new file mode 100644 index 00000000..fc410c9c --- /dev/null +++ b/src/Normalizer/Document/TranslationNormalizer.php @@ -0,0 +1,56 @@ +getTranslations($element); + $unlinkTranslations = $service->getTranslations($element, 'unlink'); + $language = $element->getProperty('language'); + unset($translations[$language], $unlinkTranslations[$language]); + $data['translations'] = $translations; + $data['unlinkTranslations'] = $unlinkTranslations; + } +} diff --git a/src/Normalizer/Element/AbstractUserNamesNormalizer.php b/src/Normalizer/Element/AbstractUserNamesNormalizer.php new file mode 100644 index 00000000..3a868f65 --- /dev/null +++ b/src/Normalizer/Element/AbstractUserNamesNormalizer.php @@ -0,0 +1,46 @@ + '', 'fullName' => $this->translator->trans('user_unknown', [], 'admin')]; + + if ($userId === null) { + return $unknown; + } + + $user = User::getById($userId); + if (empty($user)) { + return $unknown; + } + + return [ + 'userName' => $user->getName(), + 'fullName' => empty($user->getFullName()) ? $user->getName() : $user->getFullName(), + ]; + } +} diff --git a/src/Normalizer/Element/UserNamesNormalizer.php b/src/Normalizer/Element/UserNamesNormalizer.php new file mode 100644 index 00000000..3ee86121 --- /dev/null +++ b/src/Normalizer/Element/UserNamesNormalizer.php @@ -0,0 +1,60 @@ +resolveUserName($element->getUserOwner()); + $modificationName = $element->getUserOwner() === $element->getUserModification() + ? $ownerName + : $this->resolveUserName($element->getUserModification()); + + $data['userOwnerUsername'] = $ownerName['userName']; + $data['userOwnerFullname'] = $ownerName['fullName']; + $data['userModificationUsername'] = $modificationName['userName']; + $data['userModificationFullname'] = $modificationName['fullName']; + } +} diff --git a/src/Normalizer/ElementResponseNormalizer.php b/src/Normalizer/ElementResponseNormalizer.php new file mode 100644 index 00000000..07f87fe1 --- /dev/null +++ b/src/Normalizer/ElementResponseNormalizer.php @@ -0,0 +1,35 @@ + $normalizers */ + public function __construct(private readonly iterable $normalizers) {} + + public function normalize(ElementInterface $element, array &$data, string $handlerClass, array $context = []): void + { + foreach ($this->normalizers as $normalizer) { + if ($normalizer->supports($element, $handlerClass)) { + $normalizer->normalize($element, $data, $context); + } + } + } +} diff --git a/src/Normalizer/ElementResponseNormalizerInterface.php b/src/Normalizer/ElementResponseNormalizerInterface.php new file mode 100644 index 00000000..9fe5df2d --- /dev/null +++ b/src/Normalizer/ElementResponseNormalizerInterface.php @@ -0,0 +1,27 @@ +request->has('image'); + + return new self( + task: $request->request->getString('task'), + metadata: $request->request->has('metadata') + ? (json_decode($request->request->getString('metadata'), true) ?? null) + : null, + propertiesData: $request->request->has('properties') + ? (json_decode($request->request->getString('properties'), true) ?? null) + : null, + schedulerData: $request->request->has('scheduler') + ? (json_decode($request->request->getString('scheduler'), true) ?? null) + : null, + rawData: $request->request->get('data'), + hasImage: $hasImage, + imageData: $hasImage + ? (json_decode($request->request->getString('image'), true) ?? null) + : null, + ); + } +} diff --git a/src/Payload/DataObject/DataObjectPayload.php b/src/Payload/DataObject/DataObjectPayload.php new file mode 100644 index 00000000..9c3675a6 --- /dev/null +++ b/src/Payload/DataObject/DataObjectPayload.php @@ -0,0 +1,54 @@ +request->has('data') + ? (json_decode($request->request->getString('data'), true) ?? []) + : []; + + $properties = $request->request->has('properties') + ? (json_decode($request->request->getString('properties'), true) ?? []) + : []; + + $scheduler = []; + if ($request->request->has('scheduler')) { + $raw = json_decode($request->request->getString('scheduler'), true); + $scheduler = !empty($raw) ? $raw : []; + } + + return new self( + task: $taskOverride ?? $request->query->getString('task'), + data: is_array($data) ? $data : [], + properties: is_array($properties) ? $properties : [], + scheduler: $scheduler, + ); + } +} diff --git a/src/Payload/Document/EmailPayload.php b/src/Payload/Document/EmailPayload.php new file mode 100644 index 00000000..188890b2 --- /dev/null +++ b/src/Payload/Document/EmailPayload.php @@ -0,0 +1,20 @@ +request->has('properties') + ? (json_decode($request->request->getString('properties'), true) ?? null) + : null, + ); + } +} diff --git a/src/Payload/Document/HardlinkPayload.php b/src/Payload/Document/HardlinkPayload.php new file mode 100644 index 00000000..4bec4653 --- /dev/null +++ b/src/Payload/Document/HardlinkPayload.php @@ -0,0 +1,46 @@ +query->getString('task')), + data: $request->request->has('data') + ? (json_decode($request->request->getString('data'), true) ?? null) + : null, + properties: $request->request->has('properties') + ? (json_decode($request->request->getString('properties'), true) ?? null) + : null, + scheduler: $request->request->has('scheduler') + ? (json_decode($request->request->getString('scheduler'), true) ?? null) + : null, + ); + } +} diff --git a/src/Payload/Document/LinkPayload.php b/src/Payload/Document/LinkPayload.php new file mode 100644 index 00000000..2635dca7 --- /dev/null +++ b/src/Payload/Document/LinkPayload.php @@ -0,0 +1,46 @@ +query->getString('task')), + data: $request->request->has('data') + ? (json_decode($request->request->getString('data'), true) ?? null) + : null, + properties: $request->request->has('properties') + ? (json_decode($request->request->getString('properties'), true) ?? null) + : null, + scheduler: $request->request->has('scheduler') + ? (json_decode($request->request->getString('scheduler'), true) ?? null) + : null, + ); + } +} diff --git a/src/Payload/Document/PagePayload.php b/src/Payload/Document/PagePayload.php new file mode 100644 index 00000000..de4df4bb --- /dev/null +++ b/src/Payload/Document/PagePayload.php @@ -0,0 +1,56 @@ +query->getString('task')), + settings: $request->request->has('settings') + ? (json_decode($request->request->getString('settings'), true) ?? null) + : null, + editables: $request->request->has('data') + ? (json_decode($request->request->getString('data'), true) ?? null) + : null, + appendEditables: (bool) $request->request->get('appendEditables'), + properties: $request->request->has('properties') + ? (json_decode($request->request->getString('properties'), true) ?? null) + : null, + scheduler: $request->request->has('scheduler') + ? (json_decode($request->request->getString('scheduler'), true) ?? null) + : null, + missingRequiredEditable: $request->request->has('missingRequiredEditable') + ? $request->request->getString('missingRequiredEditable') === 'true' + : null, + ); + } +} diff --git a/src/Payload/Document/RenderAreabrickIndexEditmodePayload.php b/src/Payload/Document/RenderAreabrickIndexEditmodePayload.php new file mode 100644 index 00000000..de05db5c --- /dev/null +++ b/src/Payload/Document/RenderAreabrickIndexEditmodePayload.php @@ -0,0 +1,44 @@ +request->get('documentId'), + blockStateStack: json_decode($request->request->getString('blockStateStack'), true), + realName: $request->request->getString('realName'), + areaBlockConfig: json_decode($request->request->getString('areablockConfig'), true), + areaBrickData: json_decode($request->request->getString('areablockData'), true), + index: (int) $request->request->get('index'), + ); + } +} \ No newline at end of file diff --git a/src/Payload/Document/RenderRenderletPayload.php b/src/Payload/Document/RenderRenderletPayload.php new file mode 100644 index 00000000..997d7011 --- /dev/null +++ b/src/Payload/Document/RenderRenderletPayload.php @@ -0,0 +1,44 @@ +query->get('type'), + id: $request->query->getInt('id') ?: null, + controller: $request->query->getString('controller') ?: null, + parentDocumentId: $request->query->getString('opendxp_parentDocument') ?: null, + template: $request->query->getString('template') ?: null, + query: $request->query->all(), + ); + } +} \ No newline at end of file diff --git a/src/Payload/Document/SnippetPayload.php b/src/Payload/Document/SnippetPayload.php new file mode 100644 index 00000000..67bdf2a3 --- /dev/null +++ b/src/Payload/Document/SnippetPayload.php @@ -0,0 +1,20 @@ +tokenResolver->getUser(); + if (!$user) { + return false; + } + + return $user->isAllowed($attribute); + } +} diff --git a/src/Service/AdminUserContext.php b/src/Service/AdminUserContext.php new file mode 100644 index 00000000..7beeab5e --- /dev/null +++ b/src/Service/AdminUserContext.php @@ -0,0 +1,38 @@ +tokenResolver->getUser(); + } + + public function getAdminUserProxy(): ?UserProxy + { + return $this->tokenResolver->getUserProxy(); + } +} diff --git a/src/Service/AdminUserContextInterface.php b/src/Service/AdminUserContextInterface.php new file mode 100644 index 00000000..8939e805 --- /dev/null +++ b/src/Service/AdminUserContextInterface.php @@ -0,0 +1,27 @@ + $data, + 'processed' => false, + ]); + $this->eventDispatcher->dispatch($updateEvent, AdminEvents::ASSET_LIST_BEFORE_UPDATE); + + if ($updateEvent->getArgument('processed')) { + return ['success' => true]; + } + + $data = $updateEvent->getArgument('data'); + + $asset = Asset::getById((int) $data['id']); + if (!$asset) { + throw new NotFoundHttpException('Asset not found'); + } + if (!$asset->isAllowed('publish')) { + throw new AccessDeniedHttpException("Permission denied. You don't have the rights to save this asset."); + } + + $loader = OpenDxp::getContainer()->get('opendxp.implementation_loader.asset.metadata.data'); + $metadata = $asset->getMetadata(null, null, false, true); + $dirty = false; + + unset($data['id']); + $fieldLanguage = $effectiveLanguage; + foreach ($data as $key => $value) { + $fieldDef = explode('~', $key); + $key = $fieldDef[0]; + if (isset($fieldDef[1])) { + $fieldLanguage = ($fieldDef[1] === 'none' ? '' : $fieldDef[1]); + } + + foreach ($metadata as &$em) { + if ($em['name'] == $key && $em['language'] == $fieldLanguage) { + try { + $dataImpl = $loader->build($em['type']); + $value = $dataImpl->getDataFromListfolderGrid($value, $em); + } catch (UnsupportedException) { + Logger::error('could not resolve metadata implementation for ' . $em['type']); + } + $em['data'] = $value; + $dirty = true; + break; + } + } + unset($em); + + if (!$dirty) { + $defaultMetadataFields = ['title', 'alt', 'copyright']; + if (in_array($key, $defaultMetadataFields)) { + $newEm = [ + 'name' => $key, + 'language' => $fieldLanguage, + 'type' => 'input', + 'data' => $value, + ]; + try { + $dataImpl = $loader->build($newEm['type']); + $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm); + } catch (UnsupportedException) { + Logger::error('could not resolve metadata implementation for ' . $newEm['type']); + } + $metadata[] = $newEm; + $dirty = true; + } else { + $predefined = Metadata\Predefined::getByName($key); + if ($predefined && (empty($predefined->getTargetSubtype()) + || $predefined->getTargetSubtype() === $asset->getType())) { + $newEm = [ + 'name' => $key, + 'language' => $fieldLanguage, + 'type' => $predefined->getType(), + 'data' => $value, + ]; + try { + $dataImpl = $loader->build($newEm['type']); + $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm); + } catch (UnsupportedException) { + Logger::error('could not resolve metadata implementation for ' . $newEm['type']); + } + $metadata[] = $newEm; + $dirty = true; + } + } + } + } + + if ($dirty) { + $metadataEvent = new GenericEvent(null, [ + 'id' => $asset->getId(), + 'metadata' => $metadata, + ]); + $this->eventDispatcher->dispatch($metadataEvent, AdminEvents::ASSET_METADATA_PRE_SET); + + $asset->setMetadataRaw($metadataEvent->getArgument('metadata')); + $asset->save(); + + return ['success' => true]; + } + + return ['success' => false, 'message' => 'something went wrong.']; + } catch (NotFoundHttpException|AccessDeniedHttpException $e) { + throw $e; + } catch (Exception $e) { + return ['success' => false, 'message' => $e->getMessage()]; + } + } + } else { + $list = $this->gridHelperService->prepareAssetListingForGrid($allParams, $this->userContext->getAdminUser()); + + $beforeListLoadEvent = new GenericEvent($this->gridHelperService, [ + 'list' => $list, + 'context' => $allParams, + ]); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD); + /** @var Asset\Listing $list */ + $list = $beforeListLoadEvent->getArgument('list'); + + $list->load(); + + $assets = []; + foreach ($list->getAssets() as $asset) { + if ($asset->isAllowed('list')) { + $assets[] = GridData\Asset::getData($asset, $allParams['fields'], $allParams['language'] ?? ''); + } + } + + $result = ['success' => true, 'data' => $assets, 'total' => $list->getTotalCount()]; + + $afterListLoadEvent = new GenericEvent($this->gridHelperService, [ + 'list' => $result, + 'context' => $allParams, + ]); + $this->eventDispatcher->dispatch($afterListLoadEvent, AdminEvents::ASSET_LIST_AFTER_LIST_LOAD); + + return $afterListLoadEvent->getArgument('list'); + } + + return ['success' => false]; + } +} diff --git a/src/Service/Asset/AssetPayloadMapper.php b/src/Service/Asset/AssetPayloadMapper.php new file mode 100644 index 00000000..10a5e43e --- /dev/null +++ b/src/Service/Asset/AssetPayloadMapper.php @@ -0,0 +1,125 @@ +metadata !== null) { + $metadataEvent = new GenericEvent(null, [ + 'id' => $asset->getId(), + 'metadata' => $payload->metadata, + ]); + $this->eventDispatcher->dispatch($metadataEvent, AdminEvents::ASSET_METADATA_PRE_SET); + $this->applyMetadata($metadataEvent->getArgument('metadata'), $asset); + } + + $this->applyProperties($payload->propertiesData, $asset); + $this->applyScheduler($payload->schedulerData, $asset); + $this->applyRawData($payload->rawData, $asset); + $this->applyImageSettings($payload->hasImage, $payload->imageData, $asset); + } + + private function applyMetadata(array $metadata, Asset $asset): void + { + $metadataValues = Asset\Service::minimizeMetadata($metadata['values'], 'editor'); + $asset->setMetadataRaw($metadataValues); + } + + private function applyProperties(?array $propertiesData, Asset $asset): void + { + if ($propertiesData === null) { + return; + } + + $properties = []; + foreach ($propertiesData as $propertyName => $propertyData) { + try { + $property = new Model\Property(); + $property->setType($propertyData['type']); + $property->setName($propertyName); + $property->setCtype('asset'); + $property->setDataFromEditmode($propertyData['data']); + $property->setInheritable($propertyData['inheritable']); + + $properties[$propertyName] = $property; + } catch (Exception) { + Logger::err("Can't add " . $propertyName . ' to asset ' . $asset->getRealFullPath()); + } + } + + $asset->setProperties($properties); + } + + private function applyScheduler(?array $schedulerData, Asset $asset): void + { + if ($schedulerData === null || !$asset->isAllowed('settings') || !method_exists($asset, 'setScheduledTasks')) { + return; + } + + $userId = $this->userContext->getAdminUser()?->getId(); + $tasks = []; + + foreach ($schedulerData as $taskData) { + $taskData['userId'] = $userId; + $tasks[] = new Task($taskData); + } + + $asset->setScheduledTasks($tasks); + } + + private function applyRawData(?string $rawData, Asset $asset): void + { + if ($rawData !== null) { + $asset->setData($rawData); + } + } + + private function applyImageSettings(bool $hasImage, ?array $imageData, Asset $asset): void + { + if (!$asset instanceof Asset\Image) { + return; + } + + if ($hasImage && $imageData !== null) { + if (isset($imageData['focalPoint'])) { + $asset->setCustomSetting('focalPointX', $imageData['focalPoint']['x']); + $asset->setCustomSetting('focalPointY', $imageData['focalPoint']['y']); + } + } else { + $asset->removeCustomSetting('focalPointX'); + $asset->removeCustomSetting('focalPointY'); + } + } +} diff --git a/src/Service/Asset/AssetPersistenceCoordinator.php b/src/Service/Asset/AssetPersistenceCoordinator.php new file mode 100644 index 00000000..7e2fee96 --- /dev/null +++ b/src/Service/Asset/AssetPersistenceCoordinator.php @@ -0,0 +1,48 @@ +sessionService->saveAsset($asset); + } else { + $asset->setUserModification($this->userContext->getAdminUser()->getId()); + $asset->save(); + } + + return new SaveAssetResult( + $asset->getModificationDate(), + $asset->getVersionCount(), + $this->elementService->getElementTreeNodeConfig($asset), + ); + } +} diff --git a/src/Service/Asset/AssetUploadService.php b/src/Service/Asset/AssetUploadService.php new file mode 100644 index 00000000..5c9f112a --- /dev/null +++ b/src/Service/Asset/AssetUploadService.php @@ -0,0 +1,234 @@ +config['assets']['default_upload_path'] ?? '/'; + + if ($request->files->has('Filedata')) { + /** @var UploadedFile $file */ + $file = $request->files->get('Filedata'); + $filename = $file->getClientOriginalName(); + $sourcePath = $file->getPathname(); + } elseif ($request->request->get('type') === 'base64') { + $filename = $request->request->get('filename'); + $sourcePath = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/upload-base64' . uniqid('', false) . '.tmp'; + $data = preg_replace('@^data:[^,]+;base64,@', '', $request->request->get('data')); + $filesystem = new Filesystem(); + $filesystem->dumpFile($sourcePath, base64_decode($data)); + } else { + throw new Exception('The filename of the asset is empty'); + } + + $parentId = $request->query->getInt('parentId'); + $parentPath = $request->query->get('parentPath'); + + if ($request->query->has('dir') && $request->query->has('parentId')) { + $parent = Asset::getById((int) $request->query->get('parentId')); + $dir = $request->query->get('dir'); + if (str_contains($dir, '..')) { + throw new Exception('not allowed'); + } + + $newPath = $parent->getRealFullPath() . '/' . trim($dir, '/ '); + + $maxRetries = 5; + $newParent = null; + for ($retries = 0; $retries < $maxRetries; $retries++) { + try { + $newParent = Asset\Service::createFolderByPath($newPath); + + break; + } catch (Exception $e) { + if ($retries < ($maxRetries - 1)) { + $waitTime = random_int(100000, 900000); + usleep($waitTime); + } else { + throw $e; + } + } + } + if ($newParent) { + $parentId = $newParent->getId(); + } + } elseif (!$request->query->get('parentId') && $parentPath) { + $parent = Asset::getByPath($parentPath); + if ($parent instanceof Asset\Folder) { + $parentId = $parent->getId(); + } else { + $parentId = Asset\Service::createFolderByPath($parentPath)->getId(); + } + } + + $filename = Element\Service::getValidKey($filename, 'asset'); + if (empty($filename)) { + throw new Exception('The filename of the asset is empty'); + } + + $context = $request->query->get('context'); + if ($context) { + $context = json_decode($context, true); + $context = $context ?: []; + + $this->validateManyToManyRelationAssetType($context, $filename, $sourcePath); + + $event = new ResolveUploadTargetEvent($parentId, $filename); + $event->setArgument('context', $context); + + OpenDxp::getEventDispatcher()->dispatch($event, AssetEvents::RESOLVE_UPLOAD_TARGET); + $filename = Element\Service::getValidKey($event->getFilename(), 'asset'); + $parentId = $event->getParentId(); + } + + if (!$parentId) { + $parentId = Asset\Service::createFolderByPath($defaultUploadPath)->getId(); + } + + $parentAsset = Asset::getById((int)$parentId); + + if (!$request->query->get('allowOverwrite')) { + $filename = $this->getSafeFilename($parentAsset->getRealFullPath(), $filename); + } + + if (!$parentAsset->isAllowed('create')) { + throw new AccessDeniedHttpException( + 'Missing the permission to create new assets in the folder: ' . $parentAsset->getRealFullPath() + ); + } + if (is_file($sourcePath) && filesize($sourcePath) < 1) { + throw new Exception('File is empty!'); + } + + if (!is_file($sourcePath)) { + throw new Exception('Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions of your temporary directories.'); + } + + $uploadAssetType = $request->query->get('uploadAssetType'); + if ($uploadAssetType) { + $mimetype = MimeTypes::getDefault()->guessMimeType($sourcePath); + $assetType = Asset::getTypeFromMimeMapping($mimetype, $filename); + + if ($uploadAssetType !== $assetType) { + throw new Exception("Mime type $mimetype does not match with asset type: $uploadAssetType"); + } + } + + $adminUser = $this->userContext->getAdminUser(); + + if ($request->query->get('allowOverwrite') && Asset\Service::pathExists($parentAsset->getRealFullPath().'/'.$filename)) { + $asset = Asset::getByPath($parentAsset->getRealFullPath().'/'.$filename); + $asset->setStream(fopen($sourcePath, 'rb', false, \OpenDxp\File::getContext())); + $asset->save(); + } else { + $asset = Asset::create($parentId, [ + 'filename' => $filename, + 'sourcePath' => $sourcePath, + 'userOwner' => $adminUser->getId(), + 'userModification' => $adminUser->getId(), + ]); + } + + @unlink($sourcePath); + + return [ + 'success' => true, + 'asset' => $asset, + ]; + } + + public function getSafeFilename(string $targetPath, string $filename): string + { + $pathinfo = pathinfo($filename); + $originalFilename = $pathinfo['filename']; + $originalFileextension = empty($pathinfo['extension']) ? '' : '.' . $pathinfo['extension']; + $count = 1; + + if ($targetPath === '/') { + $targetPath = ''; + } + + while (true) { + if (Asset\Service::pathExists($targetPath . '/' . $filename)) { + $filename = $originalFilename . '_' . $count . $originalFileextension; + $count++; + } else { + return $filename; + } + } + } + + /** + * @throws ValidationException + */ + private function validateManyToManyRelationAssetType(array $context, string $filename, string $sourcePath): void + { + if (isset($context['containerType'], $context['objectId'], $context['fieldname']) + && 'object' === $context['containerType'] + && $object = Concrete::getById($context['objectId']) + ) { + $fieldDefinition = $object->getClass()->getFieldDefinition($context['fieldname']); + if (!$fieldDefinition instanceof ManyToManyRelation) { + return; + } + + $mimeType = MimeTypes::getDefault()->guessMimeType($sourcePath); + $type = Asset::getTypeFromMimeMapping($mimeType, $filename); + + $allowedAssetTypes = $fieldDefinition->getAssetTypes(); + $allowedAssetTypes = array_column($allowedAssetTypes, 'assetTypes'); + + if ( + !( + $fieldDefinition->getAssetsAllowed() + && ($allowedAssetTypes === [] || in_array($type, $allowedAssetTypes, true)) + ) + ) { + throw new ValidationException(sprintf('Invalid relation in field `%s` [type: %s]', $context['fieldname'], $type)); + } + } + } +} diff --git a/src/Service/CustomLoginUrlGenerator.php b/src/Service/CustomLoginUrlGenerator.php new file mode 100644 index 00000000..b2b92506 --- /dev/null +++ b/src/Service/CustomLoginUrlGenerator.php @@ -0,0 +1,39 @@ +router->generate($this->customAdminRouteName, $params, UrlGeneratorInterface::ABSOLUTE_URL); + } catch (\Exception) { + return $this->router->generate($fallbackRoute, $params, UrlGeneratorInterface::ABSOLUTE_URL); + } + } +} diff --git a/src/Controller/Admin/DataObject/DataObjectActionsTrait.php b/src/Service/DataObject/DataObjectGridService.php similarity index 82% rename from src/Controller/Admin/DataObject/DataObjectActionsTrait.php rename to src/Service/DataObject/DataObjectGridService.php index 37fc5956..4343d9c0 100644 --- a/src/Controller/Admin/DataObject/DataObjectActionsTrait.php +++ b/src/Service/DataObject/DataObjectGridService.php @@ -1,5 +1,4 @@ setLocale($requestedLanguage); - } - } else { - $requestedLanguage = $request->getLocale(); - } - if ($action === 'update') { try { - $data = $this->decodeJson($allParams['data']); + $data = json_decode($allParams['data'], true, 512, JSON_THROW_ON_ERROR); $object = DataObject::getById((int)$data['id']); if (!$object instanceof DataObject\Concrete) { - throw $this->createNotFoundException('Object not found'); + throw new NotFoundHttpException('Object not found'); } if (!$object->isAllowed('publish')) { - throw $this->createAccessDeniedException("Permission denied. You don't have the rights to save this object."); + throw new AccessDeniedHttpException("Permission denied. You don't have the rights to save this object."); } - $objectData = $this->prepareObjectData($data, $object, $requestedLanguage, $localeService); + $objectData = $this->prepareObjectData($data, $object, $requestedLanguage); $object->setValues($objectData); if ($object->getPublished() === false) { @@ -96,6 +89,8 @@ protected function gridProxy( 'success' => true, 'data' => GridData\DataObject::getData($object, $allParams['fields'], $requestedLanguage), ]; + } catch (NotFoundHttpException|AccessDeniedHttpException $e) { + throw $e; } catch (Exception $e) { return [ 'success' => false, @@ -103,14 +98,14 @@ protected function gridProxy( ]; } } else { // get list of objects/variants - $list = $gridHelperService->prepareListingForGrid($allParams, $requestedLanguage, $this->getAdminUser()); + $list = $this->gridHelperService->prepareListingForGrid($allParams, $requestedLanguage, $this->userContext->getAdminUser()); if ($objectType === DataObject::OBJECT_TYPE_OBJECT) { - $beforeListLoadEvent = new GenericEvent($this, [ + $beforeListLoadEvent = new GenericEvent($this->gridHelperService, [ 'list' => $list, 'context' => $allParams, ]); - $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::OBJECT_LIST_BEFORE_LIST_LOAD); + $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::OBJECT_LIST_BEFORE_LIST_LOAD); /** @var DataObject\Listing\Concrete $list */ $list = $beforeListLoadEvent->getArgument('list'); } @@ -124,8 +119,8 @@ protected function gridProxy( $objects = []; foreach ($list->getObjects() as $object) { if ($csvMode) { - $o = DataObject\Service::getCsvDataForObject($object, $requestedLanguage, $allParams['fields'], GridData\DataObject::getHelperDefinitions(), $localeService, 'title', false, $allParams['context']); - // Like for treeGetChildrenByIdAction, so we respect isAllowed method which can be extended (object DI) for custom permissions, so relying only users_workspaces_object is insufficient and could lead security breach + $o = DataObject\Service::getCsvDataForObject($object, $requestedLanguage, $allParams['fields'], GridData\DataObject::getHelperDefinitions(), $this->localeService,'title', false, $allParams['context']); + // respect isAllowed method which can be extended via object DI for custom permissions if ($object->isAllowed('list')) { $objects[] = $o; } @@ -145,12 +140,12 @@ protected function gridProxy( ]; if ($objectType === DataObject::OBJECT_TYPE_OBJECT) { - $afterListLoadEvent = new GenericEvent($this, [ + $afterListLoadEvent = new GenericEvent($this->gridHelperService, [ 'list' => $result, 'context' => $allParams, ]); - $eventDispatcher->dispatch($afterListLoadEvent, AdminEvents::OBJECT_LIST_AFTER_LIST_LOAD); + $this->eventDispatcher->dispatch($afterListLoadEvent, AdminEvents::OBJECT_LIST_AFTER_LIST_LOAD); $result = $afterListLoadEvent->getArgument('list'); } @@ -165,7 +160,6 @@ private function prepareObjectData( array $data, DataObject\Concrete $object, string $requestedLanguage, - LocaleServiceInterface $localeService ): array { $user = Tool\Admin::getCurrentUser(); $languagePermissions = []; @@ -271,7 +265,7 @@ private function prepareObjectData( if ($localized instanceof DataObject\ClassDefinition\Data\Localizedfields) { $field = $localized->getFieldDefinition($key); if ($field) { - $currentLocale = $localeService->findLocale(); + $currentLocale = $this->localeService->findLocale(); if (!in_array($currentLocale, $languagePermissions)) { continue; } @@ -292,12 +286,12 @@ private function prepareObjectData( return $objectData; } - protected function getFieldDefinition(DataObject\ClassDefinition $class, string $key): ?DataObject\ClassDefinition\Data + private function getFieldDefinition(DataObject\ClassDefinition $class, string $key): ?DataObject\ClassDefinition\Data { return $class->getFieldDefinition($key); } - protected function getFieldDefinitionFromBrick(string $brickType, string $key): ?DataObject\ClassDefinition\Data + private function getFieldDefinitionFromBrick(string $brickType, string $key): ?DataObject\ClassDefinition\Data { $brickDefinition = DataObject\Objectbrick\Definition::getByKey($brickType); if ($brickDefinition) { diff --git a/src/Service/DataObject/DataObjectPayloadMapper.php b/src/Service/DataObject/DataObjectPayloadMapper.php new file mode 100644 index 00000000..76ae284a --- /dev/null +++ b/src/Service/DataObject/DataObjectPayloadMapper.php @@ -0,0 +1,214 @@ +data !== []) { + try { + $this->applyChanges($object, $payload->data); + } catch (Throwable) { + $this->applyChanges($objectFromDatabase, $payload->data); + } + } + + $this->assignProperties($payload->properties, $object); + $this->applyScheduler($payload->scheduler, $object); + } + + public function applyChanges(DataObject\Concrete $object, array $changes): void + { + foreach ($changes as $key => $value) { + $fd = $object->getClass()->getFieldDefinition($key); + if ($fd) { + if ($fd instanceof DataObject\ClassDefinition\Data\Localizedfields) { + $user = Tool\Admin::getCurrentUser(); + if (!$user->getAdmin()) { + $allowedLanguages = DataObject\Service::getLanguagePermissions($object, $user, 'lEdit'); + if (!is_null($allowedLanguages)) { + $allowedLanguages = array_keys($allowedLanguages); + $submittedLanguages = array_keys($changes[$key]); + foreach ($submittedLanguages as $submittedLanguage) { + if (!in_array($submittedLanguage, $allowedLanguages)) { + unset($value[$submittedLanguage]); + } + } + } + } + } + + if ($fd instanceof ReverseObjectRelation) { + $remoteClass = DataObject\ClassDefinition::getByName($fd->getOwnerClassName()); + $relations = $object->getRelationData($fd->getOwnerFieldName(), false, $remoteClass->getId()); + $toAdd = $this->detectAddedRemoteOwnerRelations($relations, $value); + $toDelete = $this->detectDeletedRemoteOwnerRelations($relations, $value); + if (count($toAdd) > 0 || count($toDelete) > 0) { + $this->processRemoteOwnerRelations($object, $toDelete, $toAdd, $fd->getOwnerFieldName()); + } + } else { + $object->setValue($key, $fd->getDataFromEditmode($value, $object)); + } + } + } + } + + private function assignProperties(array $propertiesData, DataObject\AbstractObject $object): void + { + if ($propertiesData === []) { + return; + } + + $properties = []; + foreach ($object->getProperties() as $p) { + if ($p->isInherited()) { + $properties[$p->getName()] = $p; + } + } + + foreach ($propertiesData as $propertyName => $propertyData) { + $value = $propertyData['data']; + + try { + $property = new Model\Property(); + $property->setType($propertyData['type']); + $property->setName($propertyName); + $property->setCtype('object'); + $property->setDataFromEditmode($value); + $property->setInheritable($propertyData['inheritable']); + + $properties[$propertyName] = $property; + } catch (Exception) { + Logger::err("Can't add " . $propertyName . ' to object ' . $object->getRealFullPath()); + } + } + + $object->setProperties($properties); + } + + private function applyScheduler(array $schedulerData, DataObject\AbstractObject $object): void + { + if ($schedulerData === []) { + return; + } + + $adminUser = $this->userContext->getAdminUser(); + $tasks = []; + foreach ($schedulerData as $taskData) { + $taskData['userId'] = $adminUser->getId(); + $task = new Task($taskData); + $tasks[] = $task; + } + + if ($object->isAllowed('settings') && method_exists($object, 'setScheduledTasks')) { + $object->setScheduledTasks($tasks); + } + } + + private function processRemoteOwnerRelations(DataObject\Concrete $object, array $toDelete, array $toAdd, string $ownerFieldName): void + { + $getter = 'get' . ucfirst($ownerFieldName); + $setter = 'set' . ucfirst($ownerFieldName); + + foreach ($toDelete as $id) { + $owner = DataObject::getById($id); + //TODO: lock ?! + if (method_exists($owner, $getter)) { + $currentData = $owner->$getter(); + if (is_array($currentData)) { + $counter = count($currentData); + for ($i = 0; $i < $counter; $i++) { + if ($currentData[$i]->getId() == $object->getId()) { + unset($currentData[$i]); + $owner->$setter($currentData); + + break; + } + } + } elseif ($currentData->getId() == $object->getId()) { + $owner->$setter(null); + } + } + $owner->setUserModification($object->getUserModification()); + $owner->save(); + Logger::debug('Saved object id [ ' . $owner->getId() . ' ] by remote modification through [' . $object->getId() . '], Action: deleted [ ' . $object->getId() . " ] from [ $ownerFieldName]"); + } + + foreach ($toAdd as $id) { + $owner = DataObject::getById($id); + //TODO: lock ?! + if (method_exists($owner, $getter)) { + $currentData = $owner->$getter(); + if (is_array($currentData)) { + $currentData[] = $object; + } else { + $currentData = $object; + } + $owner->$setter($currentData); + $owner->setUserModification($object->getUserModification()); + $owner->save(); + Logger::debug('Saved object id [ ' . $owner->getId() . ' ] by remote modification through [' . $object->getId() . '], Action: added [ ' . $object->getId() . " ] to [ $ownerFieldName ]"); + } + } + } + + private function detectDeletedRemoteOwnerRelations(array $relations, array $value): array + { + $originals = []; + $changed = []; + foreach ($relations as $r) { + $originals[] = $r['dest_id']; + } + + foreach ($value as $row) { + $changed[] = $row['id']; + } + + return array_diff($originals, $changed); + } + + private function detectAddedRemoteOwnerRelations(array $relations, array $value): array + { + $originals = []; + $changed = []; + + foreach ($relations as $r) { + $originals[] = $r['dest_id']; + } + + foreach ($value as $row) { + $changed[] = $row['id']; + } + + return array_diff($changed, $originals); + } +} diff --git a/src/Service/DataObject/DataObjectPersistenceCoordinator.php b/src/Service/DataObject/DataObjectPersistenceCoordinator.php new file mode 100644 index 00000000..7a0a84ab --- /dev/null +++ b/src/Service/DataObject/DataObjectPersistenceCoordinator.php @@ -0,0 +1,113 @@ +userContext->getAdminUser(); + + if ($task === 'unpublish') { + $object->setPublished(false); + } + + if ($task === 'publish') { + $object->setPublished(true); + } + + // unpublish and save version is possible without checking mandatory fields + if (in_array($task, ['unpublish', 'version', 'autoSave'])) { + $object->setOmitMandatoryCheck(true); + } + + if ($task === 'publish' || $task === 'unpublish') { + $object->save(); + $treeData = $this->elementService->getElementTreeNodeConfig($object); + $newObject = DataObject::getById($object->getId(), ['force' => true]); + + if ($task === 'publish') { + $object->deleteAutoSaveVersions($adminUser->getId()); + } + + return new SaveDataObjectResult( + modificationDate: $object->getModificationDate(), + versionDate: $newObject->getModificationDate(), + versionCount: $newObject->getVersionCount(), + treeData: $treeData, + draftData: [], + ); + } + + if ($task === 'scheduler' && $object->isAllowed('settings')) { + $object->saveScheduledTasks(); + + return new SaveDataObjectResult( + modificationDate: $object->getModificationDate(), + versionDate: $object->getModificationDate(), + versionCount: $object->getVersionCount(), + treeData: [], + draftData: [], + ); + } + + if ($object->isAllowed('save') || $object->isAllowed('publish')) { + $isAutoSave = $task === 'autoSave'; + $draftData = []; + + if ($object->isPublished() || $isAutoSave) { + $version = $object->saveVersion(true, true, null, $isAutoSave); + $draftData = [ + 'id' => $version->getId(), + 'modificationDate' => $version->getDate(), + 'isAutoSave' => $version->isAutoSave(), + ]; + } else { + $object->save(); + } + + if ($task === 'version') { + $object->deleteAutoSaveVersions($adminUser->getId()); + } + + $treeData = $this->elementService->getElementTreeNodeConfig($object); + $newObject = DataObject::getById($object->getId(), ['force' => true]); + + return new SaveDataObjectResult( + modificationDate: $object->getModificationDate(), + versionDate: $newObject->getModificationDate(), + versionCount: $newObject->getVersionCount(), + treeData: $treeData, + draftData: $draftData, + ); + } + + throw new AccessDeniedHttpException('Missing permission to save object'); + } +} diff --git a/src/Service/Document/DocumentPayloadMapper.php b/src/Service/Document/DocumentPayloadMapper.php new file mode 100644 index 00000000..2127c7f9 --- /dev/null +++ b/src/Service/Document/DocumentPayloadMapper.php @@ -0,0 +1,245 @@ +missingRequiredEditable !== null) { + $document->setMissingRequiredEditable($payload->missingRequiredEditable); + } + + if ($payload->settings !== null && ($payload->settings['published'] ?? false)) { + $document->setMissingRequiredEditable(null); + } + + $this->applySettings($payload->settings, $document); + $this->applyEditables($payload->editables, $payload->appendEditables, $document); + $this->applyProperties($payload->properties, $document); + $this->applyScheduler($payload->scheduler, $document); + } + + public function applyLinkPayload(LinkPayload $payload, Link $document): void + { + $this->applyLinkData($payload->data, $document); + $this->applyProperties($payload->properties, $document); + $this->applyScheduler($payload->scheduler, $document); + } + + public function applyHardlinkPayload(HardlinkPayload $payload, Hardlink $document): void + { + $this->applyHardlinkData($payload->data, $document); + $this->applyProperties($payload->properties, $document); + $this->applyScheduler($payload->scheduler, $document); + } + + public function applyFolderPayload(FolderPayload $payload, Folder $document): void + { + $this->applyProperties($payload->properties, $document); + } + + private function applySettings(?array $settings, Document $document): void + { + if ($settings === null || !$document->isAllowed('settings')) { + return; + } + + if (array_key_exists('prettyUrl', $settings)) { + $settings['prettyUrl'] = htmlspecialchars($settings['prettyUrl']); + } + + $document->setValues($settings); + } + + private function applyEditables(?array $editables, bool $appendEditables, Document\PageSnippet $document): void + { + if ($editables === null) { + return; + } + + $isTargetSpecific = interface_exists(TargetingDocumentInterface::class) + && $document instanceof TargetingDocumentInterface + && $document->hasTargetGroupSpecificEditables(); + + if ($appendEditables || $isTargetSpecific) { + $document->getEditables(); + } else { + $document->setEditables(null); + } + + foreach ($editables as $name => $editableData) { + $document->setRawEditable($name, $editableData['type'], $editableData['data'] ?? null); + } + } + + private function applyProperties(?array $propertiesData, Document $document): void + { + if ($propertiesData === null) { + $document->getProperties(); + + return; + } + + $properties = []; + foreach ($document->getProperties() as $p) { + if ($p->isInherited()) { + $properties[$p->getName()] = $p; + } + } + + foreach ($propertiesData as $propertyName => $propertyData) { + $value = $propertyData['data']; + + try { + $property = new Property(); + $property->setType($propertyData['type']); + $property->setName($propertyName); + $property->setCtype('document'); + $property->setDataFromEditmode($value); + $property->setInheritable($propertyData['inheritable']); + + if ($propertyName === 'language') { + $property->setInherited($this->resolvePropertyInheritance($document, $propertyName, $value)); + } + + $properties[$propertyName] = $property; + } catch (Exception) { + Logger::warning("Can't add " . $propertyName . ' to document ' . $document->getRealFullPath()); + } + } + + if ($document->isAllowed('properties')) { + $document->setProperties($properties); + } + + $document->getProperties(); + } + + private function applyScheduler(?array $schedulerData, ElementInterface $element): void + { + if ($schedulerData === null || !$element->isAllowed('settings') || !method_exists($element, 'setScheduledTasks')) { + return; + } + + $userId = $this->userContext->getAdminUser()?->getId(); + $tasks = []; + + foreach ($schedulerData as $taskData) { + $taskData['userId'] = $userId; + $tasks[] = new Task($taskData); + } + + $element->setScheduledTasks($tasks); + } + + private function applyLinkData(?array $data, Link $document): void + { + if ($data === null) { + return; + } + + $path = $data['path']; + $target = null; + + if (!empty($path)) { + if ($data['linktype'] === 'internal' && $data['internalType']) { + $target = Element\Service::getElementByPath($data['internalType'], $path); + if ($target) { + $data['internal'] = $target->getId(); + } + } + + if (!$target) { + if ($target = Document::getByPath($path)) { + $data['internalType'] = 'document'; + $data['internal'] = $target->getId(); + } elseif ($target = Asset::getByPath($path)) { + $data['internalType'] = 'asset'; + $data['internal'] = $target->getId(); + } elseif ($target = Concrete::getByPath($path)) { + $data['internalType'] = 'object'; + $data['internal'] = $target->getId(); + } else { + $data['linktype'] = 'direct'; + $data['internalType'] = null; + $data['internal'] = null; + $data['direct'] = $path; + } + + if ($target) { + $data['linktype'] = 'internal'; + $data['direct'] = ''; + } + } + } else { + $data['linktype'] = 'internal'; + $data['direct'] = ''; + $data['internalType'] = null; + $data['internal'] = null; + } + + unset($data['path']); + $document->setValues($data); + } + + private function applyHardlinkData(?array $data, Hardlink $document): void + { + if ($data === null) { + return; + } + + $sourceId = null; + if ($sourceDocument = Document::getByPath($data['sourcePath'])) { + $sourceId = $sourceDocument->getId(); + } + + $document->setSourceId($sourceId); + $document->setValues($data); + } + + private function resolvePropertyInheritance(Document $document, string $propertyName, mixed $value): bool + { + if ($document->getParent()) { + return $value == $document->getParent()->getProperty($propertyName); + } + + return false; + } +} diff --git a/src/Service/Document/DocumentPersistenceCoordinator.php b/src/Service/Document/DocumentPersistenceCoordinator.php new file mode 100644 index 00000000..8c7075c6 --- /dev/null +++ b/src/Service/Document/DocumentPersistenceCoordinator.php @@ -0,0 +1,75 @@ +setModificationDate(time()); + $document->setUserModification($this->userContext->getAdminUser()->getId()); + + $version = null; + + if ($task === 'publish' && $document->isAllowed('publish')) { + $document->setPublished(true); + $document->save(); + } elseif ($task === 'unpublish' && $document->isAllowed('unpublish')) { + $document->setPublished(false); + $document->save(); + } elseif (in_array($task, ['save', 'version', 'autosave'], true) && $document->isAllowed('save')) { + if ($document instanceof Document\PageSnippet) { + if ($task === 'autosave' || $document->isPublished()) { + $version = $document->saveVersion(true, true, null, $task === 'autosave'); + } else { + $document->save(); + } + } + } elseif ($task === 'scheduler' && $document->isAllowed('settings')) { + if ($document instanceof Document\PageSnippet + || $document instanceof Document\Hardlink + || $document instanceof Document\Link) { + $document->saveScheduledTasks(); + } + } else { + throw new AccessDeniedHttpException(); + } + + if ($document instanceof Document\PageSnippet && in_array($task, ['publish', 'version'], true)) { + $document->deleteAutoSaveVersions(); + } + + return new DocumentSaveResult( + task: $task, + document: $document, + version: $version, + treeData: $this->elementService->getElementTreeNodeConfig($document), + ); + } +} diff --git a/src/Service/Document/DocumentSaveResult.php b/src/Service/Document/DocumentSaveResult.php new file mode 100644 index 00000000..a6e2b91f --- /dev/null +++ b/src/Service/Document/DocumentSaveResult.php @@ -0,0 +1,31 @@ +requestStack->getSession()->getId(); + + if (!Editlock::isLocked($id, $type, $sessionId)) { + Editlock::lock($id, $type, $sessionId); + + return; + } + + $lockData = ['task' => 'response']; + $eventArgs = ['data' => $lockData]; + if ($element !== null) { + $eventArgs['object'] = $element; + } + + $event = new GenericEvent(null, $eventArgs); + $this->eventDispatcher->dispatch($event, $eventName); + $task = $event->getArgument('data')['task']; + + if ($task === self::TASK_OVERWRITE) { + Editlock::lock($id, $type, $sessionId); + + return; + } + + $lock = Editlock::getByElement($id, $type); + throw new ElementLockedException($id, $type, $lock); + } +} diff --git a/src/Service/Element/SessionService.php b/src/Service/Element/SessionService.php new file mode 100644 index 00000000..90bbe394 --- /dev/null +++ b/src/Service/Element/SessionService.php @@ -0,0 +1,100 @@ +sessionId(); + DocumentService::saveElementToSession($doc, $sessionId); + if ($useForSave) { + DocumentService::saveElementToSession($doc, $sessionId, '_useForSave'); + } + } + + public function getDocument(Document $doc): ?Document + { + $sessionId = $this->sessionId(); + $sessionDoc = DocumentService::getElementFromSession('document', $doc->getId(), $sessionId); + if ($sessionDoc && DocumentService::getElementFromSession('document', $doc->getId(), $sessionId, '_useForSave')) { + DocumentService::removeElementFromSession('document', $doc->getId(), $sessionId, '_useForSave'); + } + + return $sessionDoc ?: null; + } + + public function getOrLoadDocument(int $id): ?Document\PageSnippet + { + $sessionId = $this->sessionId(); + $doc = DocumentService::getElementFromSession('document', $id, $sessionId); + if ($doc) { + return $doc; + } + + $doc = Document\PageSnippet::getById($id); + if (!$doc) { + return null; + } + + $latestVersion = $doc->getLatestVersion(); + if ($latestVersion && ($latestDoc = $latestVersion->loadData()) instanceof Document\PageSnippet) { + return $latestDoc; + } + + return $doc; + } + + public function removeDocument(int $docId): void + { + DocumentService::removeElementFromSession('document', $docId, $this->sessionId()); + } + + public function saveObject(DataObject\AbstractObject $obj, string $suffix = ''): void + { + DataObject\Service::saveElementToSession($obj, $this->sessionId(), $suffix); + } + + public function getObject(string $type, int $id): ?DataObject\AbstractObject + { + return DataObject\Service::getElementFromSession($type, $id, $this->sessionId()) ?: null; + } + + public function removeObject(string $type, int $id): void + { + DataObject\Service::removeElementFromSession($type, $id, $this->sessionId()); + } + + public function saveAsset(Asset $asset): void + { + Asset\Service::saveElementToSession($asset, $this->sessionId()); + } + + private function sessionId(): string + { + return $this->requestStack->getSession()->getId(); + } +} diff --git a/src/Service/Grid/AssetGridColumnConfigResolver.php b/src/Service/Grid/AssetGridColumnConfigResolver.php new file mode 100644 index 00000000..28fe0749 --- /dev/null +++ b/src/Service/Grid/AssetGridColumnConfigResolver.php @@ -0,0 +1,176 @@ +userContext->getAdminUser(); + $classId = $params['id']; + $context = ['purpose' => 'gridconfig']; + $types = !empty($params['types']) ? explode(',', $params['types']) : []; + $userId = $user?->getId() ?? 0; + $requestedGridConfigId = $isDelete ? null : ($params['gridConfigId'] ?? null); + $searchType = $params['searchType']; + + if ((string) ($requestedGridConfigId ?? '') === '' && $classId) { + $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId($userId, $classId, 0, $searchType); + if ($favourite) { + $requestedGridConfigId = $favourite->getGridConfigId(); + } + } + + $configData = $this->gridColumnConfigService->loadVerifiedGridConfig($requestedGridConfigId, $user, 'asset'); + $gridConfig = $configData->config; + + $availableFields = []; + if ($configData->isEmpty()) { + $availableFields = $this->getDefaultGridFields($params['noSystemColumns'], $context, $types); + } else { + foreach ($gridConfig['columns'] as $sc) { + if (!$sc['hidden']) { + $colConfig = $this->getFieldGridConfig($sc); + if ($colConfig) { + $availableFields[] = $colConfig; + } + } + } + } + usort($availableFields, static fn ($a, $b) => $a['position'] <=> $b['position']); + + $availableConfigs = $classId ? $this->gridColumnConfigService->getMyOwnColumnConfigs($userId, $classId, $searchType) : []; + $sharedConfigs = $classId ? $this->gridColumnConfigService->getSharedColumnConfigs($user, $classId, $searchType) : []; + $settings = $this->gridColumnConfigService->buildBaseSettings($configData); + + $gridContext = $gridConfig['context'] ?? null; + if ($gridContext) { + $gridContext = json_decode($gridContext, true); + } + + return new GridColumnConfigResult( + availableFields: $availableFields, + settings: $settings, + availableConfigs: $availableConfigs, + sharedConfigs: $sharedConfigs, + sortinfo: $gridConfig['sortinfo'] ?? false, + onlyDirectChildren: $gridConfig['onlyDirectChildren'] ?? false, + pageSize: $gridConfig['pageSize'] ?? false, + context: $gridContext, + onlyUnreferenced: $gridConfig['onlyUnreferenced'] ?? false, + ); + } + + private function getDefaultGridFields(bool $noSystemColumns, array $context, array $types = []): array + { + $count = 0; + $availableFields = []; + + if (!$noSystemColumns) { + foreach (Asset\Service::GRID_SYSTEM_COLUMNS as $sc) { + if ($types === []) { + $availableFields[] = [ + 'key' => $sc . '~system', + 'type' => 'system', + 'label' => $sc, + 'position' => $count, + ]; + $count++; + } + } + } + + return $availableFields; + } + + private function getFieldGridConfig(array $field, ?string $keyPrefix = null): ?array + { + $defaultMetadataFields = ['copyright', 'alt', 'title']; + $predefined = null; + + if (isset($field['fieldConfig']['layout']['name'])) { + $predefined = Metadata\Predefined::getByName($field['fieldConfig']['layout']['name']); + } + + $key = $field['name']; + if ($keyPrefix) { + $key = $keyPrefix . $key; + } + $fieldDef = explode('~', $field['name']); + $field['name'] = $fieldDef[0]; + + if (isset($fieldDef[1]) && $fieldDef[1] === 'system') { + $type = 'system'; + } elseif (in_array($fieldDef[0], $defaultMetadataFields)) { + $type = 'input'; + } else { + $type = $field['fieldConfig']['type']; + if (isset($fieldDef[1])) { + $field['fieldConfig']['label'] = $field['fieldConfig']['layout']['title'] = $fieldDef[0] . ' (' . $fieldDef[1] . ')'; + $field['fieldConfig']['layout']['icon'] = Tool::getLanguageFlagFile($fieldDef[1], true); + } + } + + $result = [ + 'key' => $key, + 'type' => $type, + 'label' => $field['fieldConfig']['label'] ?? $key, + 'width' => $field['width'], + 'position' => $field['position'], + 'language' => $field['fieldConfig']['language'] ?? null, + 'layout' => $field['fieldConfig']['layout'] ?? null, + ]; + + if (isset($field['locked'])) { + $result['locked'] = $field['locked']; + } + + if ($type === 'select' && $predefined) { + $field['fieldConfig']['layout']['config'] = $predefined->getConfig(); + $result['layout'] = $field['fieldConfig']['layout']; + } elseif (in_array($type, ['document', 'asset', 'object'], true)) { + $result['layout']['fieldtype'] = 'manyToOneRelation'; + $result['layout']['subtype'] = $type; + } + + $event = new GenericEvent(null, [ + 'field' => $field, + 'result' => $result, + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::ASSET_GET_FIELD_GRID_CONFIG); + + return $event->getArgument('result'); + } +} diff --git a/src/Service/Grid/DataObjectGridColumnConfigResolver.php b/src/Service/Grid/DataObjectGridColumnConfigResolver.php new file mode 100644 index 00000000..e730f25e --- /dev/null +++ b/src/Service/Grid/DataObjectGridColumnConfigResolver.php @@ -0,0 +1,459 @@ +userContext->getAdminUser(); + $class = null; + $fields = null; + + if ($params['id'] !== null) { + $class = DataObject\ClassDefinition::getById($params['id']); + } elseif ($params['name'] !== null) { + $class = DataObject\ClassDefinition::getByName($params['name']); + } + + $gridType = $params['gridtype'] ?? 'search'; + $objectId = $params['objectId'] !== null ? (int) $params['objectId'] : 0; + + if ($objectId) { + $fields = DataObject\Service::getCustomGridFieldDefinitions($class->getId(), $objectId); + } + + $context = ['purpose' => 'gridconfig']; + if ($class) { + $context['class'] = $class; + } + if ($objectId) { + $context['object'] = DataObject::getById($objectId); + } + + if (!$fields && $class) { + $fields = $class->getFieldDefinitions(); + } + + $types = $params['types'] !== null ? explode(',', $params['types']) : []; + $userId = $user?->getId() ?? 0; + $requestedGridConfigId = $isDelete ? null : $params['gridConfigId']; + $searchType = $params['searchType']; + + if ((string) ($requestedGridConfigId ?? '') === '' && $class) { + $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId($userId, $class->getId(), $objectId ?: 0, $searchType); + if (!$favourite && $objectId) { + $favourite = GridConfigFavourite::getByOwnerAndClassAndObjectId($userId, $class->getId(), 0, $searchType); + } + if ($favourite) { + $requestedGridConfigId = $favourite->getGridConfigId(); + } + } + + $configData = $this->gridColumnConfigService->loadVerifiedGridConfig($requestedGridConfigId, $user); + $gridConfig = $configData->config; + + $localizedFields = []; + if (is_array($fields)) { + foreach ($fields as $field) { + if ($field instanceof DataObject\ClassDefinition\Data\Localizedfields) { + $localizedFields[] = $field; + } + } + } + + $availableFields = []; + if ($configData->isEmpty()) { + $availableFields = $this->getDefaultGridFields( + $params['noSystemColumns'], + $class, + $gridType, + $params['noBrickColumns'], + $fields, + $context, + $objectId, + $types + ); + } else { + $savedColumns = $gridConfig['columns']; + foreach ($savedColumns as $key => $sc) { + if (!$sc['hidden']) { + if (in_array($key, self::SYSTEM_COLUMNS, true)) { + $colConfig = [ + 'key' => $key, + 'type' => 'system', + 'label' => $key, + 'position' => $sc['position'], + ]; + $colConfig = $this->injectCustomLayoutValues($colConfig, $sc); + $availableFields[] = $colConfig; + } else { + $keyParts = explode('~', $key); + + if (str_starts_with($key, '~')) { + $type = $keyParts[1]; + $groupAndKeyId = explode('-', $keyParts[3]); + $keyId = (int) $groupAndKeyId[1]; + + if ($type === 'classificationstore') { + $keyDef = DataObject\Classificationstore\KeyConfig::getById($keyId); + if ($keyDef) { + $keyFieldDef = json_decode($keyDef->getDefinition(), true); + if ($keyFieldDef) { + $keyFieldDef = DataObject\Classificationstore\Service::getFieldDefinitionFromJson($keyFieldDef, $keyDef->getType()); + $fieldConfig = $this->getFieldGridConfig($keyFieldDef, $gridType, (string) $sc['position'], true, null, $class, $objectId); + if ($fieldConfig) { + $fieldConfig['key'] = $key; + $fieldConfig['label'] = '#' . $keyFieldDef->getTitle(); + $fieldConfig = $this->injectCustomLayoutValues($fieldConfig, $sc); + $availableFields[] = $fieldConfig; + } + } + } + } + } elseif (count($keyParts) > 1) { + $brick = $keyParts[0]; + $brickDescriptor = null; + + if (str_contains($brick, '?')) { + $brickDescriptor = substr($brick, 1); + $brickDescriptor = json_decode($brickDescriptor, true); + $keyPrefix = $brick . '~'; + $brick = $brickDescriptor['containerKey']; + } else { + $keyPrefix = $brick . '~'; + } + + $fieldname = $keyParts[1]; + $brickClass = DataObject\Objectbrick\Definition::getByKey($brick); + + $fd = null; + if ($brickClass instanceof DataObject\Objectbrick\Definition) { + if ($brickDescriptor) { + $innerContainer = $brickDescriptor['innerContainer'] ?? 'localizedfields'; + /** @var DataObject\ClassDefinition\Data\Localizedfields $localizedField */ + $localizedField = $brickClass->getFieldDefinition($innerContainer); + $fd = $localizedField->getFieldDefinition($brickDescriptor['brickfield']); + } else { + $fd = $brickClass->getFieldDefinition($fieldname); + } + } + + if ($fd !== null) { + $fieldConfig = $this->getFieldGridConfig($fd, $gridType, (string) $sc['position'], true, $keyPrefix, $class, $objectId); + if (!empty($fieldConfig)) { + $fieldConfig = $this->injectCustomLayoutValues($fieldConfig, $sc); + $availableFields[] = $fieldConfig; + } + } + } elseif (DataObject\Service::isHelperGridColumnConfig($key)) { + $calculatedColumnConfig = $this->getCalculatedColumnConfig($request, $sc); + if ($calculatedColumnConfig) { + $availableFields[] = $calculatedColumnConfig; + } + } else { + $fd = $class->getFieldDefinition($key); + if (empty($fd)) { + foreach ($localizedFields as $lf) { + $fd = $lf->getFieldDefinition($key); + if (!empty($fd)) { + break; + } + } + } + + if (!empty($fd)) { + $fieldConfig = $this->getFieldGridConfig($fd, $gridType, (string) $sc['position'], true, null, $class, $objectId); + if (!empty($fieldConfig)) { + $fieldConfig = $this->injectCustomLayoutValues($fieldConfig, $sc); + $availableFields[] = $fieldConfig; + } + } + } + } + } + } + } + + usort($availableFields, static fn ($a, $b) => $a['position'] <=> $b['position']); + + $frontendLanguages = Tool\Admin::reorderWebsiteLanguages(Tool\Admin::getCurrentUser(), $this->config['general']['valid_languages']); + $language = $frontendLanguages ? $frontendLanguages[0] : $request->getLocale(); + if (!Tool::isValidLanguage($language)) { + $validLanguages = Tool::getValidLanguages(); + $language = $validLanguages[0]; + } + if (!empty($gridConfig) && !empty($gridConfig['language'])) { + $language = $gridConfig['language']; + } + + $availableConfigs = $class ? $this->gridColumnConfigService->getMyOwnColumnConfigs($userId, $class->getId(), $searchType) : []; + $sharedConfigs = $class ? $this->gridColumnConfigService->getSharedColumnConfigs($user, $class->getId(), $searchType) : []; + + $settings = $this->gridColumnConfigService->buildBaseSettings($configData); + $owner = null; + if ($configData->ownerId) { + $ownerObject = User::getById($configData->ownerId); + $owner = $ownerObject instanceof User ? $ownerObject->getName() : (string) $configData->ownerId; + } + $settings['owner'] = $owner; + $settings['modificationDate'] = $configData->modificationDate; + $settings['saveFilters'] = $configData->isEmpty() ? null : $configData->saveFilters; + $settings['allowVariants'] = $class && $class->getAllowVariants(); + + $gridContext = $gridConfig['context'] ?? null; + if ($gridContext) { + $gridContext = json_decode($gridContext, true); + } + + return new GridColumnConfigResult( + availableFields: $availableFields, + settings: $settings, + availableConfigs: $availableConfigs, + sharedConfigs: $sharedConfigs, + sortinfo: $gridConfig['sortinfo'] ?? false, + onlyDirectChildren: $gridConfig['onlyDirectChildren'] ?? false, + pageSize: $gridConfig['pageSize'] ?? false, + context: $gridContext, + language: $language, + searchFilter: $gridConfig['searchFilter'] ?? '', + filter: $gridConfig['filter'] ?? [], + ); + } + + /** + * @param DataObject\ClassDefinition\Data[]|null $fields + */ + private function getDefaultGridFields(bool $noSystemColumns, ?DataObject\ClassDefinition $class, string $gridType, bool $noBrickColumns, ?array $fields, array $context, int $objectId, array $types = []): array + { + $count = 0; + $availableFields = []; + + if (!$noSystemColumns && $class) { + $vis = $class->getPropertyVisibility(); + foreach (self::SYSTEM_COLUMNS as $sc) { + $key = $sc === 'fullpath' ? 'path' : $sc; + + if ($types === [] && (!empty($vis[$gridType][$key]) || $gridType === 'all')) { + $availableFields[] = [ + 'key' => $sc, + 'type' => 'system', + 'label' => $sc, + 'position' => $count, + ]; + $count++; + } + } + } + + $includeBricks = !$noBrickColumns; + + if (is_array($fields)) { + foreach ($fields as $field) { + if ($field instanceof DataObject\ClassDefinition\Data\Localizedfields) { + foreach ($field->getFieldDefinitions($context) as $fd) { + if ($types === [] || in_array($fd->getFieldType(), $types)) { + $fieldConfig = $this->getFieldGridConfig($fd, $gridType, (string) $count, false, null, $class, $objectId); + if (!empty($fieldConfig)) { + $availableFields[] = $fieldConfig; + $count++; + } + } + } + } elseif ($field instanceof DataObject\ClassDefinition\Data\Objectbricks && $includeBricks) { + if (in_array($field->getFieldType(), $types)) { + $fieldConfig = $this->getFieldGridConfig($field, $gridType, (string) $count, false, null, $class, $objectId); + if (!empty($fieldConfig)) { + $availableFields[] = $fieldConfig; + $count++; + } + } else { + $allowedTypes = $field->getAllowedTypes(); + foreach ($allowedTypes as $t) { + $brickClass = DataObject\Objectbrick\Definition::getByKey($t); + $brickFields = $brickClass->getFieldDefinitions($context); + $this->appendBrickFields($field, $brickFields, $availableFields, $gridType, $count, $t, $class, $objectId); + } + } + } elseif ($types === [] || in_array($field->getFieldType(), $types)) { + $fieldConfig = $this->getFieldGridConfig($field, $gridType, (string) $count, $types !== [], null, $class, $objectId); + if (!empty($fieldConfig)) { + $availableFields[] = $fieldConfig; + $count++; + } + } + } + } + + return $availableFields; + } + + /** + * @param DataObject\ClassDefinition\Data[] $brickFields + */ + private function appendBrickFields(DataObject\ClassDefinition\Data $field, array $brickFields, array &$availableFields, string $gridType, int &$count, string $brickType, DataObject\ClassDefinition $class, int $objectId, ?array $context = null): void + { + foreach ($brickFields as $bf) { + if ($bf instanceof DataObject\ClassDefinition\Data\Localizedfields) { + $localizedFieldDefinitions = $bf->getFieldDefinitions(); + $localizedContext = [ + 'containerKey' => $brickType, + 'fieldname' => $field->getName(), + ]; + $this->appendBrickFields($bf, $localizedFieldDefinitions, $availableFields, $gridType, $count, $brickType, $class, $objectId, $localizedContext); + } else { + if ($context) { + $context['brickfield'] = $bf->getName(); + $keyPrefix = '?' . json_encode($context) . '~'; + } else { + $keyPrefix = $brickType . '~'; + } + $fieldConfig = $this->getFieldGridConfig($bf, $gridType, (string) $count, false, $keyPrefix, $class, $objectId); + if (!empty($fieldConfig)) { + $availableFields[] = $fieldConfig; + $count++; + } + } + } + } + + private function injectCustomLayoutValues(array $fieldConfig, array $savedColumn): array + { + foreach (['width', 'locked'] as $key) { + if (isset($savedColumn[$key])) { + $fieldConfig[$key] = $savedColumn[$key]; + } + } + + if (isset($savedColumn['fieldConfig']['layout']['noteditable'])) { + $fieldConfig['layout']->setNoteditable($savedColumn['fieldConfig']['layout']['noteditable']); + } + + return $fieldConfig; + } + + private function getCalculatedColumnConfig(Request $request, array $config): mixed + { + try { + return Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($config) { + $existingKey = $config['fieldConfig']['key']; + $calculatedColumnConfig['key'] = $existingKey; + $calculatedColumnConfig['position'] = $config['position']; + $calculatedColumnConfig['isOperator'] = true; + $calculatedColumnConfig['attributes'] = $config['fieldConfig']['attributes']; + $calculatedColumnConfig['width'] = $config['width']; + $calculatedColumnConfig['locked'] = $config['locked']; + + $existingColumns = $session->get('helpercolumns', []); + + if (isset($existingColumns[$existingKey])) { + return $calculatedColumnConfig; + } + + $newKey = '#' . uniqid('', false); + $calculatedColumnConfig['key'] = $newKey; + + $phpConfig = json_encode($config['fieldConfig']); + $phpConfig = json_decode($phpConfig); + $helperColumns = [$newKey => $phpConfig, ...$existingColumns]; + $session->set('helpercolumns', $helperColumns); + + return $calculatedColumnConfig; + }, 'opendxp_gridconfig'); + } catch (Exception $e) { + Logger::error((string) $e); + } + + return null; + } + + private function getFieldGridConfig(DataObject\ClassDefinition\Data $field, string $gridType, string $position, bool $force = false, ?string $keyPrefix = null, ?DataObject\ClassDefinition $class = null, ?int $objectId = null): ?array + { + $key = $keyPrefix . $field->getName(); + $config = null; + $title = !empty($field->getTitle()) ? $field->getTitle() : $field->getName(); + + if ($field instanceof DataObject\ClassDefinition\Data\Slider) { + $config['minValue'] = $field->getMinValue(); + $config['maxValue'] = $field->getMaxValue(); + $config['increment'] = $field->getIncrement(); + } + + if (method_exists($field, 'getWidth')) { + $config['width'] = $field->getWidth(); + } + if (method_exists($field, 'getHeight')) { + $config['height'] = $field->getHeight(); + } + + $visible = match ($gridType) { + 'search' => $field->getVisibleSearch(), + 'grid' => $field->getVisibleGridView(), + default => true, + }; + + if (!$field->getInvisible() && ($force || $visible)) { + $context = ['purpose' => 'gridconfig']; + if ($class) { + $context['class'] = $class; + } + if ($objectId) { + $context['object'] = DataObject::getById($objectId); + } + DataObject\Service::enrichLayoutDefinition($field, null, $context); + + $result = [ + 'key' => $key, + 'type' => $field->getFieldType(), + 'label' => $title, + 'config' => $config, + 'layout' => $field, + 'position' => $position, + ]; + + if ($field instanceof DataObject\ClassDefinition\Data\EncryptedField) { + $result['delegateDatatype'] = $field->getDelegateDatatype(); + } + + return $result; + } + + return null; + } +} diff --git a/src/Service/Grid/Dto/GridColumnConfigResult.php b/src/Service/Grid/Dto/GridColumnConfigResult.php new file mode 100644 index 00000000..4db98b4a --- /dev/null +++ b/src/Service/Grid/Dto/GridColumnConfigResult.php @@ -0,0 +1,66 @@ + $this->sortinfo, + 'availableFields' => $this->availableFields, + 'settings' => $this->settings, + 'onlyDirectChildren' => $this->onlyDirectChildren, + 'pageSize' => $this->pageSize, + 'availableConfigs' => $this->availableConfigs, + 'sharedConfigs' => $this->sharedConfigs, + 'context' => $this->context, + ]; + + if ($this->onlyUnreferenced !== null) { + $data['onlyUnreferenced'] = $this->onlyUnreferenced; + } + if ($this->language !== null) { + $data['language'] = $this->language; + } + if ($this->searchFilter !== null) { + $data['searchFilter'] = $this->searchFilter; + } + if ($this->filter !== null) { + $data['filter'] = $this->filter; + } + + return $data; + } +} diff --git a/src/Service/Grid/Dto/GridConfigData.php b/src/Service/Grid/Dto/GridConfigData.php new file mode 100644 index 00000000..9c94a6fa --- /dev/null +++ b/src/Service/Grid/Dto/GridConfigData.php @@ -0,0 +1,38 @@ +id === 0; + } +} diff --git a/src/Service/Grid/GridBatchService.php b/src/Service/Grid/GridBatchService.php new file mode 100644 index 00000000..a2e01270 --- /dev/null +++ b/src/Service/Grid/GridBatchService.php @@ -0,0 +1,384 @@ +gridHelperService->prepareAssetListingForGrid($params, $adminUser); + + return $list->loadIdList(); + } + + /** + * @return int[] + */ + public function getObjectBatchJobIds(array $params, string $locale, User $adminUser): array + { + $list = $this->gridHelperService->prepareListingForGrid($params, $locale, $adminUser); + + return $list->loadIdList(); + } + + /** + * Executes a batch metadata update on a single asset. + * + * Returns true when the asset was saved or the update was handled by an event subscriber. + * Returns false when there is no asset to update (job already completed). + * + * @throws Exception on permission denied or save failure + */ + public function executeAssetBatch(array $data, User $adminUser): bool + { + $loader = OpenDxp::getContainer()->get('opendxp.implementation_loader.asset.metadata.data'); + + $updateEvent = new GenericEvent(null, [ + 'data' => $data, + 'processed' => false, + ]); + + $this->eventDispatcher->dispatch($updateEvent, AdminEvents::ASSET_LIST_BEFORE_BATCH_UPDATE); + + if ($updateEvent->getArgument('processed')) { + return true; + } + + $language = null; + if (isset($data['language'])) { + $language = $data['language'] !== 'default' ? $data['language'] : null; + } + + $asset = Asset::getById((int) $data['job']); + + if (!$asset) { + Logger::debug('GridBatchService::executeAssetBatch => There is no asset left to update.'); + + return false; + } + + if (!$asset->isAllowed('publish')) { + throw new Exception("Permission denied. You don't have the rights to save this asset."); + } + + $metadata = $asset->getMetadata(null, null, false, true); + $dirty = false; + + $name = $data['name']; + $value = $data['value']; + + if ($data['valueType'] === 'object') { + $value = json_decode($value); + } + + $fieldDef = explode('~', $name); + $name = $fieldDef[0]; + if (count($fieldDef) > 1) { + $language = ($fieldDef[1] === 'none' ? '' : $fieldDef[1]); + } + + foreach ($metadata as &$em) { + if ($em['name'] == $name && $em['language'] == $language) { + try { + $dataImpl = $loader->build($em['type']); + $value = $dataImpl->getDataFromListfolderGrid($value, $em); + } catch (UnsupportedException) { + Logger::error('could not resolve metadata implementation for ' . $em['type']); + } + $em['data'] = $value; + $dirty = true; + + break; + } + } + + if (!$dirty) { + $defaultMetadata = ['title', 'alt', 'copyright']; + if (in_array($name, $defaultMetadata)) { + $newEm = [ + 'name' => $name, + 'language' => $language, + 'type' => 'input', + 'data' => $value, + ]; + + try { + $dataImpl = $loader->build($newEm['type']); + $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm); + } catch (UnsupportedException) { + Logger::error('could not resolve metadata implementation for ' . $newEm['type']); + } + + $metadata[] = $newEm; + $dirty = true; + } else { + $predefined = Metadata\Predefined::getByName($name); + if ($predefined && (empty($predefined->getTargetSubtype()) + || $predefined->getTargetSubtype() === $asset->getType())) { + $newEm = [ + 'name' => $name, + 'language' => $language, + 'type' => $predefined->getType(), + 'data' => $value, + ]; + + try { + $dataImpl = $loader->build($newEm['type']); + $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm); + } catch (UnsupportedException) { + Logger::error('could not resolve metadata implementation for ' . $newEm['type']); + } + + $metadata[] = $newEm; + $dirty = true; + } + } + } + + if ($dirty) { + $metadataEvent = new GenericEvent(null, [ + 'id' => $asset->getId(), + 'metadata' => $metadata, + ]); + + $this->eventDispatcher->dispatch($metadataEvent, AdminEvents::ASSET_METADATA_PRE_SET); + + $asset->setMetadataRaw($metadata); + $asset->save(); + + return true; + } + + return false; + } + + /** + * Executes a batch field update on a single DataObject. + * + * Returns true when the object was saved. + * Returns false when there is no object to update (job already completed). + * + * @throws Exception on permission denied or save failure + */ + public function executeObjectBatch(array $params, string $locale, User $adminUser): bool + { + $object = DataObject\Concrete::getById($params['job']); + + if (!$object) { + Logger::debug('GridBatchService::executeObjectBatch => There is no object left to update.'); + + return false; + } + + $requestedLanguage = $params['language']; + if (!$requestedLanguage) { + $requestedLanguage = $locale; + } elseif ($requestedLanguage === 'default') { + $requestedLanguage = $locale; + } + + $name = $params['name']; + + if (!$object->isAllowed('save') || ($name === 'published' && !$object->isAllowed('publish'))) { + throw new Exception("Permission denied. You don't have the rights to save this object."); + } + + $append = $params['append'] ?? false; + $remove = $params['remove'] ?? false; + + $className = $object->getClassName(); + $class = DataObject\ClassDefinition::getByName($className); + $value = $params['value']; + if ($params['valueType'] === 'object') { + $value = json_decode($value, true); + } + + $parts = explode('~', $name); + + if (str_starts_with($name, '~')) { + $type = $parts[1]; + $field = $parts[2]; + $keyId = $parts[3]; + + if ($type === 'classificationstore') { + $groupKeyId = explode('-', $keyId); + $groupId = (int) $groupKeyId[0]; + $keyId = (int) $groupKeyId[1]; + + $getter = 'get' . ucfirst($field); + if (method_exists($object, $getter)) { + /** @var DataObject\ClassDefinition\Data\Classificationstore $csFieldDefinition */ + $csFieldDefinition = $object->getClass()->getFieldDefinition($field); + $csLanguage = $requestedLanguage; + if (!$csFieldDefinition->isLocalized()) { + $csLanguage = 'default'; + } + + /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ + $fd = $class->getFieldDefinition($field); + $keyConfig = $fd->getKeyConfiguration($keyId); + $dataDefinition = DataObject\Classificationstore\Service::getFieldDefinitionFromKeyConfig($keyConfig); + + /** @var DataObject\Classificationstore $classificationStoreData */ + $classificationStoreData = $object->$getter(); + if ($append) { + $oldValue = $classificationStoreData->getLocalizedKeyValue($groupId, $keyId); + $value = $dataDefinition->appendData($oldValue, $value); + } + if ($remove) { + $oldValue = $classificationStoreData->getLocalizedKeyValue($groupId, $keyId); + $value = $dataDefinition->removeData($oldValue, $value); + } + $classificationStoreData->setLocalizedKeyValue( + $groupId, + $keyId, + $dataDefinition->getDataFromEditmode($value), + $csLanguage + ); + $object->markFieldDirty($field); + } + } + } elseif (count($parts) > 1) { + // check for bricks + $brickType = $parts[0]; + + if (str_contains($brickType, '?')) { + $brickDescriptor = substr($brickType, 1); + $brickDescriptor = json_decode($brickDescriptor, true); + $brickType = $brickDescriptor['containerKey']; + } + $brickKey = $parts[1]; + $brickField = DataObject\Service::getFieldForBrickType($object->getClass(), $brickType); + + $fieldGetter = 'get' . ucfirst($brickField); + $brickGetter = 'get' . ucfirst($brickType); + $valueSetter = 'set' . ucfirst($brickKey); + + $brick = $object->$fieldGetter()->$brickGetter(); + if (empty($brick)) { + $classname = '\\OpenDxp\\Model\\DataObject\\Objectbrick\\Data\\' . ucfirst($brickType); + $brickSetter = 'set' . ucfirst($brickType); + $brick = new $classname($object); + $object->$fieldGetter()->$brickSetter($brick); + } + + $brickClass = DataObject\Objectbrick\Definition::getByKey($brickType); + $field = $brickClass->getFieldDefinition($brickKey); + + $newData = $field->getDataFromEditmode($value, $object); + + if ($append) { + $valueGetter = 'get' . ucfirst($brickKey); + $existingData = $brick->$valueGetter(); + $newData = $field->appendData($existingData, $newData); + } + if ($remove) { + $valueGetter = 'get' . ucfirst($brickKey); + $existingData = $brick->$valueGetter(); + $newData = $field->removeData($existingData, $newData); + } + + $localizedFields = $brickClass->getFieldDefinition('localizedfields'); + $isLocalizedField = false; + if ($localizedFields instanceof DataObject\ClassDefinition\Data\Localizedfields && $localizedFields->getFieldDefinition($brickKey)) { + $isLocalizedField = true; + } + + if ($isLocalizedField) { + $brick->$valueSetter($newData, $params['language']); + } else { + $brick->$valueSetter($newData); + } + } else { + // everything else + $field = $class->getFieldDefinition($name); + if ($field) { + $newData = $field->getDataFromEditmode($value, $object); + + if ($append) { + $existingData = $object->{'get' . $name}(); + $newData = $field->appendData($existingData, $newData); + } + if ($remove) { + $existingData = $object->{'get' . $name}(); + $newData = $field->removeData($existingData, $newData); + } + $object->setValue($name, $newData); + } else { + // check if it is a localized field + if ($params['language']) { + $localizedField = $class->getFieldDefinition('localizedfields'); + if ($localizedField instanceof DataObject\ClassDefinition\Data\Localizedfields) { + $field = $localizedField->getFieldDefinition($name); + if ($field) { + $getter = 'get' . $name; + $setter = 'set' . $name; + $newData = $field->getDataFromEditmode($value, $object); + if ($append) { + $existingData = $object->$getter($params['language']); + $newData = $field->appendData($existingData, $newData); + } + if ($remove) { + $existingData = $object->$getter($params['language']); + $newData = $field->removeData($existingData, $newData); + } + + $object->$setter($newData, $params['language']); + } + } + } + + // seems to be a system field, this is actually only possible for the "published" field yet + if ($name === 'published') { + if ($value === 'false' || empty($value)) { + $object->setPublished(false); + } else { + $object->setPublished(true); + } + } + } + } + + // don't check for mandatory fields here + $object->setOmitMandatoryCheck(!$object->isPublished()); + $object->setUserModification($adminUser->getId()); + $object->save(); + + return true; + } +} \ No newline at end of file diff --git a/src/Service/Grid/GridColumnConfigService.php b/src/Service/Grid/GridColumnConfigService.php new file mode 100644 index 00000000..1b09cec2 --- /dev/null +++ b/src/Service/Grid/GridColumnConfigService.php @@ -0,0 +1,302 @@ +quote($classId), + ]; + + if ($searchType) { + $conditionParts[] = 'searchType = ' . $db->quote($searchType); + } + + $listing = new GridConfig\Listing(); + $listing->setOrderKey('name'); + $listing->setOrder('ASC'); + $listing->setCondition(implode(' AND ', $conditionParts)); + $listing = $listing->load(); + + $data = []; + foreach ($listing as $config) { + $data[] = $config->getObjectVars(); + } + + return $data; + } + + public function getSharedColumnConfigs(?User $user, string $classId, ?string $searchType = null): array + { + if (!$user) { + return []; + } + + $db = Db::get(); + + $userIds = [$user->getId(), ...$user->getRoles()]; + + $ids = $db->fetchFirstColumn( + 'SELECT DISTINCT c1.id FROM gridconfigs c1, gridconfig_shares s + WHERE (c1.searchType = ? AND c1.id = s.gridConfigId AND s.sharedWithUserId IN (?) AND c1.classId = ?) + UNION DISTINCT SELECT c2.id FROM gridconfigs c2 WHERE shareGlobally = 1 AND c2.classId = ? AND c2.ownerId != ?', + [$searchType, $userIds, $classId, $classId, $user->getId()], + [ParameterType::STRING, ArrayParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER] + ); + + $data = []; + if ($ids) { + $listing = new GridConfig\Listing(); + $listing->setOrderKey('name'); + $listing->setOrder('ASC'); + $listing->setCondition('id in (' . implode(',', $ids) . ')'); + + foreach ($listing->load() as $config) { + $data[] = $config->getObjectVars(); + } + } + + return $data; + } + + public function getShareSettings(int $gridConfigId): array + { + $result = [ + 'sharedUserIds' => [], + 'sharedRoleIds' => [], + ]; + + $db = Db::get(); + $allShares = $db->fetchAllAssociative( + 'SELECT s.sharedWithUserId, u.type FROM gridconfig_shares s, users u + WHERE s.sharedWithUserId = u.id AND s.gridConfigId = ?', + [$gridConfigId] + ); + + foreach ($allShares as $share) { + $result['shared' . ucfirst($share['type']) . 'Ids'][] = $share['sharedWithUserId']; + } + + foreach ($result as $idx => $value) { + $result[$idx] = $value ? implode(',', $value) : ''; + } + + return $result; + } + + /** + * @throws Exception + */ + public function updateGridConfigShares(?GridConfig $gridConfig, array $metadata, ?User $user, bool $adminCanEditAll = false): void + { + if (!$gridConfig || !$user || !$user->isAllowed('share_configurations')) { + return; + } + + $ownerMismatch = $gridConfig->getOwnerId() !== $user->getId(); + if ($ownerMismatch && (!$adminCanEditAll || !$user->isAdmin())) { + throw new Exception("don't mess with someone elses grid config"); + } + + $combinedShares = []; + if ($metadata['sharedUserIds']) { + $combinedShares = explode(',', $metadata['sharedUserIds']); + } + if ($metadata['sharedRoleIds']) { + $combinedShares = [...$combinedShares, ...explode(',', $metadata['sharedRoleIds'])]; + } + + $db = Db::get(); + $db->delete('gridconfig_shares', ['gridConfigId' => $gridConfig->getId()]); + + foreach ($combinedShares as $id) { + $share = new GridConfigShare(); + $share->setGridConfigId($gridConfig->getId()); + $share->setSharedWithUserId((int) $id); + $share->save(); + } + } + + /** + * Loads a GridConfig by ID, verifies the user has access, and returns a populated DTO. + * Returns an empty GridConfigData when no valid config ID is given or the config is not found. + * + * @throws Exception when the user has neither ownership nor a share grant + */ + public function loadVerifiedGridConfig( + int|string|null $requestedConfigId, + ?User $user, + ?string $expectedType = null, + ): GridConfigData { + if (!is_numeric($requestedConfigId) || (int) $requestedConfigId <= 0) { + return new GridConfigData(); + } + + $savedGridConfig = GridConfig::getById((int) $requestedConfigId); + if (!$savedGridConfig) { + return new GridConfigData(); + } + if ($expectedType !== null && $savedGridConfig->getType() !== $expectedType) { + return new GridConfigData(); + } + + $isShared = false; + if (!$user) { + return new GridConfigData(); + } + if (!$user->isAdmin()) { + $userIds = [$user->getId(), ...$user->getRoles()]; + $isSharedGlobally = $savedGridConfig->getOwnerId() !== $user->getId() && $savedGridConfig->isShareGlobally(); + + $db = Db::get(); + $isSharedWithUser = (bool) $db->fetchOne( + 'SELECT 1 FROM gridconfig_shares WHERE sharedWithUserId IN (?) AND gridConfigId = ?', + [$userIds, $savedGridConfig->getId()], + [ArrayParameterType::INTEGER, ParameterType::INTEGER] + ); + + $isShared = $isSharedGlobally || $isSharedWithUser; + + if (!$isShared && $savedGridConfig->getOwnerId() !== $user->getId()) { + throw new Exception('You are neither the owner of this config nor it is shared with you'); + } + } + + $config = json_decode($savedGridConfig->getConfig(), true); + foreach ($config['columns'] as &$column) { + if (array_key_exists('isOperator', $column) && $column['isOperator']) { + $colAttributes = &$column['fieldConfig']['attributes']; + SecurityHelper::convertHtmlSpecialCharsArrayKeys($colAttributes, ['label', 'attribute', 'param1']); + } + } + + return new GridConfigData( + id: $savedGridConfig->getId(), + config: $config, + name: SecurityHelper::convertHtmlSpecialChars($savedGridConfig->getName()), + description: SecurityHelper::convertHtmlSpecialChars($savedGridConfig->getDescription()), + sharedGlobally: $savedGridConfig->isShareGlobally(), + setAsFavourite: $savedGridConfig->isSetAsFavourite(), + isShared: $isShared, + ownerId: $savedGridConfig->getOwnerId(), + modificationDate: $savedGridConfig->getModificationDate(), + saveFilters: $savedGridConfig->isSaveFilters(), + ); + } + + public function buildBaseSettings(GridConfigData $data): array + { + $settings = $this->getShareSettings($data->id); + $settings['gridConfigId'] = $data->id; + $settings['gridConfigName'] = $data->isEmpty() ? null : $data->name; + $settings['gridConfigDescription'] = $data->isEmpty() ? null : $data->description; + $settings['shareGlobally'] = $data->isEmpty() ? null : $data->sharedGlobally; + $settings['setAsFavourite'] = $data->isEmpty() ? null : $data->setAsFavourite; + $settings['isShared'] = $data->isEmpty() || $data->isShared; + + return $settings; + } + + /** + * @throws Exception + */ + public function updateGridConfigFavourites(GridConfig $gridConfig, array $metadata, ?User $user, int $objectId = 0): void + { + if (!$user || !$user->isAllowed('share_configurations')) { + return; + } + if (!$user->isAdmin() && $gridConfig->getOwnerId() !== $user->getId()) { + throw new Exception("don't mess with someone elses grid config"); + } + + $sharedUsers = []; + if ($metadata['shareGlobally'] === false && $metadata['sharedUserIds']) { + $sharedUsers = array_map(intval(...), explode(',', $metadata['sharedUserIds'])); + } elseif ($metadata['shareGlobally'] === true) { + $users = new User\Listing(); + $users->setCondition('id = ?', $user->getId()); + foreach ($users as $u) { + $sharedUsers[] = $u->getId(); + } + } + + foreach ($sharedUsers as $id) { + if (!$this->canOverwriteFavourite($id, $gridConfig, $objectId)) { + continue; + } + + $favourite = new GridConfigFavourite(); + $favourite->setGridConfigId($gridConfig->getId()); + $favourite->setClassId($gridConfig->getClassId()); + $favourite->setObjectId($objectId); + $favourite->setOwnerId($id); + $favourite->setType($gridConfig->getType()); + $favourite->setSearchType($gridConfig->getSearchType()); + $favourite->save(); + + if ($objectId !== 0 && $this->canOverwriteFavourite($id, $gridConfig, 0)) { + $favourite->setObjectId(0); + $favourite->save(); + } + } + } + + private function canOverwriteFavourite(int $userId, GridConfig $gridConfig, int $objectId): bool + { + $existing = GridConfigFavourite::getByOwnerAndClassAndObjectId( + $userId, + $gridConfig->getClassId(), + $objectId, + $gridConfig->getSearchType() + ); + + if (!($existing instanceof GridConfigFavourite)) { + return true; + } + + $existingConfig = GridConfig::getById($existing->getGridConfigId()); + if (!($existingConfig instanceof GridConfig)) { + return true; + } + + return $existingConfig->isShareGlobally() && $existingConfig->getOwnerId() !== $userId; + } + + public function encode(null|string|array $value): string + { + if (is_array($value)) { + $value = implode(',', $value); + } + + return '"' . str_replace('"', '""', $value ?? '') . '"'; + } +} diff --git a/src/Service/Grid/GridExportService.php b/src/Service/Grid/GridExportService.php new file mode 100644 index 00000000..551932ec --- /dev/null +++ b/src/Service/Grid/GridExportService.php @@ -0,0 +1,78 @@ +getCsvFile(File::getValidFilename($fileHandle)); + + try { + $csvData = $storage->read($csvFile); + $response = new Response($csvData); + $response->headers->set('Content-Type', 'application/csv'); + $response->headers->set( + 'Content-Disposition', + HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, 'export.csv') + ); + $storage->delete($csvFile); + + return $response; + } catch (FilesystemException | UnableToReadFile) { + throw new \RuntimeException('CSV file not found'); + } + } + + /** + * @throws FilesystemException + */ + public function downloadXlsxFile(string $fileHandle): BinaryFileResponse + { + $storage = Storage::get('temp'); + $csvFile = $this->getCsvFile(File::getValidFilename($fileHandle)); + + try { + return $this->gridHelperService->createXlsxExportFile($storage, File::getValidFilename($fileHandle), $csvFile); + } catch (Exception | FilesystemException | UnableToReadFile) { + throw new \RuntimeException('XLSX file not found'); + } + } +} diff --git a/src/Service/Login/LoginPageService.php b/src/Service/Login/LoginPageService.php new file mode 100644 index 00000000..3df84524 --- /dev/null +++ b/src/Service/Login/LoginPageService.php @@ -0,0 +1,110 @@ +base() + [ + 'csrfTokenRefreshInterval' => ($gcMaxlifetime - 60) * 1000, + 'browserSupported' => $this->detectBrowser(), + 'debug' => OpenDxp::inDebugMode(), + 'includeTemplates' => [], + 'deeplink' => $this->request->query->has('deeplink'), + 'error' => $this->resolveError($tooManyAttempts), + 'login_error' => $this->authenticationUtils->getLastAuthenticationError(), + ]; + + $event = new GenericEvent(null, [ + 'parameters' => $params, + 'config' => $this->config, + 'request' => $this->request, + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::LOGIN_BEFORE_RENDER); + + return $event->getArgument('parameters'); + } + + /** + * Base params shared across all login-area pages + * (lost password, 2FA, 2FA setup). + */ + public function base(): array + { + return [ + 'config' => $this->config, + 'adminSettings' => AdminConfig::get(), + 'pluginCssPaths' => $this->bundleManager->getCssPaths(), + ]; + } + + private function resolveError(?string $tooManyAttempts): ?string + { + if ($tooManyAttempts !== null) { + return SecurityHelper::convertHtmlSpecialChars($tooManyAttempts); + } + if ($this->request->query->has('auth_failed')) { + return 'error_auth_failed'; + } + if ($this->request->query->has('session_expired')) { + return 'error_session_expired'; + } + + return null; + } + + private function detectBrowser(): bool + { + $browser = new Browser(); + $version = (float) $browser->getVersion(); + + return match ($browser->getBrowser()) { + Browser::BROWSER_FIREFOX => $version >= 72, + Browser::BROWSER_CHROME => $version >= 84, + Browser::BROWSER_SAFARI => $version >= 13.1, + Browser::BROWSER_EDGE => $version >= 90, + default => false, + }; + } +} diff --git a/src/Service/Translation/AdminSearchTermResolver.php b/src/Service/Translation/AdminSearchTermResolver.php new file mode 100644 index 00000000..c87e6493 --- /dev/null +++ b/src/Service/Translation/AdminSearchTermResolver.php @@ -0,0 +1,46 @@ +setDomain(Translation::DOMAIN_ADMIN); + $translationListing->setCondition( + $translationListing->quoteIdentifier('language') . ' = ? AND ' . + $translationListing->quoteIdentifier('text') . ' LIKE ?', + [$user->getLanguage(), '%' . $searchTerm . '%'] + ); + foreach ($translationListing as $translation) { + $terms[] = $translation->getKey(); + } + } + + return $terms; + } +} diff --git a/src/Service/Workflow/WorkflowElementResolver.php b/src/Service/Workflow/WorkflowElementResolver.php new file mode 100644 index 00000000..c7e57642 --- /dev/null +++ b/src/Service/Workflow/WorkflowElementResolver.php @@ -0,0 +1,84 @@ + Document::getById($cid), + 'asset' => Asset::getById($cid), + 'object' => ConcreteObject::getById($cid), + default => null, + }; + + if ($element === null) { + throw new Exception('Cannot load element ' . $cid . ' of type \'' . $ctype . '\''); + } + + $element = $this->getLatestVersion($element); + $element->setUserModification((int) $this->userContext->getAdminUser()->getId()); + + return $element; + } + + private function getLatestVersion(ConcreteObject|Document|Asset $element): ConcreteObject|Document|Asset + { + if ( + $element instanceof Document\Folder + || $element instanceof Asset\Folder + || $element instanceof DataObject\Folder + || $element instanceof Document\Hardlink + || $element instanceof Document\Link + ) { + return $element; + } + + if ($element instanceof Document\PageSnippet) { + $latestVersion = $element->getLatestVersion(); + if ($latestVersion) { + $latestDoc = $latestVersion->loadData(); + if ($latestDoc instanceof Document\PageSnippet) { + $element = $latestDoc; + } + } + } + + if ($element instanceof ConcreteObject) { + $latestVersion = $element->getLatestVersion(); + if ($latestVersion) { + $latestObj = $latestVersion->loadData(); + if ($latestObj instanceof ConcreteObject) { + $element = $latestObj; + } + } + } + + return $element; + } +} diff --git a/tests/Model/Permissions/AbstractPermissionTest.php b/tests/Model/Permissions/AbstractPermissionTest.php index 2bbe04b8..7a99e63f 100644 --- a/tests/Model/Permissions/AbstractPermissionTest.php +++ b/tests/Model/Permissions/AbstractPermissionTest.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Tests\Model\Controller; use Codeception\Stub; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\ElementService; use OpenDxp\Config; use OpenDxp\Model\User; @@ -29,11 +30,12 @@ abstract class AbstractPermissionTest extends ModelTestCase { - protected function buildController(string $classname, User $user): mixed + protected function buildElementService(User $user): ElementService { $openDxpModule = $this->getModule('\\'.OpenDxp::class); $config = $openDxpModule->grabService(Config::class); - $elementService = Stub::construct( + + return Stub::construct( ElementService::class, [ Stub::makeEmpty(UrlGeneratorInterface::class), @@ -45,8 +47,22 @@ protected function buildController(string $classname, User $user): mixed ]), ] ); + } + + protected function buildUserContext(User $user): AdminUserContextInterface + { + return Stub::makeEmpty(AdminUserContextInterface::class, [ + 'getAdminUser' => function () use ($user) { + return $user; + }, + ]); + } + + protected function buildController(string $classname, User $user, array $extraConstructorArgs = []): mixed + { + $elementService = $this->buildElementService($user); - return Stub::construct($classname, [$elementService], [ + return Stub::construct($classname, [$elementService, ...$extraConstructorArgs], [ 'getAdminUser' => function () use ($user) { return $user; }, @@ -63,4 +79,4 @@ protected function buildController(string $classname, User $user): mixed } abstract public function testTreeGetChildrenById(): void; -} +} \ No newline at end of file diff --git a/tests/Model/Permissions/ModelAssetPermissionsTest.php b/tests/Model/Permissions/ModelAssetPermissionsTest.php index cb53fede..f6cac295 100644 --- a/tests/Model/Permissions/ModelAssetPermissionsTest.php +++ b/tests/Model/Permissions/ModelAssetPermissionsTest.php @@ -18,6 +18,8 @@ namespace OpenDxp\Bundle\AdminBundle\Tests\Model\Controller; use OpenDxp\Bundle\AdminBundle\Controller\Admin\Asset\AssetController; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetGridService; use OpenDxp\Model\Asset; use OpenDxp\Model\Property; use OpenDxp\Model\User; @@ -290,19 +292,21 @@ public function testTreeGetChildrenById(): void protected function doTestTreeGetChildrenById(Asset $element, User $user, array $expectedChildren): void { - $controller = $this->buildController(AssetController::class, $user); + $elementService = $this->buildElementService($user); + $userContext = $this->buildUserContext($user); + $handler = new GetAssetChildrenHandler($userContext, $elementService, new EventDispatcher()); + + $controller = $this->buildController(AssetController::class, $user, [ + (new \ReflectionClass(AssetGridService::class))->newInstanceWithoutConstructor(), + ]); $request = new Request([ 'node' => $element->getId(), 'limit' => 100, 'view' => 0, ]); - $eventDispatcher = new EventDispatcher(); - $responseData = $controller->treeGetChildrenByIdAction( - $request, - $eventDispatcher - ); + $responseData = $controller->treeGetChildrenByIdAction($handler, $request); $responsePaths = []; $responseData = json_decode($responseData->getContent(), true); foreach ($responseData['nodes'] as $node) { diff --git a/tests/Model/Permissions/ModelDataObjectPermissionsTest.php b/tests/Model/Permissions/ModelDataObjectPermissionsTest.php index 9cfd969a..4e82b4fb 100644 --- a/tests/Model/Permissions/ModelDataObjectPermissionsTest.php +++ b/tests/Model/Permissions/ModelDataObjectPermissionsTest.php @@ -19,6 +19,9 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject\DataObjectController; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenByIdHandler; +use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Model\DataObject; use OpenDxp\Model\User; use OpenDxp\Tests\Support\Util\TestHelper; @@ -216,18 +219,23 @@ protected function doTestTreeGetChildrenById( User $user, ?array $expectedChildren ): void { - $controller = $this->buildController(DataObjectController::class, $user); + $elementService = $this->buildElementService($user); + $userContext = $this->buildUserContext($user); + $childrenHandler = new GetDataObjectChildrenHandler($userContext, $elementService); + $handler = new TreeGetChildrenByIdHandler($userContext, $elementService, $childrenHandler, new EventDispatcher()); - $request = new Request([ - 'node' => $element->getId(), + $controller = $this->buildController(DataObjectController::class, $user, [ + (new \ReflectionClass(SessionService::class))->newInstanceWithoutConstructor(), ]); - $eventDispatcher = new EventDispatcher(); + + $request = new Request(['node' => $element->getId()]); try { TestHelper::callMethod($controller, 'checkPermission', ['objects']); $responseData = $controller->treeGetChildrenByIdAction( + $handler, $request, - $eventDispatcher + node: (int) $element->getId(), ); } catch (Exception $e) { if (is_null($expectedChildren)) { diff --git a/tests/Model/Permissions/ModelDocumentPermissionsTest.php b/tests/Model/Permissions/ModelDocumentPermissionsTest.php index c2d0f259..bf79baef 100644 --- a/tests/Model/Permissions/ModelDocumentPermissionsTest.php +++ b/tests/Model/Permissions/ModelDocumentPermissionsTest.php @@ -18,6 +18,8 @@ namespace OpenDxp\Bundle\AdminBundle\Tests\Model\Controller; use OpenDxp\Bundle\AdminBundle\Controller\Admin\Document\DocumentController; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildrenHandler; use OpenDxp\Model\Document; use OpenDxp\Model\Document\Page; use OpenDxp\Model\User; @@ -162,6 +164,11 @@ protected function prepareUsers(): void protected function doTestTreeGetChildrenById(Document $element, User $user, array $expectedChildren): void { + $elementService = $this->buildElementService($user); + $userContext = $this->buildUserContext($user); + $childrenHandler = new GetDocumentChildrenHandler($elementService); + $handler = new TreeGetDocumentChildrenHandler($userContext, $elementService, $childrenHandler, new EventDispatcher()); + $controller = $this->buildController(DocumentController::class, $user); $request = new Request([ @@ -169,12 +176,8 @@ protected function doTestTreeGetChildrenById(Document $element, User $user, arra 'limit' => 100, 'view' => 0, ]); - $eventDispatcher = new EventDispatcher(); - $responseData = $controller->treeGetChildrenByIdAction( - $request, - $eventDispatcher - ); + $responseData = $controller->treeGetChildrenByIdAction($handler, $request); $responsePaths = []; $responseData = json_decode($responseData->getContent(), true); From b6d40c9b99123b4d102033f91ac86e71a65ccebd Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Wed, 17 Jun 2026 09:25:13 +0200 Subject: [PATCH 02/10] part 1: remove admin bundle handler classes no longer in use (CQRS refactor cleanup) --- config/services.yaml | 10 +- src/Builder/AdminSettingsAssembler.php | 12 +- .../Admin/Asset/AssetController.php | 68 ++-- .../Admin/Asset/AssetCopyController.php | 7 +- .../Admin/Asset/AssetDownloadController.php | 47 +-- .../Admin/Asset/AssetEditorController.php | 13 +- .../Admin/Asset/AssetHelperController.php | 74 ++-- .../Admin/Asset/AssetMediaController.php | 29 +- .../Admin/Asset/AssetThumbnailController.php | 74 +--- .../Admin/Asset/AssetUploadController.php | 41 +- .../Admin/Asset/AssetVersionController.php | 13 +- .../Admin/DataObject/ClassController.php | 195 ++++------ .../ClassificationstoreController.php | 354 +++++------------- .../DataObject/CustomLayoutController.php | 59 ++- .../Admin/DataObject/DataObjectController.php | 226 +++++------ .../DataObject/DataObjectCopyController.php | 69 ++-- .../DataObject/DataObjectHelperController.php | 266 +++++-------- .../DataObjectVersionController.php | 23 +- .../DataObject/FieldCollectionController.php | 84 ++--- .../DataObject/ObjectBrickController.php | 77 ++-- .../DataObject/QuantityValueController.php | 77 ++-- .../Admin/DataObject/VariantsController.php | 24 +- .../Admin/Document/DocumentController.php | 230 ++++++------ .../Admin/Document/DocumentControllerBase.php | 69 ++-- .../Admin/Document/DocumentCopyController.php | 80 ++-- .../Document/DocumentVersionController.php | 36 +- .../Admin/Document/EmailController.php | 17 +- .../Admin/Document/FolderController.php | 18 +- .../Admin/Document/HardlinkController.php | 17 +- .../Admin/Document/LinkController.php | 17 +- .../Admin/Document/PageController.php | 84 ++--- .../Admin/Document/RenderletController.php | 9 +- .../Admin/Document/SnippetController.php | 18 +- src/Controller/Admin/ElementController.php | 36 +- src/Controller/Admin/EmailController.php | 66 +--- src/Controller/Admin/IndexController.php | 29 +- src/Controller/Admin/RecyclebinController.php | 34 +- src/Controller/Admin/SettingsController.php | 144 +++---- .../Admin/TranslationController.php | 92 ++--- .../SettingsHandler.php} | 11 +- .../Admin/Settings/SettingsPayload.php | 36 ++ .../Admin/{ => Settings}/SettingsResult.php | 2 +- .../StatisticsHandler.php} | 7 +- .../ClearAssetThumbnailPayload.php | 35 ++ .../Asset/ClearAssetThumbnailHandler.php | 7 +- .../Asset/Copy/CopyAsset/CopyAssetPayload.php | 30 ++ src/Handler/Asset/Copy/CopyAssetHandler.php | 17 +- .../GetAssetChildIdsPayload.php | 35 ++ .../Asset/Copy/GetAssetChildIdsHandler.php | 4 +- .../CreateAssetFolderPayload.php | 37 ++ .../Asset/CreateAssetFolderHandler.php | 6 +- .../Asset/DeleteAsset/DeleteAssetPayload.php | 39 ++ src/Handler/Asset/DeleteAssetHandler.php | 7 +- .../AddFilesToZip/AddFilesToZipPayload.php | 43 +++ .../Asset/Download/AddFilesToZipHandler.php | 16 +- .../DownloadAsset/DownloadAssetPayload.php | 35 ++ .../Asset/Download/DownloadAssetHandler.php | 4 +- .../DownloadImageThumbnailPayload.php | 57 +++ .../DownloadImageThumbnailHandler.php | 13 +- .../DownloadZip/DownloadZipPayload.php | 37 ++ .../Asset/Download/DownloadZipHandler.php | 5 +- .../GetDownloadZipJobsPayload.php | 37 ++ .../Download/GetDownloadZipJobsHandler.php | 5 +- .../LoadAssetForEditorPayload.php | 35 ++ .../Editor/LoadAssetForEditorHandler.php | 4 +- .../SaveImageEditorPayload.php | 37 ++ .../Asset/Editor/SaveImageEditorHandler.php | 5 +- .../GetAssetChildrenPayload.php | 51 +++ src/Handler/Asset/GetAssetChildrenHandler.php | 16 +- .../GetAssetData/GetAssetDataPayload.php | 37 ++ src/Handler/Asset/GetAssetDataHandler.php | 5 +- .../DeleteGridColumnConfigPayload.php | 35 ++ .../Helper/DeleteGridColumnConfigHandler.php | 4 +- .../DoAssetExport/DoAssetExportPayload.php | 51 +++ .../Asset/Helper/DoAssetExportHandler.php | 19 +- .../ExecuteAssetBatchPayload.php | 37 ++ .../Asset/Helper/ExecuteAssetBatchHandler.php | 4 +- .../GetAssetBatchJobsPayload.php | 39 ++ .../Asset/Helper/GetAssetBatchJobsHandler.php | 4 +- .../GetExportJobs/GetExportJobsPayload.php | 35 ++ .../Asset/Helper/GetExportJobsHandler.php | 4 +- .../MarkGridConfigFavouritePayload.php | 41 ++ .../Helper/MarkGridConfigFavouriteHandler.php | 14 +- .../SaveGridColumnConfigPayload.php | 50 +++ .../Helper/SaveGridColumnConfigHandler.php | 20 +- .../GetAssetText/GetAssetTextPayload.php | 39 ++ .../Asset/Media/GetAssetTextHandler.php | 5 +- .../GetDocumentPreviewPayload.php | 35 ++ .../Asset/Media/GetDocumentPreviewHandler.php | 4 +- .../GetVideoPreviewPayload.php | 37 ++ .../Asset/Media/GetVideoPreviewHandler.php | 5 +- .../ServeVideoPreviewPayload.php | 37 ++ .../Asset/Media/ServeVideoPreviewHandler.php | 5 +- .../{ => SaveAsset}/SaveAssetHandler.php | 7 +- .../Asset/SaveAsset/SaveAssetPayload.php} | 21 +- .../Asset/{ => SaveAsset}/SaveAssetResult.php | 2 +- .../GetDocumentThumbnailPayload.php | 43 +++ .../Thumbnail/GetDocumentThumbnailHandler.php | 15 +- .../GetFolderContentPreviewPayload.php | 35 ++ .../GetFolderContentPreviewHandler.php | 5 +- .../GetFolderThumbnailPayload.php | 35 ++ .../Thumbnail/GetFolderThumbnailHandler.php | 4 +- .../GetImageThumbnailPayload.php | 60 +++ .../Thumbnail/GetImageThumbnailHandler.php | 29 +- .../GetVideoThumbnailPayload.php | 53 +++ .../Thumbnail/GetVideoThumbnailHandler.php | 25 +- .../Asset/UpdateAsset/UpdateAssetPayload.php | 37 ++ src/Handler/Asset/UpdateAssetHandler.php | 6 +- .../CheckAssetExistsPayload.php | 39 ++ .../Asset/Upload/CheckAssetExistsHandler.php | 6 +- .../Upload/ImportZip/ImportZipPayload.php | 43 +++ .../ImportZipFiles/ImportZipFilesPayload.php | 45 +++ .../Asset/Upload/ImportZipFilesHandler.php | 17 +- src/Handler/Asset/Upload/ImportZipHandler.php | 6 +- .../ReplaceAsset/ReplaceAssetPayload.php | 43 +++ .../Asset/Upload/ReplaceAssetHandler.php | 6 +- .../PublishVersion/PublishVersionPayload.php | 35 ++ .../Asset/Version/PublishVersionHandler.php | 4 +- .../ShowVersion/ShowVersionPayload.php | 35 ++ .../Asset/Version/ShowVersionHandler.php | 4 +- .../DataObject/AddObjectFolderHandler.php | 5 +- src/Handler/DataObject/AddObjectHandler.php | 17 +- .../ChangeChildrenSortByHandler.php | 9 +- .../DataObject/ClassDef/AddClassHandler.php | 8 +- .../DataObject/ClassDef/AddClassPayload.php | 24 ++ .../DataObject/ClassDef/BulkCommitHandler.php | 3 +- .../DataObject/ClassDef/BulkCommitPayload.php | 22 ++ .../ClassDef/BulkExportPrepareHandler.php | 6 +- .../ClassDef/BulkExportPreparePayload.php | 22 ++ .../DataObject/ClassDef/BulkImportHandler.php | 6 +- .../DataObject/ClassDef/BulkImportPayload.php | 26 ++ .../ClassDef/DeleteClassHandler.php | 4 +- .../ClassDef/DeleteClassPayload.php | 22 ++ .../ClassDef/DeleteSelectOptionsHandler.php | 4 +- .../ClassDef/DeleteSelectOptionsPayload.php | 23 ++ .../ClassDef/ExportClassHandler.php | 6 +- .../ClassDef/ExportClassPayload.php | 22 ++ ...tClassDefinitionForColumnConfigHandler.php | 6 +- ...tClassDefinitionForColumnConfigPayload.php | 24 ++ .../DataObject/ClassDef/GetClassHandler.php | 4 +- .../ClassDef/GetClassIconsHandler.php | 5 +- .../ClassDef/GetClassIconsPayload.php | 24 ++ .../DataObject/ClassDef/GetClassPayload.php | 22 ++ .../ClassDef/GetClassTreeHandler.php | 13 +- .../ClassDef/GetClassTreePayload.php | 28 ++ .../ClassDef/GetSelectOptionsHandler.php | 4 +- .../ClassDef/GetSelectOptionsPayload.php | 23 ++ .../ClassDef/GetSelectOptionsTreeHandler.php | 3 +- .../ClassDef/GetSelectOptionsTreePayload.php | 22 ++ .../GetSelectOptionsUsagesHandler.php | 4 +- .../GetSelectOptionsUsagesPayload.php | 23 ++ .../ClassDef/GetTextLayoutPreviewHandler.php | 27 +- .../ClassDef/GetTextLayoutPreviewPayload.php | 30 ++ .../ClassDef/ImportClassHandler.php | 6 +- .../ClassDef/ImportClassPayload.php | 28 ++ .../ClassDef/SaveClassDefinitionHandler.php | 12 +- .../ClassDef/SaveClassDefinitionPayload.php | 26 ++ .../ClassDef/SaveSelectOptionsHandler.php | 22 +- .../ClassDef/SaveSelectOptionsPayload.php | 35 ++ .../AddCollectionsHandler.php | 5 +- .../AddCollectionsPayload.php | 26 ++ .../Classificationstore/AddGroupsHandler.php | 5 +- .../Classificationstore/AddGroupsPayload.php | 26 ++ .../AddPropertyHandler.php | 12 +- .../AddPropertyPayload.php | 24 ++ .../CreateCollectionHandler.php | 8 +- .../CreateCollectionPayload.php | 25 ++ .../CreateGroupHandler.php | 8 +- .../CreateGroupPayload.php | 25 ++ .../CreateStoreHandler.php | 6 +- .../CreateStorePayload.php | 23 ++ .../DeleteCollectionHandler.php | 6 +- .../DeleteCollectionPayload.php | 22 ++ .../DeleteCollectionRelationHandler.php | 6 +- .../DeleteCollectionRelationPayload.php | 24 ++ .../DeleteGroupHandler.php | 4 +- .../DeleteGroupPayload.php | 22 ++ .../DeletePropertyHandler.php | 4 +- .../DeletePropertyPayload.php | 22 ++ .../DeleteRelationHandler.php | 6 +- .../DeleteRelationPayload.php | 24 ++ .../Classificationstore/EditStoreHandler.php | 6 +- .../Classificationstore/EditStorePayload.php | 28 ++ .../GetCollectionRelationsHandler.php | 31 +- .../GetCollectionRelationsPayload.php | 36 ++ .../GetCollectionsHandler.php | 42 +-- .../GetCollectionsPayload.php | 42 +++ .../Classificationstore/GetGroupsHandler.php | 49 +-- .../Classificationstore/GetGroupsPayload.php | 44 +++ .../Classificationstore/GetPageHandler.php | 30 +- .../Classificationstore/GetPagePayload.php | 32 ++ .../GetPropertiesHandler.php | 53 ++- .../GetPropertiesPayload.php | 44 +++ .../GetRelationsHandler.php | 31 +- .../GetRelationsPayload.php | 38 ++ .../SaveCollectionRelationsHandler.php | 3 +- .../SaveCollectionRelationsPayload.php | 24 ++ .../SaveRelationHandler.php | 3 +- .../SaveRelationPayload.php | 24 ++ .../SearchRelationsHandler.php | 36 +- .../SearchRelationsPayload.php | 38 ++ .../UpdateCollectionHandler.php | 3 +- .../UpdateCollectionPayload.php | 24 ++ .../UpdateGroupHandler.php | 3 +- .../UpdateGroupPayload.php | 24 ++ .../UpdatePropertyHandler.php | 3 +- .../UpdatePropertyPayload.php | 24 ++ .../CopyDataObjectHandler.php | 44 +-- .../CopyDataObject/CopyDataObjectPayload.php | 55 +++ .../CopyDataObjectResult.php | 2 +- .../GetDataObjectChildIdsHandler.php | 10 +- .../GetDataObjectChildIdsPayload.php | 30 ++ .../GetDataObjectChildIdsResult.php} | 4 +- .../RewriteDataObjectIdsHandler.php | 10 +- .../RewriteDataObjectIdsPayload.php | 50 +++ .../AddCustomLayoutPayload.php | 38 ++ .../CustomLayout/AddCustomLayoutHandler.php | 9 +- .../DeleteCustomLayoutHandler.php | 4 +- .../ExportCustomLayoutPayload.php | 30 ++ .../ExportCustomLayoutHandler.php | 9 +- .../GetCustomLayoutPayload.php | 30 ++ .../GetCustomLayoutDefinitionsPayload.php | 30 ++ .../GetCustomLayoutDefinitionsHandler.php | 8 +- .../CustomLayout/GetCustomLayoutHandler.php | 15 +- .../ImportCustomLayoutPayload.php | 44 +++ .../ImportCustomLayoutHandler.php | 6 +- .../SaveCustomLayoutPayload.php | 38 ++ .../CustomLayout/SaveCustomLayoutHandler.php | 7 +- .../SuggestCustomLayoutIdentifierPayload.php | 30 ++ .../SuggestCustomLayoutIdentifierHandler.php | 7 +- .../DataObject/DataObjectGridProxyHandler.php | 6 +- .../DataObject/DeleteDataObjectHandler.php | 6 +- .../DeleteFieldCollectionHandler.php | 5 +- .../ExportFieldCollectionPayload.php | 30 ++ .../ExportFieldCollectionHandler.php | 7 +- .../GetFieldCollectionPayload.php | 30 ++ .../GetFieldCollectionHandler.php | 5 +- .../GetFieldCollectionListPayload.php | 42 +++ .../GetFieldCollectionListHandler.php | 20 +- .../GetFieldCollectionTreePayload.php | 42 +++ .../GetFieldCollectionTreeHandler.php | 20 +- .../GetFieldCollectionUsagesPayload.php | 30 ++ .../GetFieldCollectionUsagesHandler.php | 5 +- .../ImportFieldCollectionPayload.php | 40 ++ .../ImportFieldCollectionHandler.php | 9 +- .../UpdateFieldCollectionPayload.php | 44 +++ .../UpdateFieldCollectionHandler.php | 18 +- .../DataObject/GetDataObjectFolderHandler.php | 7 +- .../DataObject/GetDataObjectHandler.php | 5 +- .../GetDataObjectPreviewUrlPayload.php | 28 ++ .../GetDataObjectPreviewUrlHandler.php | 6 +- .../GetIdPathPagingInfoPayload.php | 37 ++ .../DataObject/GetIdPathPagingInfoHandler.php | 5 +- .../DataObject/GetSelectOptionsHandler.php | 15 +- .../Helper/ApplyGridConfigToAllHandler.php | 7 +- .../Helper/ApplyGridConfigToAllPayload.php | 39 ++ ...eleteDataObjectGridColumnConfigHandler.php | 4 +- ...eleteDataObjectGridColumnConfigPayload.php | 35 ++ .../DeleteGridColumnConfigHandler.php | 28 +- .../DeleteGridColumnConfigPayload.php | 55 +++ .../Helper/DoDataObjectExportHandler.php | 52 +-- .../Helper/DoDataObjectExportPayload.php | 66 ++++ .../DataObject/Helper/ExecuteBatchHandler.php | 4 +- .../DataObject/Helper/ExecuteBatchPayload.php | 41 ++ .../GetAvailableVisibleFieldsHandler.php | 3 +- .../GetAvailableVisibleFieldsPayload.php | 35 ++ .../DataObject/Helper/GetBatchJobsHandler.php | 4 +- .../DataObject/Helper/GetBatchJobsPayload.php | 37 ++ .../Helper/GetExportConfigsHandler.php | 6 +- .../Helper/GetExportConfigsPayload.php | 35 ++ .../Helper/GetExportJobsHandler.php | 5 +- .../Helper/GetExportJobsPayload.php | 37 ++ .../GetGridColumnConfigHandler.php | 28 +- .../GetGridColumnConfigPayload.php | 59 +++ .../DataObject/Helper/ImportUploadHandler.php | 6 +- .../DataObject/Helper/ImportUploadPayload.php | 41 ++ .../Helper/LoadObjectDataHandler.php | 8 +- .../Helper/LoadObjectDataPayload.php | 37 ++ ...rkDataObjectGridConfigFavouriteHandler.php | 33 +- ...rkDataObjectGridConfigFavouritePayload.php | 45 +++ .../PrepareHelperColumnConfigsHandler.php | 8 +- .../PrepareHelperColumnConfigsPayload.php | 43 +++ .../SaveDataObjectGridColumnConfigHandler.php | 28 +- .../SaveDataObjectGridColumnConfigPayload.php | 47 +++ .../ObjectBrick/DeleteObjectBrickHandler.php | 5 +- .../ExportObjectBrickPayload.php | 30 ++ .../ObjectBrick/ExportObjectBrickHandler.php | 7 +- .../GetBrickUsages/GetBrickUsagesPayload.php | 30 ++ .../ObjectBrick/GetBrickUsagesHandler.php | 5 +- .../GetObjectBrick/GetObjectBrickPayload.php | 30 ++ .../ObjectBrick/GetObjectBrickHandler.php | 5 +- .../GetObjectBrickListPayload.php | 40 ++ .../ObjectBrick/GetObjectBrickListHandler.php | 20 +- .../GetObjectBrickTreePayload.php | 42 +++ .../ObjectBrick/GetObjectBrickTreeHandler.php | 24 +- .../ImportObjectBrickPayload.php | 40 ++ .../ObjectBrick/ImportObjectBrickHandler.php | 9 +- .../UpdateObjectBrickPayload.php | 44 +++ .../ObjectBrick/UpdateObjectBrickHandler.php | 18 +- .../ConvertAllQuantityValuesPayload.php | 36 ++ .../ConvertAllQuantityValuesHandler.php | 9 +- .../ConvertQuantityValuePayload.php | 38 ++ .../ConvertQuantityValueHandler.php | 9 +- .../CreateQuantityValueUnitHandler.php | 51 +++ .../DeleteQuantityValueUnitHandler.php | 38 ++ .../GetQuantityValueUnitListPayload.php | 30 ++ .../GetQuantityValueUnitListHandler.php | 4 +- .../GetQuantityValueUnitsPayload.php | 40 ++ .../GetQuantityValueUnitsHandler.php | 13 +- .../ImportQuantityValueUnitsPayload.php | 34 ++ .../ImportQuantityValueUnitsHandler.php | 5 +- .../ManageQuantityValueUnitHandler.php | 72 ---- .../QuantityValueUnitPayload.php | 30 ++ .../UpdateQuantityValueUnitHandler.php | 45 +++ .../SaveDataObjectHandler.php | 7 +- .../SaveDataObject/SaveDataObjectPayload.php} | 17 +- .../SaveDataObjectResult.php | 2 +- .../SaveDataObjectFolderHandler.php | 6 +- .../DataObject/TreeGetChildrenByIdHandler.php | 20 +- .../DataObject/UpdateDataObjectHandler.php | 14 +- .../GetVariants/GetVariantsPayload.php | 44 +++ .../Variants/GetVariantsHandler.php | 7 +- .../UpdateObjectKeyPayload.php | 36 ++ .../Variants/UpdateObjectKeyHandler.php | 6 +- .../DiffVersionsHandler.php | 14 +- .../DiffVersions/DiffVersionsPayload.php | 36 ++ .../{ => DiffVersions}/DiffVersionsResult.php | 2 +- .../PreviewVersionHandler.php | 11 +- .../PreviewVersionResult.php | 2 +- .../PublishVersionHandler.php | 9 +- .../PublishVersionResult.php | 2 +- .../{ => AddDocument}/AddDocumentHandler.php | 44 +-- .../AddDocument/AddDocumentPayload.php | 38 ++ .../{ => AddDocument}/AddDocumentResult.php | 2 +- .../ChangeMainDocumentHandler.php | 10 +- .../ChangeMainDocumentPayload.php | 36 ++ .../ConvertDocumentHandler.php | 12 +- .../ConvertDocumentPayload.php | 36 ++ .../CopyDocumentHandler.php | 49 ++- .../Copy/CopyDocument/CopyDocumentPayload.php | 61 +++ .../{ => CopyDocument}/CopyDocumentResult.php | 4 +- .../GetDocumentChildIdsHandler.php | 10 +- .../GetDocumentChildIdsPayload.php | 30 ++ .../GetDocumentChildIdsResult.php} | 4 +- .../RewriteDocumentIdsHandler.php | 16 +- .../RewriteDocumentIdsPayload.php | 52 +++ .../DeleteDocumentHandler.php | 12 +- .../DeleteDocument/DeleteDocumentPayload.php | 38 ++ .../DeleteDocumentResult.php | 2 +- .../CreateDocType/CreateDocTypeHandler.php | 39 ++ .../CreateDocType/CreateDocTypeResult.php | 12 + .../DeleteDocType/DeleteDocTypeHandler.php | 36 ++ .../DeleteDocType/DeleteDocTypeResult.php | 12 + .../Document/DocTypes/DocTypePayload.php | 25 ++ .../GetDocTypesListHandler.php | 8 +- .../GetDocTypesListResult.php | 2 +- .../UpdateDocType/UpdateDocTypeHandler.php | 39 ++ .../UpdateDocType/UpdateDocTypeResult.php | 12 + .../GetEmailDataHandler.php | 7 +- .../{ => GetEmailData}/GetEmailDataResult.php | 2 +- .../{ => SaveEmail}/SaveEmailHandler.php | 8 +- .../SaveEmail/SaveEmailPayload.php} | 11 +- .../Email/{ => SaveEmail}/SaveEmailResult.php | 2 +- .../GetFolderDataHandler.php | 6 +- .../GetFolderData/GetFolderDataPayload.php | 35 ++ .../GetFolderDataResult.php | 2 +- .../{ => SaveFolder}/SaveFolderHandler.php | 8 +- .../Folder/SaveFolder/SaveFolderPayload.php} | 11 +- .../{ => SaveFolder}/SaveFolderResult.php | 2 +- .../GetDocTypesByTypeHandler.php | 6 +- .../GetDocTypesByTypePayload.php | 34 ++ .../GetDocTypesByTypeResult.php | 2 +- .../GetDocumentDataHandler.php | 8 +- .../GetDocumentDataPayload.php | 34 ++ .../GetDocumentDataResult.php | 2 +- .../GetDocumentIdForPathHandler.php | 6 +- .../GetDocumentIdForPathPayload.php | 34 ++ .../GetDocumentIdForPathResult.php | 2 +- .../GetHardlinkDataHandler.php | 7 +- .../GetHardlinkDataResult.php | 2 +- .../SaveHardlinkHandler.php | 8 +- .../SaveHardlink/SaveHardlinkPayload.php} | 11 +- .../{ => SaveHardlink}/SaveHardlinkResult.php | 2 +- .../{ => GetLinkData}/GetLinkDataHandler.php | 7 +- .../{ => GetLinkData}/GetLinkDataResult.php | 2 +- .../Link/{ => SaveLink}/SaveLinkHandler.php | 8 +- .../Link/SaveLink/SaveLinkPayload.php} | 11 +- .../Link/{ => SaveLink}/SaveLinkResult.php | 2 +- .../Document/ManageDocTypesHandler.php | 67 ---- .../CheckPrettyUrlHandler.php | 11 +- .../CheckPrettyUrl/CheckPrettyUrlPayload.php | 37 ++ .../CheckPrettyUrlResult.php | 2 +- .../GeneratePagePreviewsHandler.php | 5 +- .../GenerateQrCodeHandler.php | 9 +- .../GenerateQrCode/GenerateQrCodePayload.php | 37 ++ .../{ => GetPageData}/GetPageDataHandler.php | 7 +- .../Page/GetPageData/GetPageDataPayload.php | 35 ++ .../{ => GetPageData}/GetPageDataResult.php | 2 +- .../GetPagePreviewImagePathHandler.php | 7 +- .../GetPagePreviewImagePathPayload.php | 35 ++ .../Document/Page}/PagePayload.php | 7 +- .../RenderAreabrickIndexEditmodeHandler.php | 4 +- .../RenderAreabrickIndexEditmodePayload.php | 7 +- .../RenderAreabrickIndexEditmodeResult.php | 2 +- .../ResetEditablesSessionHandler.php | 7 +- .../ResetEditablesSessionPayload.php | 35 ++ .../Page/{ => SavePage}/SavePageHandler.php | 8 +- .../Page/SavePage/SavePagePayload.php} | 6 +- .../Page/{ => SavePage}/SavePageResult.php | 2 +- .../RemoveFromSessionHandler.php | 32 ++ .../RenderRenderletHandler.php | 4 +- .../RenderRenderletPayload.php | 7 +- .../RenderRenderletResult.php | 2 +- .../SaveToSession/SaveToSessionHandler.php | 59 +++ .../SaveToSession/SaveToSessionPayload.php | 55 +++ .../GetSiteCustomSettingsHandler.php | 6 +- .../GetSiteCustomSettingsPayload.php | 34 ++ .../GetSiteCustomSettingsResult.php | 2 +- .../{ => RemoveSite}/RemoveSiteHandler.php | 6 +- .../Site/RemoveSite/RemoveSitePayload.php | 34 ++ .../UpdateSite}/UpdateSiteHandler.php | 31 +- .../Site/UpdateSite/UpdateSitePayload.php | 58 +++ .../UpdateSite}/UpdateSiteResult.php | 2 +- .../GetSnippetDataHandler.php | 7 +- .../GetSnippetDataResult.php | 2 +- .../{ => SaveSnippet}/SaveSnippetHandler.php | 8 +- .../SaveSnippet/SaveSnippetPayload.php} | 11 +- .../{ => SaveSnippet}/SaveSnippetResult.php | 2 +- .../AddDocumentTranslationHandler.php | 8 +- .../AddDocumentTranslationPayload.php | 36 ++ .../CheckTranslationLanguageHandler.php | 6 +- .../CheckTranslationLanguagePayload.php | 34 ++ .../CheckTranslationLanguageResult.php | 2 +- .../DetermineTranslationParentHandler.php | 10 +- .../DetermineTranslationParentPayload.php | 36 ++ .../DetermineTranslationParentResult.php | 2 +- .../GetLanguageTreeHandler.php | 8 +- .../GetLanguageTreePayload.php | 36 ++ .../GetLanguageTreeResult.php | 2 +- .../GetLanguageTreeRootHandler.php | 6 +- .../GetLanguageTreeRootPayload.php | 34 ++ .../GetLanguageTreeRootResult.php | 2 +- .../RemoveDocumentTranslationHandler.php | 8 +- .../RemoveDocumentTranslationPayload.php | 36 ++ .../TreeGetDocumentChildrenHandler.php | 24 +- .../TreeGetDocumentChildrenPayload.php | 39 ++ .../TreeGetDocumentChildrenResult.php | 5 +- .../UpdateDocumentHandler.php | 6 +- .../UpdateDocument/UpdateDocumentPayload.php | 36 ++ .../UpdateDocumentResult.php | 2 +- .../DiffVersionsHandler.php | 14 +- .../DiffVersions/DiffVersionsPayload.php | 38 ++ .../{ => DiffVersions}/DiffVersionsResult.php | 4 +- .../PublishVersionHandler.php | 9 +- .../PublishVersionResult.php | 2 +- .../SaveVersionToSessionHandler.php | 9 +- src/Handler/Element/DeleteNoteHandler.php | 15 +- src/Handler/Element/GetNoteListHandler.php | 41 +- src/Handler/Element/NoteListPayload.php | 59 +++ src/Handler/Email/BlocklistPayload.php | 62 +++ .../Email/CreateBlocklistEntryHandler.php | 3 +- .../Email/DeleteBlocklistEntryHandler.php | 4 +- src/Handler/Email/GetBlocklistHandler.php | 23 +- .../Email/UpdateBlocklistEntryHandler.php | 6 +- .../DeleteRecyclebinItemHandler.php | 4 +- .../Recyclebin/ListRecyclebinHandler.php | 24 +- src/Handler/Recyclebin/RecyclebinPayload.php | 59 +++ .../CreatePredefinedMetadataHandler.php | 5 +- .../CreatePredefinedPropertyHandler.php | 5 +- .../Settings/CreateWebsiteSettingHandler.php | 5 +- .../DeletePredefinedMetadataHandler.php | 4 +- .../DeletePredefinedPropertyHandler.php | 4 +- .../Settings/DeleteWebsiteSettingHandler.php | 4 +- .../GetPredefinedMetadataListHandler.php | 8 +- .../GetPredefinedPropertiesListHandler.php | 8 +- .../GetWebsiteSettingsListHandler.php | 23 +- .../Settings/PredefinedMetadataPayload.php | 44 +++ .../Settings/PredefinedPropertyPayload.php | 44 +++ .../UpdatePredefinedMetadataHandler.php | 3 +- .../UpdatePredefinedPropertyHandler.php | 3 +- .../Settings/UpdateWebsiteSettingHandler.php | 3 +- .../Settings/WebsiteSettingPayload.php | 63 ++++ .../Translation/CreateTranslationHandler.php | 9 +- .../Translation/DeleteTranslationHandler.php | 4 +- .../Translation/GetTranslationsHandler.php | 28 +- .../Translation/TranslationPayload.php | 59 +++ .../Translation/UpdateTranslationHandler.php | 6 +- src/Http/ExtJsValueResolver.php | 35 ++ .../Document/DocumentMetaNormalizer.php | 14 +- src/Normalizer/Document/DraftNormalizer.php | 6 +- .../Document/PropertiesNormalizer.php | 14 +- .../Document/TranslationNormalizer.php | 14 +- .../Element/UserNamesNormalizer.php | 14 +- src/Payload/Common/EmptyPayload.php | 28 ++ src/Payload/Common/IdBodyPayload.php | 30 ++ src/Payload/Common/IdQueryPayload.php | 30 ++ src/Payload/Common/StringIdBodyPayload.php | 30 ++ .../DataObject/AddObjectFolderPayload.php | 37 ++ src/Payload/DataObject/AddObjectPayload.php | 45 +++ .../ChangeChildrenSortByPayload.php | 39 ++ .../DataObject/DataObjectGridProxyPayload.php | 45 +++ .../DataObject/DeleteDataObjectPayload.php | 39 ++ .../DataObject/GetDataObjectPayload.php | 36 ++ .../DataObject/GetSelectOptionsPayload.php | 40 ++ .../SaveDataObjectFolderPayload.php | 43 +++ .../DataObject/TreeGetChildrenByIdPayload.php | 46 +++ .../DataObject/UpdateDataObjectPayload.php | 39 ++ ...lPayload.php => ExtJsPayloadInterface.php} | 12 +- src/Service/Asset/AssetPayloadMapper.php | 4 +- .../Asset/AssetPersistenceCoordinator.php | 2 +- .../DataObject/DataObjectPayloadMapper.php | 4 +- .../DataObjectPersistenceCoordinator.php | 2 +- .../Document/DocumentPayloadMapper.php | 14 +- .../DataObjectGridColumnConfigResolver.php | 54 ++- 514 files changed, 9526 insertions(+), 3184 deletions(-) rename src/Handler/Admin/{IndexActionHandler.php => Settings/SettingsHandler.php} (96%) create mode 100644 src/Handler/Admin/Settings/SettingsPayload.php rename src/Handler/Admin/{ => Settings}/SettingsResult.php (90%) rename src/Handler/Admin/{StatisticsActionHandler.php => Statistics/StatisticsHandler.php} (87%) create mode 100644 src/Handler/Asset/ClearAssetThumbnail/ClearAssetThumbnailPayload.php create mode 100644 src/Handler/Asset/Copy/CopyAsset/CopyAssetPayload.php create mode 100644 src/Handler/Asset/Copy/GetAssetChildIds/GetAssetChildIdsPayload.php create mode 100644 src/Handler/Asset/CreateAssetFolder/CreateAssetFolderPayload.php create mode 100644 src/Handler/Asset/DeleteAsset/DeleteAssetPayload.php create mode 100644 src/Handler/Asset/Download/AddFilesToZip/AddFilesToZipPayload.php create mode 100644 src/Handler/Asset/Download/DownloadAsset/DownloadAssetPayload.php create mode 100644 src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailPayload.php create mode 100644 src/Handler/Asset/Download/DownloadZip/DownloadZipPayload.php create mode 100644 src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsPayload.php create mode 100644 src/Handler/Asset/Editor/LoadAssetForEditor/LoadAssetForEditorPayload.php create mode 100644 src/Handler/Asset/Editor/SaveImageEditor/SaveImageEditorPayload.php create mode 100644 src/Handler/Asset/GetAssetChildren/GetAssetChildrenPayload.php create mode 100644 src/Handler/Asset/GetAssetData/GetAssetDataPayload.php create mode 100644 src/Handler/Asset/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigPayload.php create mode 100644 src/Handler/Asset/Helper/DoAssetExport/DoAssetExportPayload.php create mode 100644 src/Handler/Asset/Helper/ExecuteAssetBatch/ExecuteAssetBatchPayload.php create mode 100644 src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsPayload.php create mode 100644 src/Handler/Asset/Helper/GetExportJobs/GetExportJobsPayload.php create mode 100644 src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouritePayload.php create mode 100644 src/Handler/Asset/Helper/SaveGridColumnConfig/SaveGridColumnConfigPayload.php create mode 100644 src/Handler/Asset/Media/GetAssetText/GetAssetTextPayload.php create mode 100644 src/Handler/Asset/Media/GetDocumentPreview/GetDocumentPreviewPayload.php create mode 100644 src/Handler/Asset/Media/GetVideoPreview/GetVideoPreviewPayload.php create mode 100644 src/Handler/Asset/Media/ServeVideoPreview/ServeVideoPreviewPayload.php rename src/Handler/Asset/{ => SaveAsset}/SaveAssetHandler.php (81%) rename src/{Payload/Asset/AssetPayload.php => Handler/Asset/SaveAsset/SaveAssetPayload.php} (69%) rename src/Handler/Asset/{ => SaveAsset}/SaveAssetResult.php (91%) create mode 100644 src/Handler/Asset/Thumbnail/GetDocumentThumbnail/GetDocumentThumbnailPayload.php create mode 100644 src/Handler/Asset/Thumbnail/GetFolderContentPreview/GetFolderContentPreviewPayload.php create mode 100644 src/Handler/Asset/Thumbnail/GetFolderThumbnail/GetFolderThumbnailPayload.php create mode 100644 src/Handler/Asset/Thumbnail/GetImageThumbnail/GetImageThumbnailPayload.php create mode 100644 src/Handler/Asset/Thumbnail/GetVideoThumbnail/GetVideoThumbnailPayload.php create mode 100644 src/Handler/Asset/UpdateAsset/UpdateAssetPayload.php create mode 100644 src/Handler/Asset/Upload/CheckAssetExists/CheckAssetExistsPayload.php create mode 100644 src/Handler/Asset/Upload/ImportZip/ImportZipPayload.php create mode 100644 src/Handler/Asset/Upload/ImportZipFiles/ImportZipFilesPayload.php create mode 100644 src/Handler/Asset/Upload/ReplaceAsset/ReplaceAssetPayload.php create mode 100644 src/Handler/Asset/Version/PublishVersion/PublishVersionPayload.php create mode 100644 src/Handler/Asset/Version/ShowVersion/ShowVersionPayload.php create mode 100644 src/Handler/DataObject/ClassDef/AddClassPayload.php create mode 100644 src/Handler/DataObject/ClassDef/BulkCommitPayload.php create mode 100644 src/Handler/DataObject/ClassDef/BulkExportPreparePayload.php create mode 100644 src/Handler/DataObject/ClassDef/BulkImportPayload.php create mode 100644 src/Handler/DataObject/ClassDef/DeleteClassPayload.php create mode 100644 src/Handler/DataObject/ClassDef/DeleteSelectOptionsPayload.php create mode 100644 src/Handler/DataObject/ClassDef/ExportClassPayload.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigPayload.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassIconsPayload.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassPayload.php create mode 100644 src/Handler/DataObject/ClassDef/GetClassTreePayload.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsPayload.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsTreePayload.php create mode 100644 src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesPayload.php create mode 100644 src/Handler/DataObject/ClassDef/GetTextLayoutPreviewPayload.php create mode 100644 src/Handler/DataObject/ClassDef/ImportClassPayload.php create mode 100644 src/Handler/DataObject/ClassDef/SaveClassDefinitionPayload.php create mode 100644 src/Handler/DataObject/ClassDef/SaveSelectOptionsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/AddCollectionsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/AddGroupsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/AddPropertyPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateCollectionPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateGroupPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/CreateStorePayload.php create mode 100644 src/Handler/DataObject/Classificationstore/DeleteCollectionPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/DeleteCollectionRelationPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/DeleteGroupPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/DeletePropertyPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/DeleteRelationPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/EditStorePayload.php create mode 100644 src/Handler/DataObject/Classificationstore/GetCollectionRelationsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/GetCollectionsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/GetGroupsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/GetPagePayload.php create mode 100644 src/Handler/DataObject/Classificationstore/GetPropertiesPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/GetRelationsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/SaveCollectionRelationsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/SaveRelationPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/SearchRelationsPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdateCollectionPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdateGroupPayload.php create mode 100644 src/Handler/DataObject/Classificationstore/UpdatePropertyPayload.php rename src/Handler/DataObject/Copy/{ => CopyDataObject}/CopyDataObjectHandler.php (70%) create mode 100644 src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectPayload.php rename src/Handler/DataObject/Copy/{ => CopyDataObject}/CopyDataObjectResult.php (90%) rename src/Handler/DataObject/Copy/{ => GetDataObjectChildIds}/GetDataObjectChildIdsHandler.php (70%) create mode 100644 src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsPayload.php rename src/Handler/DataObject/Copy/{ChildIdsResult.php => GetDataObjectChildIds/GetDataObjectChildIdsResult.php} (82%) rename src/Handler/DataObject/Copy/{ => RewriteDataObjectIds}/RewriteDataObjectIdsHandler.php (77%) create mode 100644 src/Handler/DataObject/Copy/RewriteDataObjectIds/RewriteDataObjectIdsPayload.php create mode 100644 src/Handler/DataObject/CustomLayout/AddCustomLayout/AddCustomLayoutPayload.php create mode 100644 src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutPayload.php create mode 100644 src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutPayload.php create mode 100644 src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/GetCustomLayoutDefinitionsPayload.php create mode 100644 src/Handler/DataObject/CustomLayout/ImportCustomLayout/ImportCustomLayoutPayload.php create mode 100644 src/Handler/DataObject/CustomLayout/SaveCustomLayout/SaveCustomLayoutPayload.php create mode 100644 src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierPayload.php create mode 100644 src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionPayload.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionPayload.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollectionList/GetFieldCollectionListPayload.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/GetFieldCollectionTreePayload.php create mode 100644 src/Handler/DataObject/FieldCollection/GetFieldCollectionUsages/GetFieldCollectionUsagesPayload.php create mode 100644 src/Handler/DataObject/FieldCollection/ImportFieldCollection/ImportFieldCollectionPayload.php create mode 100644 src/Handler/DataObject/FieldCollection/UpdateFieldCollection/UpdateFieldCollectionPayload.php create mode 100644 src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlPayload.php create mode 100644 src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoPayload.php create mode 100644 src/Handler/DataObject/Helper/ApplyGridConfigToAllPayload.php create mode 100644 src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigPayload.php rename src/Handler/DataObject/Helper/{ => DeleteGridColumnConfig}/DeleteGridColumnConfigHandler.php (56%) create mode 100644 src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigPayload.php create mode 100644 src/Handler/DataObject/Helper/DoDataObjectExportPayload.php create mode 100644 src/Handler/DataObject/Helper/ExecuteBatchPayload.php create mode 100644 src/Handler/DataObject/Helper/GetAvailableVisibleFieldsPayload.php create mode 100644 src/Handler/DataObject/Helper/GetBatchJobsPayload.php create mode 100644 src/Handler/DataObject/Helper/GetExportConfigsPayload.php create mode 100644 src/Handler/DataObject/Helper/GetExportJobsPayload.php rename src/Handler/DataObject/Helper/{ => GetGridColumnConfig}/GetGridColumnConfigHandler.php (54%) create mode 100644 src/Handler/DataObject/Helper/GetGridColumnConfig/GetGridColumnConfigPayload.php create mode 100644 src/Handler/DataObject/Helper/ImportUploadPayload.php create mode 100644 src/Handler/DataObject/Helper/LoadObjectDataPayload.php create mode 100644 src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouritePayload.php create mode 100644 src/Handler/DataObject/Helper/PrepareHelperColumnConfigsPayload.php create mode 100644 src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigPayload.php create mode 100644 src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickPayload.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetBrickUsages/GetBrickUsagesPayload.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickPayload.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetObjectBrickList/GetObjectBrickListPayload.php create mode 100644 src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/GetObjectBrickTreePayload.php create mode 100644 src/Handler/DataObject/ObjectBrick/ImportObjectBrick/ImportObjectBrickPayload.php create mode 100644 src/Handler/DataObject/ObjectBrick/UpdateObjectBrick/UpdateObjectBrickPayload.php create mode 100644 src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesPayload.php create mode 100644 src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValuePayload.php create mode 100644 src/Handler/DataObject/QuantityValue/CreateQuantityValueUnit/CreateQuantityValueUnitHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/DeleteQuantityValueUnit/DeleteQuantityValueUnitHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListPayload.php create mode 100644 src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsPayload.php create mode 100644 src/Handler/DataObject/QuantityValue/ImportQuantityValueUnits/ImportQuantityValueUnitsPayload.php delete mode 100644 src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitHandler.php create mode 100644 src/Handler/DataObject/QuantityValue/QuantityValueUnitPayload.php create mode 100644 src/Handler/DataObject/QuantityValue/UpdateQuantityValueUnit/UpdateQuantityValueUnitHandler.php rename src/Handler/DataObject/{ => SaveDataObject}/SaveDataObjectHandler.php (92%) rename src/{Payload/DataObject/DataObjectPayload.php => Handler/DataObject/SaveDataObject/SaveDataObjectPayload.php} (71%) rename src/Handler/DataObject/{ => SaveDataObject}/SaveDataObjectResult.php (91%) create mode 100644 src/Handler/DataObject/Variants/GetVariants/GetVariantsPayload.php create mode 100644 src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyPayload.php rename src/Handler/DataObject/Version/{ => DiffVersions}/DiffVersionsHandler.php (81%) create mode 100644 src/Handler/DataObject/Version/DiffVersions/DiffVersionsPayload.php rename src/Handler/DataObject/Version/{ => DiffVersions}/DiffVersionsResult.php (91%) rename src/Handler/DataObject/Version/{ => PreviewVersion}/PreviewVersionHandler.php (79%) rename src/Handler/DataObject/Version/{ => PreviewVersion}/PreviewVersionResult.php (90%) rename src/Handler/DataObject/Version/{ => PublishVersion}/PublishVersionHandler.php (84%) rename src/Handler/DataObject/Version/{ => PublishVersion}/PublishVersionResult.php (88%) rename src/Handler/Document/{ => AddDocument}/AddDocumentHandler.php (81%) create mode 100644 src/Handler/Document/AddDocument/AddDocumentPayload.php rename src/Handler/Document/{ => AddDocument}/AddDocumentResult.php (89%) rename src/Handler/Document/{ => ChangeMainDocument}/ChangeMainDocumentHandler.php (72%) create mode 100644 src/Handler/Document/ChangeMainDocument/ChangeMainDocumentPayload.php rename src/Handler/Document/{ => ConvertDocument}/ConvertDocumentHandler.php (80%) create mode 100644 src/Handler/Document/ConvertDocument/ConvertDocumentPayload.php rename src/Handler/Document/Copy/{ => CopyDocument}/CopyDocumentHandler.php (66%) create mode 100644 src/Handler/Document/Copy/CopyDocument/CopyDocumentPayload.php rename src/Handler/Document/Copy/{ => CopyDocument}/CopyDocumentResult.php (86%) rename src/Handler/Document/Copy/{ => GetDocumentChildIds}/GetDocumentChildIdsHandler.php (69%) create mode 100644 src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsPayload.php rename src/Handler/Document/Copy/{ChildIdsResult.php => GetDocumentChildIds/GetDocumentChildIdsResult.php} (82%) rename src/Handler/Document/Copy/{ => RewriteDocumentIds}/RewriteDocumentIdsHandler.php (74%) create mode 100644 src/Handler/Document/Copy/RewriteDocumentIds/RewriteDocumentIdsPayload.php rename src/Handler/Document/{ => DeleteDocument}/DeleteDocumentHandler.php (84%) create mode 100644 src/Handler/Document/DeleteDocument/DeleteDocumentPayload.php rename src/Handler/Document/{ => DeleteDocument}/DeleteDocumentResult.php (88%) create mode 100644 src/Handler/Document/DocTypes/CreateDocType/CreateDocTypeHandler.php create mode 100644 src/Handler/Document/DocTypes/CreateDocType/CreateDocTypeResult.php create mode 100644 src/Handler/Document/DocTypes/DeleteDocType/DeleteDocTypeHandler.php create mode 100644 src/Handler/Document/DocTypes/DeleteDocType/DeleteDocTypeResult.php create mode 100644 src/Handler/Document/DocTypes/DocTypePayload.php rename src/Handler/Document/{ => DocTypes/GetDocTypesList}/GetDocTypesListHandler.php (85%) rename src/Handler/Document/{ => DocTypes/GetDocTypesList}/GetDocTypesListResult.php (89%) create mode 100644 src/Handler/Document/DocTypes/UpdateDocType/UpdateDocTypeHandler.php create mode 100644 src/Handler/Document/DocTypes/UpdateDocType/UpdateDocTypeResult.php rename src/Handler/Document/Email/{ => GetEmailData}/GetEmailDataHandler.php (89%) rename src/Handler/Document/Email/{ => GetEmailData}/GetEmailDataResult.php (91%) rename src/Handler/Document/Email/{ => SaveEmail}/SaveEmailHandler.php (88%) rename src/Handler/Document/{ManageDocTypesResult.php => Email/SaveEmail/SaveEmailPayload.php} (71%) rename src/Handler/Document/Email/{ => SaveEmail}/SaveEmailResult.php (91%) rename src/Handler/Document/Folder/{ => GetFolderData}/GetFolderDataHandler.php (85%) create mode 100644 src/Handler/Document/Folder/GetFolderData/GetFolderDataPayload.php rename src/Handler/Document/Folder/{ => GetFolderData}/GetFolderDataResult.php (90%) rename src/Handler/Document/Folder/{ => SaveFolder}/SaveFolderHandler.php (83%) rename src/{Payload/Document/FolderPayload.php => Handler/Document/Folder/SaveFolder/SaveFolderPayload.php} (68%) rename src/Handler/Document/Folder/{ => SaveFolder}/SaveFolderResult.php (90%) rename src/Handler/Document/{ => GetDocTypesByType}/GetDocTypesByTypeHandler.php (86%) create mode 100644 src/Handler/Document/GetDocTypesByType/GetDocTypesByTypePayload.php rename src/Handler/Document/{ => GetDocTypesByType}/GetDocTypesByTypeResult.php (89%) rename src/Handler/Document/{ => GetDocumentData}/GetDocumentDataHandler.php (88%) create mode 100644 src/Handler/Document/GetDocumentData/GetDocumentDataPayload.php rename src/Handler/Document/{ => GetDocumentData}/GetDocumentDataResult.php (88%) rename src/Handler/Document/{ => GetDocumentIdForPath}/GetDocumentIdForPathHandler.php (76%) create mode 100644 src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathPayload.php rename src/Handler/Document/{ => GetDocumentIdForPath}/GetDocumentIdForPathResult.php (89%) rename src/Handler/Document/Hardlink/{ => GetHardlinkData}/GetHardlinkDataHandler.php (88%) rename src/Handler/Document/Hardlink/{ => GetHardlinkData}/GetHardlinkDataResult.php (91%) rename src/Handler/Document/Hardlink/{ => SaveHardlink}/SaveHardlinkHandler.php (82%) rename src/{Payload/Document/HardlinkPayload.php => Handler/Document/Hardlink/SaveHardlink/SaveHardlinkPayload.php} (77%) rename src/Handler/Document/Hardlink/{ => SaveHardlink}/SaveHardlinkResult.php (90%) rename src/Handler/Document/Link/{ => GetLinkData}/GetLinkDataHandler.php (89%) rename src/Handler/Document/Link/{ => GetLinkData}/GetLinkDataResult.php (92%) rename src/Handler/Document/Link/{ => SaveLink}/SaveLinkHandler.php (84%) rename src/{Payload/Document/LinkPayload.php => Handler/Document/Link/SaveLink/SaveLinkPayload.php} (78%) rename src/Handler/Document/Link/{ => SaveLink}/SaveLinkResult.php (91%) delete mode 100644 src/Handler/Document/ManageDocTypesHandler.php rename src/Handler/Document/Page/{ => CheckPrettyUrl}/CheckPrettyUrlHandler.php (85%) create mode 100644 src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlPayload.php rename src/Handler/Document/Page/{ => CheckPrettyUrl}/CheckPrettyUrlResult.php (90%) rename src/Handler/Document/Page/{ => GeneratePagePreviews}/GeneratePagePreviewsHandler.php (85%) rename src/Handler/Document/Page/{ => GenerateQrCode}/GenerateQrCodeHandler.php (76%) create mode 100644 src/Handler/Document/Page/GenerateQrCode/GenerateQrCodePayload.php rename src/Handler/Document/Page/{ => GetPageData}/GetPageDataHandler.php (91%) create mode 100644 src/Handler/Document/Page/GetPageData/GetPageDataPayload.php rename src/Handler/Document/Page/{ => GetPageData}/GetPageDataResult.php (91%) rename src/Handler/Document/Page/{ => GetPagePreviewImagePath}/GetPagePreviewImagePathHandler.php (72%) create mode 100644 src/Handler/Document/Page/GetPagePreviewImagePath/GetPagePreviewImagePathPayload.php rename src/{Payload/Document => Handler/Document/Page}/PagePayload.php (88%) rename src/Handler/Document/Page/{ => RenderAreabrickIndexEditmode}/RenderAreabrickIndexEditmodeHandler.php (91%) rename src/{Payload/Document => Handler/Document/Page/RenderAreabrickIndexEditmode}/RenderAreabrickIndexEditmodePayload.php (85%) rename src/Handler/Document/Page/{ => RenderAreabrickIndexEditmode}/RenderAreabrickIndexEditmodeResult.php (89%) rename src/Handler/Document/Page/{ => ResetEditablesSession}/ResetEditablesSessionHandler.php (81%) create mode 100644 src/Handler/Document/Page/ResetEditablesSession/ResetEditablesSessionPayload.php rename src/Handler/Document/Page/{ => SavePage}/SavePageHandler.php (90%) rename src/{Payload/Document/SnippetPayload.php => Handler/Document/Page/SavePage/SavePagePayload.php} (72%) rename src/Handler/Document/Page/{ => SavePage}/SavePageResult.php (92%) create mode 100644 src/Handler/Document/RemoveFromSession/RemoveFromSessionHandler.php rename src/Handler/Document/Renderlet/{ => RenderRenderlet}/RenderRenderletHandler.php (94%) rename src/{Payload/Document => Handler/Document/Renderlet/RenderRenderlet}/RenderRenderletPayload.php (85%) rename src/Handler/Document/Renderlet/{ => RenderRenderlet}/RenderRenderletResult.php (88%) create mode 100644 src/Handler/Document/SaveToSession/SaveToSessionHandler.php create mode 100644 src/Handler/Document/SaveToSession/SaveToSessionPayload.php rename src/Handler/Document/Site/{ => GetSiteCustomSettings}/GetSiteCustomSettingsHandler.php (82%) create mode 100644 src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsPayload.php rename src/Handler/Document/Site/{ => GetSiteCustomSettings}/GetSiteCustomSettingsResult.php (88%) rename src/Handler/Document/Site/{ => RemoveSite}/RemoveSiteHandler.php (75%) create mode 100644 src/Handler/Document/Site/RemoveSite/RemoveSitePayload.php rename src/Handler/Document/{ => Site/UpdateSite}/UpdateSiteHandler.php (68%) create mode 100644 src/Handler/Document/Site/UpdateSite/UpdateSitePayload.php rename src/Handler/Document/{ => Site/UpdateSite}/UpdateSiteResult.php (89%) rename src/Handler/Document/Snippet/{ => GetSnippetData}/GetSnippetDataHandler.php (91%) rename src/Handler/Document/Snippet/{ => GetSnippetData}/GetSnippetDataResult.php (90%) rename src/Handler/Document/Snippet/{ => SaveSnippet}/SaveSnippetHandler.php (87%) rename src/Handler/Document/{DocumentResult.php => Snippet/SaveSnippet/SaveSnippetPayload.php} (71%) rename src/Handler/Document/Snippet/{ => SaveSnippet}/SaveSnippetResult.php (91%) rename src/Handler/Document/Translation/{ => AddDocumentTranslation}/AddDocumentTranslationHandler.php (87%) create mode 100644 src/Handler/Document/Translation/AddDocumentTranslation/AddDocumentTranslationPayload.php rename src/Handler/Document/Translation/{ => CheckTranslationLanguage}/CheckTranslationLanguageHandler.php (86%) create mode 100644 src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguagePayload.php rename src/Handler/Document/Translation/{ => CheckTranslationLanguage}/CheckTranslationLanguageResult.php (96%) rename src/Handler/Document/Translation/{ => DetermineTranslationParent}/DetermineTranslationParentHandler.php (80%) create mode 100644 src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentPayload.php rename src/Handler/Document/Translation/{ => DetermineTranslationParent}/DetermineTranslationParentResult.php (95%) rename src/Handler/Document/Translation/{ => GetLanguageTree}/GetLanguageTreeHandler.php (93%) create mode 100644 src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreePayload.php rename src/Handler/Document/Translation/{ => GetLanguageTree}/GetLanguageTreeResult.php (97%) rename src/Handler/Document/Translation/{ => GetLanguageTreeRoot}/GetLanguageTreeRootHandler.php (96%) create mode 100644 src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootPayload.php rename src/Handler/Document/Translation/{ => GetLanguageTreeRoot}/GetLanguageTreeRootResult.php (96%) rename src/Handler/Document/Translation/{ => RemoveDocumentTranslation}/RemoveDocumentTranslationHandler.php (80%) create mode 100644 src/Handler/Document/Translation/RemoveDocumentTranslation/RemoveDocumentTranslationPayload.php rename src/Handler/Document/{ => TreeGetDocumentChildren}/TreeGetDocumentChildrenHandler.php (83%) create mode 100644 src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenPayload.php rename src/Handler/Document/{ => TreeGetDocumentChildren}/TreeGetDocumentChildrenResult.php (74%) rename src/Handler/Document/{ => UpdateDocument}/UpdateDocumentHandler.php (97%) create mode 100644 src/Handler/Document/UpdateDocument/UpdateDocumentPayload.php rename src/Handler/Document/{ => UpdateDocument}/UpdateDocumentResult.php (88%) rename src/Handler/Document/Version/{ => DiffVersions}/DiffVersionsHandler.php (88%) create mode 100644 src/Handler/Document/Version/DiffVersions/DiffVersionsPayload.php rename src/Handler/Document/Version/{ => DiffVersions}/DiffVersionsResult.php (86%) rename src/Handler/Document/Version/{ => PublishVersion}/PublishVersionHandler.php (86%) rename src/Handler/Document/Version/{ => PublishVersion}/PublishVersionResult.php (88%) rename src/Handler/Document/Version/{ => SaveVersionToSession}/SaveVersionToSessionHandler.php (75%) create mode 100644 src/Handler/Element/NoteListPayload.php create mode 100644 src/Handler/Email/BlocklistPayload.php create mode 100644 src/Handler/Recyclebin/RecyclebinPayload.php create mode 100644 src/Handler/Settings/PredefinedMetadataPayload.php create mode 100644 src/Handler/Settings/PredefinedPropertyPayload.php create mode 100644 src/Handler/Settings/WebsiteSettingPayload.php create mode 100644 src/Handler/Translation/TranslationPayload.php create mode 100644 src/Http/ExtJsValueResolver.php create mode 100644 src/Payload/Common/EmptyPayload.php create mode 100644 src/Payload/Common/IdBodyPayload.php create mode 100644 src/Payload/Common/IdQueryPayload.php create mode 100644 src/Payload/Common/StringIdBodyPayload.php create mode 100644 src/Payload/DataObject/AddObjectFolderPayload.php create mode 100644 src/Payload/DataObject/AddObjectPayload.php create mode 100644 src/Payload/DataObject/ChangeChildrenSortByPayload.php create mode 100644 src/Payload/DataObject/DataObjectGridProxyPayload.php create mode 100644 src/Payload/DataObject/DeleteDataObjectPayload.php create mode 100644 src/Payload/DataObject/GetDataObjectPayload.php create mode 100644 src/Payload/DataObject/GetSelectOptionsPayload.php create mode 100644 src/Payload/DataObject/SaveDataObjectFolderPayload.php create mode 100644 src/Payload/DataObject/TreeGetChildrenByIdPayload.php create mode 100644 src/Payload/DataObject/UpdateDataObjectPayload.php rename src/Payload/{Document/EmailPayload.php => ExtJsPayloadInterface.php} (59%) diff --git a/config/services.yaml b/config/services.yaml index ed5071df..68c81ed4 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -25,7 +25,7 @@ services: OpenDxp\Maintenance\ExecutorInterface: alias: OpenDxp\Maintenance\Executor - OpenDxp\Bundle\AdminBundle\Handler\Admin\StatisticsActionHandler: + OpenDxp\Bundle\AdminBundle\Handler\Admin\Statistics\StatisticsHandler: arguments: $httpClient: '@opendxp.http_client' @@ -165,13 +165,19 @@ services: $customAdminRouteName: '%opendxp_admin.custom_admin_route_name%' $secret: '%secret%' + # + # HTTP / Value Resolvers + # + OpenDxp\Bundle\AdminBundle\Http\: + resource: '../src/Http' + # # Handlers # OpenDxp\Bundle\AdminBundle\Handler\: resource: '../src/Handler' - OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocumentHandler: + OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocument\AddDocumentHandler: arguments: $documentClassResolver: '@opendxp.class.resolver.document' $defaultDocumentController: '%opendxp.documents.default_controller%' diff --git a/src/Builder/AdminSettingsAssembler.php b/src/Builder/AdminSettingsAssembler.php index 668465e8..263833c0 100644 --- a/src/Builder/AdminSettingsAssembler.php +++ b/src/Builder/AdminSettingsAssembler.php @@ -41,7 +41,8 @@ use OpenDxp\Tool\MaintenanceModeHelperInterface; use OpenDxp\Version; use OpenDxp\Video; -use Symfony\Component\HttpFoundation\Request; +use OpenDxp\Bundle\AdminBundle\Handler\Admin\Settings\SettingsPayload; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\HttpKernel\KernelInterface; @@ -56,11 +57,12 @@ public function __construct( private readonly UrlGeneratorInterface $urlGenerator, private readonly Connection $db, private readonly KernelInterface $kernel, + private readonly RequestStack $requestStack, private readonly string $customAdminRouteName, private readonly string $secret, ) {} - public function createSettings(Request $request, User $user): AdminSettingsDto + public function createSettings(SettingsPayload $payload, User $user): AdminSettingsDto { $config = $this->config; $systemSettings = SystemSettingsConfig::get(); @@ -115,9 +117,9 @@ public function createSettings(Request $request, User $user): AdminSettingsDto devMode: OpenDxp::inDevMode(), disableMinifyJs: OpenDxp::disableMinifyJs(), environment: $this->kernel->getEnvironment(), - sessionId: htmlentities($request->getSession()->getId(), ENT_QUOTES, 'UTF-8'), + sessionId: htmlentities($payload->sessionId, ENT_QUOTES, 'UTF-8'), - language: $request->getLocale(), + language: $payload->locale, websiteLanguages: Admin::reorderWebsiteLanguages($user, $systemSettings['general']['valid_languages'], true), requiredLanguages: $requiredLanguages, @@ -183,7 +185,7 @@ classDefinitionWriteable: !isset($_SERVER['OPENDXP_CLASS_DEFINITION_WRITABLE']) checkNewNotificationEnabled: $notificationsEnabled && (bool) $config['notifications']['check_new_notification']['enabled'], checkNewNotificationInterval: $config['notifications']['check_new_notification']['interval'] * 1000, - csrfToken: $this->csrfProtection->getCsrfToken($request->getSession()), + csrfToken: $this->csrfProtection->getCsrfToken($this->requestStack->getSession()), ); } diff --git a/src/Controller/Admin/Asset/AssetController.php b/src/Controller/Admin/Asset/AssetController.php index 21975514..891541b4 100644 --- a/src/Controller/Admin/Asset/AssetController.php +++ b/src/Controller/Admin/Asset/AssetController.php @@ -20,15 +20,21 @@ use OpenDxp\Bundle\AdminBundle\Controller\Admin\ElementControllerBase; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnail\ClearAssetThumbnailPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolder\CreateAssetFolderPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAsset\DeleteAssetPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildren\GetAssetChildrenPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetData\GetAssetDataPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset\SaveAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\UpdateAsset\UpdateAssetPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\UpdateAssetHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Asset\AssetPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset\SaveAssetPayload; use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetGridService; @@ -106,13 +112,11 @@ public function deleteInfoAction( #[Route('/get-data-by-id', name: 'opendxp_admin_asset_getdatabyid', methods: ['GET'])] public function getDataByIdAction( + GetAssetDataPayload $payload, GetAssetDataHandler $getAssetData, - Request $request, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $type = null, ): JsonResponse { try { - $result = $getAssetData($id, $request->getSchemeAndHttpHost()); + $result = $getAssetData($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } @@ -122,32 +126,19 @@ public function getDataByIdAction( #[Route('/tree-get-children-by-id', name: 'opendxp_admin_asset_treegetchildrenbyid', methods: ['GET'])] public function treeGetChildrenByIdAction( + GetAssetChildrenPayload $payload, GetAssetChildrenHandler $getChildren, Request $request, - #[MapQueryParameter] ?string $filter = null, #[MapQueryParameter] int $inSearch = 0, ): JsonResponse { - $allParams = $request->query->all(); - $nodeId = (int) $allParams['node']; - - $limit = (int) ($allParams['limit'] ?? 0); - if ($filter !== null) { - $limit = 100; - } elseif (!$limit) { - $limit = 100000000; - } - - $offset = isset($allParams['start']) ? (int) $allParams['start'] : 0; - $customViewId = ($allParams['view'] ?? null) ?: null; + $result = $getChildren($payload); - $result = $getChildren($nodeId, $customViewId, $filter, $limit, $offset); - - if ($allParams['limit'] ?? false) { + if ($request->query->has('limit')) { return $this->adminJson([ 'offset' => $result->offset, 'limit' => $result->limit, 'total' => $result->totalChildCount, - 'overflow' => $filter !== null && ($result->filteredTotalCount > $result->limit), + 'overflow' => $payload->filter !== null && ($result->filteredTotalCount > $result->limit), 'nodes' => $result->assets, 'filter' => $result->filter ?: '', 'inSearch' => $inSearch, @@ -158,24 +149,17 @@ public function treeGetChildrenByIdAction( } #[Route('/add-folder', name: 'opendxp_admin_asset_addfolder', methods: ['POST'])] - public function addFolderAction(CreateAssetFolderHandler $createFolder, Request $request): JsonResponse + public function addFolderAction(CreateAssetFolderPayload $payload, CreateAssetFolderHandler $createFolder): JsonResponse { - $createFolder( - (int) $request->request->get('parentId'), - (string) $request->request->get('name'), - ); + $createFolder($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/delete', name: 'opendxp_admin_asset_delete', methods: ['DELETE'])] - public function deleteAction(DeleteAssetHandler $deleteAsset, Request $request): JsonResponse + public function deleteAction(DeleteAssetPayload $payload, DeleteAssetHandler $deleteAsset): JsonResponse { - $result = $deleteAsset( - (string) $request->request->get('type', ''), - (int) $request->request->get('id'), - (int) $request->request->get('amount', 0), - ); + $result = $deleteAsset($payload); if ($result->deleted) { return $this->adminJson(ApiResponse::ok(['deleted' => $result->deleted])); @@ -185,21 +169,17 @@ public function deleteAction(DeleteAssetHandler $deleteAsset, Request $request): } #[Route('/update', name: 'opendxp_admin_asset_update', methods: ['PUT'])] - public function updateAction(UpdateAssetHandler $updateAsset, Request $request): JsonResponse + public function updateAction(UpdateAssetPayload $payload, UpdateAssetHandler $updateAsset): JsonResponse { - $updateData = [...$request->request->all(), ...$request->query->all()]; - $result = $updateAsset((int) $request->request->get('id'), $updateData); + $result = $updateAsset($payload); return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } #[Route('/save', name: 'opendxp_admin_asset_save', methods: ['PUT', 'POST'])] - public function saveAction(SaveAssetHandler $saveAsset, Request $request): JsonResponse + public function saveAction(SaveAssetHandler $saveAsset, SaveAssetPayload $payload): JsonResponse { - $result = $saveAsset( - (int) $request->request->get('id'), - AssetPayload::fromRequest($request), - ); + $result = $saveAsset($payload); return $this->adminJson(ApiResponse::ok([ 'data' => [ @@ -211,9 +191,9 @@ public function saveAction(SaveAssetHandler $saveAsset, Request $request): JsonR } #[Route('/clear-thumbnail', name: 'opendxp_admin_asset_clearthumbnail', methods: ['POST'])] - public function clearThumbnailAction(ClearAssetThumbnailHandler $clearThumbnail, Request $request): JsonResponse + public function clearThumbnailAction(ClearAssetThumbnailPayload $payload, ClearAssetThumbnailHandler $clearThumbnail): JsonResponse { - $clearThumbnail((int) $request->request->get('id')); + $clearThumbnail($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/Asset/AssetCopyController.php b/src/Controller/Admin/Asset/AssetCopyController.php index 5eb9cd93..22b4f0c9 100644 --- a/src/Controller/Admin/Asset/AssetCopyController.php +++ b/src/Controller/Admin/Asset/AssetCopyController.php @@ -19,7 +19,9 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAsset\CopyAssetPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\GetAssetChildIds\GetAssetChildIdsPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\GetAssetChildIdsHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Tool; @@ -39,6 +41,7 @@ class AssetCopyController extends AdminAbstractController { #[Route('/copy-info', name: 'opendxp_admin_asset_copyinfo', methods: ['GET'])] public function copyInfoAction( + GetAssetChildIdsPayload $childIdsPayload, GetAssetChildIdsHandler $getChildIds, Request $request, #[MapQueryParameter] ?string $type = null, @@ -65,7 +68,7 @@ public function copyInfoAction( ], ]]; - $childIds = $getChildIds($sourceId)->ids; + $childIds = $getChildIds($childIdsPayload)->ids; foreach ($childIds as $id) { $pasteJobs[] = [[ 'url' => $this->generateUrl('opendxp_admin_asset_copy'), @@ -109,7 +112,7 @@ public function copyAction(CopyAssetHandler $copyAsset, Request $request): JsonR $targetParentId = $request->request->has('targetParentId') ? (int) $request->request->get('targetParentId') : null; $sessionParentId = $sessionBag['parentId'] ? (int) $sessionBag['parentId'] : null; - $result = $copyAsset($sourceId, $targetId, $type, $sourceParentId, $targetParentId, $sessionParentId); + $result = $copyAsset(new CopyAssetPayload($sourceId, $targetId, $type, $sourceParentId, $targetParentId, $sessionParentId)); if ($result->newAsset !== null && $request->request->get('saveParentId')) { $sessionBag['parentId'] = $result->newAsset->getId(); diff --git a/src/Controller/Admin/Asset/AssetDownloadController.php b/src/Controller/Admin/Asset/AssetDownloadController.php index 7012b306..712bc60e 100644 --- a/src/Controller/Admin/Asset/AssetDownloadController.php +++ b/src/Controller/Admin/Asset/AssetDownloadController.php @@ -21,10 +21,15 @@ use DateTime; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\AddFilesToZip\AddFilesToZipPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\AddFilesToZipHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAsset\DownloadAssetPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnail\DownloadImageThumbnailPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZip\DownloadZipPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZipHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobs\GetDownloadZipJobsPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobsHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\BinaryFileResponse; @@ -32,7 +37,6 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -44,9 +48,9 @@ class AssetDownloadController extends AdminAbstractController { #[Route('/download', name: 'opendxp_admin_asset_download', methods: ['GET'])] - public function downloadAction(DownloadAssetHandler $downloadAsset, #[MapQueryParameter] int $id): StreamedResponse + public function downloadAction(DownloadAssetPayload $payload, DownloadAssetHandler $downloadAsset): StreamedResponse { - $result = $downloadAsset($id); + $result = $downloadAsset($payload); $asset = $result->asset; $stream = $asset->getStream(); @@ -65,25 +69,10 @@ public function downloadAction(DownloadAssetHandler $downloadAsset, #[MapQueryPa #[Route('/download-image-thumbnail', name: 'opendxp_admin_asset_downloadimagethumbnail', methods: ['GET'])] public function downloadImageThumbnailAction( + DownloadImageThumbnailPayload $payload, DownloadImageThumbnailHandler $downloadImageThumbnail, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $thumbnail = null, - #[MapQueryParameter] ?string $config = null, - #[MapQueryParameter] ?string $type = null, ): BinaryFileResponse { - $configData = null; - if ($config !== null) { - $configData = $this->decodeJson($config); - } elseif ($type !== null) { - $predefined = [ - 'web' => ['resize_mode' => 'scaleByWidth', 'width' => 3500, 'dpi' => 72, 'format' => 'JPEG', 'quality' => 85], - 'print' => ['resize_mode' => 'scaleByWidth', 'width' => 6000, 'dpi' => 300, 'format' => 'JPEG', 'quality' => 95], - 'office' => ['resize_mode' => 'scaleByWidth', 'width' => 1190, 'dpi' => 144, 'format' => 'JPEG', 'quality' => 90], - ]; - $configData = $predefined[$type]; - } - - $result = $downloadImageThumbnail($id, $thumbnail, $config, $configData); + $result = $downloadImageThumbnail($payload); $downloadFilename = preg_replace( '/\.' . preg_quote(pathinfo($result->image->getFilename(), PATHINFO_EXTENSION), '/') . '$/i', @@ -104,25 +93,20 @@ public function downloadImageThumbnailAction( #[Route('/download-as-zip-jobs', name: 'opendxp_admin_asset_downloadaszipjobs', methods: ['GET'])] public function downloadAsZipJobsAction( + GetDownloadZipJobsPayload $payload, GetDownloadZipJobsHandler $getZipJobs, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] string $selectedIds = '', ): JsonResponse { - $result = $getZipJobs($id, $selectedIds); + $result = $getZipJobs($payload); return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs, 'jobId' => $result->jobId])); } #[Route('/download-as-zip-add-files', name: 'opendxp_admin_asset_downloadaszipaddfiles', methods: ['GET'])] public function downloadAsZipAddFilesAction( + AddFilesToZipPayload $payload, AddFilesToZipHandler $addFilesToZip, - #[MapQueryParameter] ?string $jobId = null, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $selectedIds = null, - #[MapQueryParameter] int $offset = 0, - #[MapQueryParameter] int $limit = 0, ): JsonResponse { - $addFilesToZip($id, $selectedIds, $offset, $limit, (string) $jobId); + $addFilesToZip($payload); return $this->adminJson(ApiResponse::ok()); } @@ -133,11 +117,10 @@ public function downloadAsZipAddFilesAction( */ #[Route('/download-as-zip', name: 'opendxp_admin_asset_downloadaszip', methods: ['GET'])] public function downloadAsZipAction( + DownloadZipPayload $payload, DownloadZipHandler $downloadZip, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $jobId = null, ): BinaryFileResponse { - $result = $downloadZip($id, (string) $jobId); + $result = $downloadZip($payload); $response = new BinaryFileResponse($result->zipFile); $response->headers->set('Content-Type', 'application/zip'); diff --git a/src/Controller/Admin/Asset/AssetEditorController.php b/src/Controller/Admin/Asset/AssetEditorController.php index ba28c934..0b25fe3f 100644 --- a/src/Controller/Admin/Asset/AssetEditorController.php +++ b/src/Controller/Admin/Asset/AssetEditorController.php @@ -19,13 +19,13 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\LoadAssetForEditor\LoadAssetForEditorPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\LoadAssetForEditorHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\SaveImageEditor\SaveImageEditorPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\SaveImageEditorHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -37,20 +37,19 @@ class AssetEditorController extends AdminAbstractController { #[Route('/image-editor', name: 'opendxp_admin_asset_imageeditor', methods: ['GET'])] - public function imageEditorAction(LoadAssetForEditorHandler $loadForEditor, #[MapQueryParameter] int $id): Response + public function imageEditorAction(LoadAssetForEditorPayload $payload, LoadAssetForEditorHandler $loadForEditor): Response { - $result = $loadForEditor($id); + $result = $loadForEditor($payload); return $this->render('@OpenDxpAdmin/admin/asset/image_editor.html.twig', ['asset' => $result->asset]); } #[Route('/image-editor-save', name: 'opendxp_admin_asset_imageeditorsave', methods: ['PUT'])] public function imageEditorSaveAction( - Request $request, + SaveImageEditorPayload $payload, SaveImageEditorHandler $saveImageEditor, - #[MapQueryParameter] int $id, ): JsonResponse { - $saveImageEditor($id, (string) $request->request->get('dataUri')); + $saveImageEditor($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/Asset/AssetHelperController.php b/src/Controller/Admin/Asset/AssetHelperController.php index e5a5459a..2e4667a2 100644 --- a/src/Controller/Admin/Asset/AssetHelperController.php +++ b/src/Controller/Admin/Asset/AssetHelperController.php @@ -18,17 +18,23 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DeleteGridColumnConfig\DeleteGridColumnConfigPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DeleteGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExport\DoAssetExportPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatch\ExecuteAssetBatchPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatchHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobs\GetAssetBatchJobsPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetMetadataForColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobs\GetExportJobsPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavourite\MarkGridConfigFavouritePayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavouriteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\SaveGridColumnConfig\SaveGridColumnConfigPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\SaveGridColumnConfigHandler; use OpenDxp\Bundle\AdminBundle\Service\Grid\AssetGridColumnConfigResolver; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; -use OpenDxp\File; use OpenDxp\Tool\Session; use stdClass; use Symfony\Component\HttpFoundation\BinaryFileResponse; @@ -52,6 +58,7 @@ public function __construct( #[Route('/grid-delete-column-config', name: 'opendxp_admin_asset_assethelper_griddeletecolumnconfig', methods: ['DELETE'])] public function gridDeleteColumnConfigAction( + DeleteGridColumnConfigPayload $deletePayload, DeleteGridColumnConfigHandler $deleteGridColumnConfig, Request $request, #[MapQueryParameter(name: 'no_system_columns')] bool $noSystemColumns = false, @@ -65,9 +72,7 @@ public function gridDeleteColumnConfigAction( 'noSystemColumns' => $noSystemColumns, ]; - $deleteGridColumnConfig( - (int) $request->request->get('gridConfigId'), - ); + $deleteGridColumnConfig($deletePayload); $resolverResult = $this->gridConfigResolver->resolve($params, true); @@ -126,36 +131,20 @@ public function prepareHelperColumnConfigs(Request $request): JsonResponse #[Route('/grid-mark-favourite-column-config', name: 'opendxp_admin_asset_assethelper_gridmarkfavouritecolumnconfig', methods: ['POST'])] public function gridMarkFavouriteColumnConfigAction( + MarkGridConfigFavouritePayload $payload, MarkGridConfigFavouriteHandler $markFavourite, - Request $request, ): JsonResponse { - $result = $markFavourite( - $request->request->get('classId'), - (int) $request->request->get('gridConfigId'), - $request->request->get('searchType'), - $request->request->get('type'), - ); + $result = $markFavourite($payload); return $this->adminJson(ApiResponse::ok(['specializedConfigs' => $result->specializedConfigs])); } #[Route('/grid-save-column-config', name: 'opendxp_admin_asset_assethelper_gridsavecolumnconfig', methods: ['POST'])] public function gridSaveColumnConfigAction( + SaveGridColumnConfigPayload $payload, SaveGridColumnConfigHandler $saveGridColumnConfig, - Request $request, ): JsonResponse { - $gridConfigData = $this->decodeJson($request->request->get('gridconfig')); - $metadata = json_decode($request->request->get('settings'), true); - - $result = $saveGridColumnConfig( - (int) $request->request->get('id'), - $request->request->get('class_id'), - $request->request->get('context'), - $request->request->get('searchType'), - $request->request->get('type'), - $gridConfigData, - $metadata, - ); + $result = $saveGridColumnConfig($payload); return $this->adminJson(ApiResponse::ok([ 'settings' => $result->settings, @@ -165,30 +154,17 @@ public function gridSaveColumnConfigAction( } #[Route('/get-export-jobs', name: 'opendxp_admin_asset_assethelper_getexportjobs', methods: ['POST'])] - public function getExportJobsAction(GetExportJobsHandler $getExportJobs, Request $request): JsonResponse + public function getExportJobsAction(GetExportJobsPayload $payload, GetExportJobsHandler $getExportJobs): JsonResponse { - $allParams = [...$request->request->all(), ...$request->query->all()]; - $result = $getExportJobs($allParams); + $result = $getExportJobs($payload); return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs, 'fileHandle' => $result->fileHandle])); } #[Route('/do-export', name: 'opendxp_admin_asset_assethelper_doexport', methods: ['POST'])] - public function doExportAction(DoAssetExportHandler $doExport, Request $request): JsonResponse + public function doExportAction(DoAssetExportPayload $payload, DoAssetExportHandler $doExport): JsonResponse { - $fileHandle = File::getValidFilename($request->request->get('fileHandle')); - $settings = json_decode($request->request->get('settings'), true); - $fields = json_decode($request->request->all('fields')[0], true); - - $doExport( - $fileHandle, - $request->request->all('ids'), - $settings['delimiter'] ?? ';', - str_replace('default', '', $request->request->get('language')), - $settings['header'] ?? 'title', - $fields, - (bool) $request->request->get('initial'), - ); + $doExport($payload); return $this->adminJson(ApiResponse::ok()); } @@ -226,24 +202,22 @@ public function getMetadataForColumnConfigAction(GetAssetMetadataForColumnConfig } #[Route('/get-batch-jobs', name: 'opendxp_admin_asset_assethelper_getbatchjobs', methods: ['POST'])] - public function getBatchJobsAction(GetAssetBatchJobsHandler $getAssetBatchJobs, Request $request): JsonResponse + public function getBatchJobsAction(GetAssetBatchJobsPayload $payload, GetAssetBatchJobsHandler $getAssetBatchJobs, Request $request): JsonResponse { - if ($request->request->get('language')) { - $request->setLocale($request->request->get('language')); + if ($payload->language) { + $request->setLocale($payload->language); } - $allParams = [...$request->request->all(), ...$request->query->all()]; - $result = $getAssetBatchJobs($allParams); + $result = $getAssetBatchJobs($payload); return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs])); } #[Route('/batch', name: 'opendxp_admin_asset_assethelper_batch', methods: ['PUT'])] - public function batchAction(ExecuteAssetBatchHandler $executeAssetBatch, Request $request): JsonResponse + public function batchAction(ExecuteAssetBatchPayload $payload, ExecuteAssetBatchHandler $executeAssetBatch): JsonResponse { - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data'), true); - $executeAssetBatch($data); + if ($payload->data !== null) { + $executeAssetBatch($payload); } return $this->adminJson(ApiResponse::ok()); diff --git a/src/Controller/Admin/Asset/AssetMediaController.php b/src/Controller/Admin/Asset/AssetMediaController.php index 6cb98232..291b6d26 100644 --- a/src/Controller/Admin/Asset/AssetMediaController.php +++ b/src/Controller/Admin/Asset/AssetMediaController.php @@ -21,17 +21,21 @@ use DateTime; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAsset\DownloadAssetPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetAssetText\GetAssetTextPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetAssetTextHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetDocumentPreview\GetDocumentPreviewPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetDocumentPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetVideoPreview\GetVideoPreviewPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetVideoPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\ServeVideoPreview\ServeVideoPreviewPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\ServeVideoPreviewHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Model\Asset\Enum\PdfScanStatus; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -43,9 +47,9 @@ class AssetMediaController extends AdminAbstractController { #[Route('/get-asset', name: 'opendxp_admin_asset_getasset', methods: ['GET'])] - public function getAssetAction(DownloadAssetHandler $downloadAsset, #[MapQueryParameter] int $id): StreamedResponse + public function getAssetAction(DownloadAssetPayload $payload, DownloadAssetHandler $downloadAsset): StreamedResponse { - $result = $downloadAsset($id); + $result = $downloadAsset($payload); $asset = $result->asset; $stream = $asset->getStream(); @@ -65,9 +69,9 @@ public function getAssetAction(DownloadAssetHandler $downloadAsset, #[MapQueryPa } #[Route('/get-preview-document', name: 'opendxp_admin_asset_getpreviewdocument', methods: ['GET'])] - public function getPreviewDocumentAction(GetDocumentPreviewHandler $getDocumentPreview, #[MapQueryParameter] int $id): StreamedResponse|Response + public function getPreviewDocumentAction(GetDocumentPreviewPayload $payload, GetDocumentPreviewHandler $getDocumentPreview): StreamedResponse|Response { - $result = $getDocumentPreview($id); + $result = $getDocumentPreview($payload); $asset = $result->asset; if ($result->thumbnailPath !== null) { @@ -98,11 +102,10 @@ public function getPreviewDocumentAction(GetDocumentPreviewHandler $getDocumentP #[Route('/get-preview-video', name: 'opendxp_admin_asset_getpreviewvideo', methods: ['GET'])] public function getPreviewVideoAction( + GetVideoPreviewPayload $payload, GetVideoPreviewHandler $getVideoPreview, - #[MapQueryParameter] int $id, - #[MapQueryParameter] ?string $config = null, ): Response { - $result = $getVideoPreview($id, $config); + $result = $getVideoPreview($payload); $previewData = [ 'asset' => $result->asset, 'thumbnail' => $result->thumbnail, @@ -118,11 +121,10 @@ public function getPreviewVideoAction( #[Route('/serve-video-preview', name: 'opendxp_admin_asset_servevideopreview', methods: ['GET'])] public function serveVideoPreviewAction( + ServeVideoPreviewPayload $payload, ServeVideoPreviewHandler $serveVideoPreview, - #[MapQueryParameter] int $id, - #[MapQueryParameter] ?string $config = null, ): StreamedResponse { - $result = $serveVideoPreview($id, $config); + $result = $serveVideoPreview($payload); return new StreamedResponse(static function () use ($result): void { fpassthru($result->stream); @@ -135,11 +137,10 @@ public function serveVideoPreviewAction( #[Route('/get-text', name: 'opendxp_admin_asset_gettext', methods: ['GET'])] public function getTextAction( + GetAssetTextPayload $payload, GetAssetTextHandler $getAssetText, - #[MapQueryParameter] int $id, - #[MapQueryParameter] ?int $page = null, ): JsonResponse { - $result = $getAssetText($id, $page); + $result = $getAssetText($payload); return $this->adminJson(ApiResponse::ok(['text' => $result->text])); } diff --git a/src/Controller/Admin/Asset/AssetThumbnailController.php b/src/Controller/Admin/Asset/AssetThumbnailController.php index 5a6af57d..8d1e7447 100644 --- a/src/Controller/Admin/Asset/AssetThumbnailController.php +++ b/src/Controller/Admin/Asset/AssetThumbnailController.php @@ -21,18 +21,21 @@ use DateTime; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetDocumentThumbnail\GetDocumentThumbnailPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetDocumentThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderContentPreview\GetFolderContentPreviewPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderContentPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderThumbnail\GetFolderThumbnailPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetImageThumbnail\GetImageThumbnailPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetImageThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetVideoThumbnail\GetVideoThumbnailPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetVideoThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -44,37 +47,10 @@ class AssetThumbnailController extends AdminAbstractController { #[Route('/get-image-thumbnail', name: 'opendxp_admin_asset_getimagethumbnail', methods: ['GET'])] public function getImageThumbnailAction( + GetImageThumbnailPayload $payload, GetImageThumbnailHandler $getImageThumbnail, - Request $request, - #[MapQueryParameter] ?string $fileinfo = null, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $config = null, - #[MapQueryParameter] ?array $thumbnail = null, - #[MapQueryParameter] ?string $treepreview = null, - #[MapQueryParameter] ?string $origin = null, - #[MapQueryParameter] ?string $cropPercent = null, - #[MapQueryParameter] ?string $cropWidth = null, - #[MapQueryParameter] ?string $cropHeight = null, - #[MapQueryParameter] ?string $cropTop = null, - #[MapQueryParameter] ?string $cropLeft = null, ): BinaryFileResponse|JsonResponse|StreamedResponse { - $configDecoded = $config ? $this->decodeJson($config) : null; - $hasCropPercent = $cropPercent !== null && filter_var($cropPercent, FILTER_VALIDATE_BOOLEAN); - - $result = $getImageThumbnail( - $id, - $fileinfo !== null, - $thumbnail, - $configDecoded, - $request->query->all(), - $treepreview !== null, - $origin, - $hasCropPercent, - $cropWidth, - $cropHeight, - $cropTop, - $cropLeft, - ); + $result = $getImageThumbnail($payload); if ($result->returnLoadingGif) { $response = new BinaryFileResponse(OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/video-loading.gif'); @@ -112,9 +88,9 @@ public function getImageThumbnailAction( #[Route('/get-folder-thumbnail', name: 'opendxp_admin_asset_getfolderthumbnail', methods: ['GET'])] #[IsGranted(CorePermission::Assets->value)] - public function getFolderThumbnailAction(GetFolderThumbnailHandler $getFolderThumbnail, #[MapQueryParameter] ?int $id = null): StreamedResponse + public function getFolderThumbnailAction(GetFolderThumbnailPayload $payload, GetFolderThumbnailHandler $getFolderThumbnail): StreamedResponse { - $result = $getFolderThumbnail($id); + $result = $getFolderThumbnail($payload); $response = new StreamedResponse(static function () use ($result): void { fpassthru($result->stream); @@ -128,26 +104,10 @@ public function getFolderThumbnailAction(GetFolderThumbnailHandler $getFolderThu #[Route('/get-video-thumbnail', name: 'opendxp_admin_asset_getvideothumbnail', methods: ['GET'])] public function getVideoThumbnailAction( + GetVideoThumbnailPayload $payload, GetVideoThumbnailHandler $getVideoThumbnail, - Request $request, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $path = null, - #[MapQueryParameter] ?string $time = null, - #[MapQueryParameter] int $image = 0, - #[MapQueryParameter] ?string $origin = null, ): StreamedResponse { - $result = $getVideoThumbnail( - $request->query->has('id') ? $id : null, - $path, - $request->query->has('treepreview'), - $request->query->has('settime'), - $request->query->has('setimage'), - $request->query->has('image'), - $image, - $time, - $origin, - $request->query->all(), - ); + $result = $getVideoThumbnail($payload); $response = new StreamedResponse(static function () use ($result): void { fpassthru($result->stream); @@ -161,14 +121,10 @@ public function getVideoThumbnailAction( #[Route('/get-document-thumbnail', name: 'opendxp_admin_asset_getdocumentthumbnail', methods: ['GET'])] public function getDocumentThumbnailAction( + GetDocumentThumbnailPayload $payload, GetDocumentThumbnailHandler $getDocumentThumbnail, - Request $request, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $treepreview = null, - #[MapQueryParameter] ?int $page = null, - #[MapQueryParameter] ?string $origin = null, ): BinaryFileResponse|StreamedResponse { - $result = $getDocumentThumbnail($id, $treepreview !== null, $page, $origin, $request->query->all()); + $result = $getDocumentThumbnail($payload); if ($result->stream) { $response = new StreamedResponse(static function () use ($result): void { @@ -188,10 +144,10 @@ public function getDocumentThumbnailAction( #[Route('/get-folder-content-preview', name: 'opendxp_admin_asset_getfoldercontentpreview', methods: ['GET'])] #[IsGranted(CorePermission::Assets->value)] public function getFolderContentPreviewAction( + GetFolderContentPreviewPayload $payload, GetFolderContentPreviewHandler $getFolderContentPreview, - Request $request, ): JsonResponse { - $result = $getFolderContentPreview($request->query->all()); + $result = $getFolderContentPreview($payload); return $this->adminJson(ApiResponse::ok(['assets' => $result->assets, 'total' => $result->total])); } diff --git a/src/Controller/Admin/Asset/AssetUploadController.php b/src/Controller/Admin/Asset/AssetUploadController.php index e7567d72..b9a74481 100644 --- a/src/Controller/Admin/Asset/AssetUploadController.php +++ b/src/Controller/Admin/Asset/AssetUploadController.php @@ -19,17 +19,19 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\CheckAssetExists\CheckAssetExistsPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\CheckAssetExistsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZipFiles\ImportZipFilesPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZipFilesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZip\ImportZipPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZipHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ReplaceAsset\ReplaceAssetPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ReplaceAssetHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetUploadService; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -81,23 +83,18 @@ public function addAssetCompatibilityAction(Request $request): JsonResponse #[Route('/exists', name: 'opendxp_admin_asset_exists', methods: ['GET'])] public function existsAction( + CheckAssetExistsPayload $payload, CheckAssetExistsHandler $checkAssetExists, - #[MapQueryParameter] int $parentId, - #[MapQueryParameter] string $filename = '', - #[MapQueryParameter] string $dir = '', ): JsonResponse { return new JsonResponse([ - 'exists' => $checkAssetExists($parentId, $filename, $dir), + 'exists' => $checkAssetExists($payload), ]); } #[Route('/replace-asset', name: 'opendxp_admin_asset_replaceasset', methods: ['POST', 'PUT'])] - public function replaceAssetAction(ReplaceAssetHandler $replaceAsset, Request $request, #[MapQueryParameter] int $id): JsonResponse + public function replaceAssetAction(ReplaceAssetPayload $payload, ReplaceAssetHandler $replaceAsset): JsonResponse { - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - - $asset = $replaceAsset($id, $file->getPathname(), $file->getClientOriginalName()); + $asset = $replaceAsset($payload); $response = $this->adminJson(ApiResponse::ok(['id' => $asset->getId(), 'path' => $asset->getRealFullPath()])); $response->headers->set('Content-Type', 'text/html'); @@ -107,37 +104,23 @@ public function replaceAssetAction(ReplaceAssetHandler $replaceAsset, Request $r #[Route('/import-zip', name: 'opendxp_admin_asset_importzip', methods: ['POST'])] public function importZipAction( + ImportZipPayload $payload, ImportZipHandler $importZip, Request $request, - #[MapQueryParameter] int $parentId = 0, - #[MapQueryParameter] ?string $allowOverwrite = null, ): Response { if (!$request->files->has('Filedata')) { throw new BadRequestHttpException('Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions on the file system'); } - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - if (!is_file($file->getPathname())) { - throw new BadRequestHttpException('Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions on the file system'); - } - - $importResult = $importZip($parentId, $file->getPathname(), $allowOverwrite); + $importResult = $importZip($payload); return new Response($this->encodeJson(ApiResponse::ok(['jobs' => $importResult->jobs, 'jobId' => $importResult->jobId]))); } #[Route('/import-zip-files', name: 'opendxp_admin_asset_importzipfiles', methods: ['POST'])] - public function importZipFilesAction(ImportZipFilesHandler $importZipFiles, Request $request): JsonResponse + public function importZipFilesAction(ImportZipFilesPayload $payload, ImportZipFilesHandler $importZipFiles): JsonResponse { - $importZipFiles( - (int) $request->request->get('parentId'), - (string) $request->request->get('jobId'), - (int) $request->request->get('offset'), - (int) $request->request->get('limit'), - $request->request->get('allowOverwrite') === 'true', - (bool) $request->request->get('last'), - ); + $importZipFiles($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/Asset/AssetVersionController.php b/src/Controller/Admin/Asset/AssetVersionController.php index 4a2b2f10..f463b1ff 100644 --- a/src/Controller/Admin/Asset/AssetVersionController.php +++ b/src/Controller/Admin/Asset/AssetVersionController.php @@ -20,13 +20,14 @@ use OpenDxp; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\PublishVersion\PublishVersionPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\PublishVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\ShowVersion\ShowVersionPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\ShowVersionHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Tool; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; @@ -43,14 +44,12 @@ class AssetVersionController extends AdminAbstractController { #[Route('/publish-version', name: 'opendxp_admin_asset_publishversion', methods: ['POST'])] public function publishVersionAction( - Request $request, + PublishVersionPayload $payload, PublishVersionHandler $publishVersion, ElementServiceInterface $elementService, ): JsonResponse { - $result = $publishVersion( - (int) $request->request->get('id'), - ); + $result = $publishVersion($payload); return $this->adminJson(ApiResponse::ok([ 'treeData' => $elementService->getElementTreeNodeConfig($result->asset), @@ -60,11 +59,11 @@ public function publishVersionAction( #[Route('/show-version', name: 'opendxp_admin_asset_showversion', methods: ['GET'])] public function showVersionAction( Environment $twig, + ShowVersionPayload $payload, ShowVersionHandler $showVersion, - #[MapQueryParameter] int $id = 0, #[MapQueryParameter] ?string $userTimezone = null, ): Response { - $result = $showVersion($id); + $result = $showVersion($payload); if ($result->isPdf) { return $this->render( diff --git a/src/Controller/Admin/DataObject/ClassController.php b/src/Controller/Admin/DataObject/ClassController.php index ac7170e5..30d4c8b4 100644 --- a/src/Controller/Admin/DataObject/ClassController.php +++ b/src/Controller/Admin/DataObject/ClassController.php @@ -18,38 +18,52 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClassPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommitHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommitPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkExportPrepareHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkExportPreparePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImportPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClassPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptionsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DoBulkExportHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClassPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetAssetTypesHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassBulkExportListHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfigPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIconsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIconsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTreePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetDocumentTypesHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTreePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsagesPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreviewHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreviewPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetVideoAllowedTypesHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClassPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinitionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinitionPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptionsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SuggestClassIdentifierHandler; +use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Logger; -use OpenDxp\Model\DataObject; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -75,13 +89,8 @@ public function getAssetTypesAction(GetAssetTypesHandler $handler): JsonResponse } #[Route('/get-tree', name: 'gettree', methods: ['GET', 'POST'])] - public function getTreeAction( - GetClassTreeHandler $handler, - #[MapQueryParameter] ?string $createAllowed = null, - #[MapQueryParameter] ?string $withId = null, - #[MapQueryParameter] ?string $useTitle = null, - #[MapQueryParameter] ?string $grouped = null, - ): JsonResponse { + public function getTreeAction(GetClassTreePayload $payload, GetClassTreeHandler $handler): JsonResponse + { try { $this->checkPermission('objects'); } catch (AccessDeniedHttpException) { @@ -90,67 +99,44 @@ public function getTreeAction( return $this->adminJson([]); } - return $this->adminJson($handler( - createAllowed: (bool) $createAllowed, - withId: (bool) $withId, - useTitle: (bool) $useTitle, - grouped: (bool) $grouped, - )->nodes); + return $this->adminJson($handler($payload)->nodes); } #[IsGranted(CorePermission::Classes->value)] #[Route('/get', name: 'get', methods: ['GET'])] - public function getAction( - GetClassHandler $handler, - #[MapQueryParameter] ?string $id = null, - ): JsonResponse { - return $this->adminJson($handler($id)->classData); + public function getAction(GetClassPayload $payload, GetClassHandler $handler): JsonResponse + { + return $this->adminJson($handler($payload)->classData); } #[IsGranted(CorePermission::Classes->value)] #[Route('/add', name: 'add', methods: ['POST'])] - public function addAction(Request $request, AddClassHandler $handler): JsonResponse + public function addAction(AddClassPayload $payload, AddClassHandler $handler): JsonResponse { - $result = $handler( - className: $request->request->get('className'), - classId: $request->request->get('classIdentifier'), - ); - - return $this->adminJson(ApiResponse::ok(['id' => $result->id])); + return $this->adminJson(ApiResponse::ok(['id' => $handler($payload)->id])); } #[IsGranted(CorePermission::Classes->value)] #[Route('/delete', name: 'delete', methods: ['DELETE'])] - public function deleteAction(Request $request, DeleteClassHandler $handler): Response + public function deleteAction(DeleteClassPayload $payload, DeleteClassHandler $handler): Response { - $handler($request->request->get('id')); + $handler($payload); return new Response(); } #[IsGranted(CorePermission::Classes->value)] #[Route('/save', name: 'save', methods: ['PUT'])] - public function saveAction(Request $request, SaveClassDefinitionHandler $handler): JsonResponse + public function saveAction(SaveClassDefinitionPayload $payload, SaveClassDefinitionHandler $handler): JsonResponse { - $result = $handler( - id: $request->request->get('id'), - configuration: $this->decodeJson($request->request->get('configuration')), - values: $this->decodeJson($request->request->get('values')), - ); - - return $this->adminJson(ApiResponse::ok(['class' => $result->class])); + return $this->adminJson(ApiResponse::ok(['class' => $handler($payload)->class])); } #[IsGranted(CorePermission::Classes->value)] #[Route('/import-class', name: 'importclass', methods: ['POST', 'PUT'])] - public function importClassAction( - Request $request, - ImportClassHandler $handler, - #[MapQueryParameter] ?string $id = null, - ): Response { - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $handler($id, file_get_contents($file->getPathname())); + public function importClassAction(ImportClassPayload $payload, ImportClassHandler $handler): Response + { + $handler($payload); $response = $this->adminJson(ApiResponse::ok()); @@ -163,11 +149,9 @@ public function importClassAction( #[IsGranted(CorePermission::Classes->value)] #[Route('/export-class', name: 'exportclass', methods: ['GET'])] - public function exportClassAction( - ExportClassHandler $handler, - #[MapQueryParameter] ?string $id = null, - ): Response { - $result = $handler($id); + public function exportClassAction(ExportClassPayload $payload, ExportClassHandler $handler): Response + { + $result = $handler($payload); $response = new Response($result->json); $response->headers->set('Content-type', 'application/json'); @@ -178,11 +162,10 @@ public function exportClassAction( #[Route('/get-class-definition-for-column-config', name: 'getclassdefinitionforcolumnconfig', methods: ['GET'])] public function getClassDefinitionForColumnConfigAction( + GetClassDefinitionForColumnConfigPayload $payload, GetClassDefinitionForColumnConfigHandler $handler, - #[MapQueryParameter] ?string $id = null, - #[MapQueryParameter] int $oid = 0, ): JsonResponse { - return $this->adminJson($handler($id, $oid)->config); + return $this->adminJson($handler($payload)->config); } /** @@ -190,13 +173,9 @@ public function getClassDefinitionForColumnConfigAction( */ #[IsGranted(CorePermission::Classes->value)] #[Route('/bulk-import', name: 'bulkimport', methods: ['POST'])] - public function bulkImportAction(Request $request, BulkImportHandler $handler): JsonResponse + public function bulkImportAction(BulkImportPayload $payload, BulkImportHandler $handler): JsonResponse { - /** @var UploadedFile $uploadFile */ - $uploadFile = $request->files->get('Filedata'); - $result = $handler(file_get_contents($uploadFile->getPathname())); - - $response = $this->adminJson(ApiResponse::ok(['data' => $result->items])); + $response = $this->adminJson(ApiResponse::ok(['data' => $handler($payload)->items])); $response->headers->set('Content-Type', 'text/html'); return $response; @@ -206,9 +185,9 @@ public function bulkImportAction(Request $request, BulkImportHandler $handler): * Add option to export/import all class definitions/brick definitions etc. at once */ #[Route('/bulk-commit', name: 'bulkcommit', methods: ['POST'])] - public function bulkCommitAction(Request $request, BulkCommitHandler $handler): JsonResponse + public function bulkCommitAction(BulkCommitPayload $payload, BulkCommitHandler $handler): JsonResponse { - $handler(json_decode($request->request->get('data'), true)); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -218,9 +197,9 @@ public function bulkCommitAction(Request $request, BulkCommitHandler $handler): */ #[IsGranted(CorePermission::Classes->value)] #[Route('/bulk-export-prepare', name: 'bulkexportprepare', methods: ['POST'])] - public function bulkExportPrepareAction(Request $request, BulkExportPrepareHandler $handler): Response + public function bulkExportPrepareAction(BulkExportPreparePayload $payload, BulkExportPrepareHandler $handler): Response { - $handler($request->request->get('data')); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -243,22 +222,19 @@ public function doBulkExportAction(DoBulkExportHandler $handler): Response return $response; } - #[Route('/get-select-options-usages', name: 'getselectoptionsusages', methods: [Request::METHOD_GET])] + #[Route('/get-select-options-usages', name: 'getselectoptionsusages', methods: ['GET'])] public function getSelectOptionsUsagesAction( + GetSelectOptionsUsagesPayload $payload, GetSelectOptionsUsagesHandler $handler, - #[MapQueryParameter(name: DataObject\SelectOptions\Config::PROPERTY_ID)] ?string $id = null, ): Response { - return $this->adminJson($handler($id)->usages); + return $this->adminJson($handler($payload)->usages); } #[IsGranted(CorePermission::Classes->value)] #[Route('/get-icons', name: 'geticons', methods: ['GET'])] - public function getIconsAction( - GetClassIconsHandler $handler, - #[MapQueryParameter] ?string $classId = null, - #[MapQueryParameter] ?string $type = null, - ): Response { - return $this->adminJson($handler($type, $classId)->icons); + public function getIconsAction(GetClassIconsPayload $payload, GetClassIconsHandler $handler): Response + { + return $this->adminJson($handler($payload)->icons); } #[IsGranted(CorePermission::Classes->value)] @@ -275,21 +251,9 @@ public function suggestClassIdentifierAction(SuggestClassIdentifierHandler $hand #[IsGranted(CorePermission::Classes->value)] #[Route('/text-layout-preview', name: 'textlayoutpreview', methods: ['GET'])] - public function textLayoutPreviewAction( - GetTextLayoutPreviewHandler $handler, - #[MapQueryParameter] string $previewObject = '', - #[MapQueryParameter] ?string $className = null, - #[MapQueryParameter] ?string $renderingData = null, - #[MapQueryParameter] ?string $renderingClass = null, - #[MapQueryParameter] ?string $html = null, - ): Response { - $response = new Response($handler( - objPath: $previewObject, - className: $className, - renderingData: $renderingData, - renderingClass: $renderingClass, - html: $html, - )->content); + public function textLayoutPreviewAction(GetTextLayoutPreviewPayload $payload, GetTextLayoutPreviewHandler $handler): Response + { + $response = new Response($handler($payload)->content); $response->headers->set('Content-Type', 'text/html'); return $response; @@ -303,48 +267,31 @@ public function videoAllowedTypesAction(GetVideoAllowedTypesHandler $handler): R } #[IsGranted(CorePermission::Selectoptions->value)] - #[Route('/select-options-get', name: 'selectoptionsget', methods: [Request::METHOD_GET])] - public function selectOptionsGetAction( - GetSelectOptionsHandler $handler, - #[MapQueryParameter(name: DataObject\SelectOptions\Config::PROPERTY_ID)] ?string $id = null, - ): JsonResponse { - return $this->adminJson($handler($id)->data); + #[Route('/select-options-get', name: 'selectoptionsget', methods: ['GET'])] + public function selectOptionsGetAction(GetSelectOptionsPayload $payload, GetSelectOptionsHandler $handler): JsonResponse + { + return $this->adminJson($handler($payload)->data); } #[IsGranted(CorePermission::Selectoptions->value)] - #[Route('/select-options-update', name: 'selectoptionsupdate', methods: [Request::METHOD_PUT, Request::METHOD_POST])] - public function selectOptionsUpdateAction( - Request $request, - SaveSelectOptionsHandler $handler, - ): JsonResponse { - $result = $handler( - id: $request->request->get(DataObject\SelectOptions\Config::PROPERTY_ID), - task: $request->request->get('task', ''), - group: $request->request->get(DataObject\SelectOptions\Config::PROPERTY_GROUP), - useTraits: $request->request->get(DataObject\SelectOptions\Config::PROPERTY_USE_TRAITS, ''), - implementsInterfaces: $request->request->get(DataObject\SelectOptions\Config::PROPERTY_IMPLEMENTS_INTERFACES, ''), - selectOptionsData: $this->decodeJson($request->request->get(DataObject\SelectOptions\Config::PROPERTY_SELECT_OPTIONS, 'null')), - ); - - return $this->adminJson(ApiResponse::ok(['id' => $result->id])); + #[Route('/select-options-update', name: 'selectoptionsupdate', methods: ['PUT', 'POST'])] + public function selectOptionsUpdateAction(SaveSelectOptionsPayload $payload, SaveSelectOptionsHandler $handler): JsonResponse + { + return $this->adminJson(ApiResponse::ok(['id' => $handler($payload)->id])); } #[IsGranted(CorePermission::Selectoptions->value)] - #[Route('/select-options-tree', name: 'selectoptionstree', methods: [Request::METHOD_GET, Request::METHOD_POST])] - public function selectOptionsTreeAction( - GetSelectOptionsTreeHandler $handler, - #[MapQueryParameter] int $grouped = 0, - ): JsonResponse { - return $this->adminJson($handler($grouped)->configurations); + #[Route('/select-options-tree', name: 'selectoptionstree', methods: ['GET', 'POST'])] + public function selectOptionsTreeAction(GetSelectOptionsTreePayload $payload, GetSelectOptionsTreeHandler $handler): JsonResponse + { + return $this->adminJson($handler($payload)->configurations); } #[IsGranted(CorePermission::Selectoptions->value)] - #[Route('/select-options-delete', name: 'selectoptionsdelete', methods: [Request::METHOD_DELETE])] - public function selectOptionsDeleteAction( - Request $request, - DeleteSelectOptionsHandler $handler, - ): JsonResponse { - $handler($request->request->get(DataObject\SelectOptions\Config::PROPERTY_ID)); + #[Route('/select-options-delete', name: 'selectoptionsdelete', methods: ['DELETE'])] + public function selectOptionsDeleteAction(DeleteSelectOptionsPayload $payload, DeleteSelectOptionsHandler $handler): JsonResponse + { + $handler($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/DataObject/ClassificationstoreController.php b/src/Controller/Admin/DataObject/ClassificationstoreController.php index c5c28194..95ce1a77 100644 --- a/src/Controller/Admin/DataObject/ClassificationstoreController.php +++ b/src/Controller/Admin/DataObject/ClassificationstoreController.php @@ -19,36 +19,57 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollectionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollectionsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroupsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroupsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddPropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddPropertyPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollectionPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroupPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStoreHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStorePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelationPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroupPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeletePropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeletePropertyPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelationPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStoreHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStorePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelationsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroupsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroupsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPagePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPropertiesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPropertiesPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelationsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetStoreTreeHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\ListStoresHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelationsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelationPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelationsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollectionPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroupPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdatePropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdatePropertyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use OpenDxp\Security\SecurityHelper; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -61,55 +82,45 @@ class ClassificationstoreController extends AdminAbstractController { #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-collection', name: 'deletecollection', methods: ['DELETE'])] - public function deleteCollectionAction(Request $request, DeleteCollectionHandler $handler): JsonResponse + public function deleteCollectionAction(DeleteCollectionPayload $payload, DeleteCollectionHandler $handler): JsonResponse { - $handler($request->request->getInt('id')); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-collection-relation', name: 'deletecollectionrelation', methods: ['DELETE'])] - public function deleteCollectionRelationAction(Request $request, DeleteCollectionRelationHandler $handler): JsonResponse + public function deleteCollectionRelationAction(DeleteCollectionRelationPayload $payload, DeleteCollectionRelationHandler $handler): JsonResponse { - $handler( - colId: $request->request->getInt('colId'), - groupId: $request->request->getInt('groupId'), - ); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-relation', name: 'deleterelation', methods: ['DELETE'])] - public function deleteRelationAction(Request $request, DeleteRelationHandler $handler): JsonResponse + public function deleteRelationAction(DeleteRelationPayload $payload, DeleteRelationHandler $handler): JsonResponse { - $handler( - keyId: $request->request->getInt('keyId'), - groupId: $request->request->getInt('groupId'), - ); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-group', name: 'deletegroup', methods: ['DELETE'])] - public function deleteGroupAction(Request $request, DeleteGroupHandler $handler): JsonResponse + public function deleteGroupAction(DeleteGroupPayload $payload, DeleteGroupHandler $handler): JsonResponse { - $handler($request->request->getInt('id')); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/create-group', name: 'creategroup', methods: ['POST'])] - public function createGroupAction(Request $request, CreateGroupHandler $handler): JsonResponse + public function createGroupAction(CreateGroupPayload $payload, CreateGroupHandler $handler): JsonResponse { - $name = SecurityHelper::convertHtmlSpecialChars($request->request->get('name')); - $result = $handler( - name: $name, - storeId: $request->request->getInt('storeId'), - ); + $result = $handler($payload); if ($result->alreadyExists) { throw new BadRequestHttpException('classificationstore_error_group_exists_msg'); @@ -120,341 +131,167 @@ public function createGroupAction(Request $request, CreateGroupHandler $handler) #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/create-store', name: 'createstore', methods: ['POST'])] - public function createStoreAction(Request $request, CreateStoreHandler $handler): JsonResponse + public function createStoreAction(CreateStorePayload $payload, CreateStoreHandler $handler): JsonResponse { - $name = SecurityHelper::convertHtmlSpecialChars($request->request->get('name')); - $result = $handler($name); - - return $this->adminJson(ApiResponse::ok(['storeId' => $result->storeId])); + return $this->adminJson(ApiResponse::ok(['storeId' => $handler($payload)->storeId])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/create-collection', name: 'createcollection', methods: ['POST'])] - public function createCollectionAction(Request $request, CreateCollectionHandler $handler): JsonResponse + public function createCollectionAction(CreateCollectionPayload $payload, CreateCollectionHandler $handler): JsonResponse { - $name = SecurityHelper::convertHtmlSpecialChars($request->request->get('name')); - $result = $handler( - name: $name, - storeId: $request->request->getInt('storeId'), - ); - - return $this->adminJson(ApiResponse::ok(['id' => $result->name])); + return $this->adminJson(ApiResponse::ok(['id' => $handler($payload)->name])); } #[IsGranted(CorePermission::Objects->value)] #[Route('/collections', name: 'collectionsactionget', methods: ['GET'])] - public function collectionsActionGet( - Request $request, - GetCollectionsHandler $handler, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $limit = null, - #[MapQueryParameter] ?string $dir = null, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] bool $overrideSort = false, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $oid = null, - #[MapQueryParameter] ?string $fieldname = null, - #[MapQueryParameter] ?string $searchfilter = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $storeId = null, - #[MapQueryParameter] ?string $filter = null, - ): JsonResponse { - $result = $handler( - queryAll: $request->query->all(), - limit: $limit ?? 15, - start: $start, - dir: $dir, - overrideSort: $overrideSort, - oid: $oid, - fieldname: $fieldname, - searchfilter: $searchfilter, - storeId: $storeId, - filter: $filter, - ); + public function collectionsActionGet(GetCollectionsPayload $payload, GetCollectionsHandler $handler): JsonResponse + { + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/collections', name: 'collections', methods: ['POST', 'PUT'])] - public function collectionsAction(Request $request, UpdateCollectionHandler $handler): JsonResponse + public function collectionsAction(UpdateCollectionPayload $payload, UpdateCollectionHandler $handler): JsonResponse { - if (!$request->request->has('data')) { + if (!$payload->hasData) { throw new BadRequestHttpException(); } - $data = $this->decodeJson($request->request->get('data')); - $result = $handler($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->item])); + return $this->adminJson(ApiResponse::ok(['data' => $handler($payload)->item])); } #[IsGranted(CorePermission::Objects->value)] #[Route('/groups', name: 'groupsactionget', methods: ['GET'])] - public function groupsActionGet( - Request $request, - GetGroupsHandler $handler, - #[MapQueryParameter] ?string $dir = null, - #[MapQueryParameter] ?string $sort = null, - #[MapQueryParameter] int $limit = 0, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] bool $overrideSort = false, - #[MapQueryParameter] ?string $searchfilter = null, - #[MapQueryParameter] int $storeId = 0, - #[MapQueryParameter] ?string $filter = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $oid = null, - #[MapQueryParameter] ?string $fieldname = null, - ): JsonResponse { - $result = $handler( - queryAll: $request->query->all(), - limit: $limit ?: 15, - start: $start, - dir: $dir, - sort: $sort, - overrideSort: $overrideSort, - searchfilter: $searchfilter, - storeId: $storeId, - filter: $filter, - oid: $oid, - fieldname: $fieldname, - ); + public function groupsActionGet(GetGroupsPayload $payload, GetGroupsHandler $handler): JsonResponse + { + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/groups', name: 'groupsaction', methods: ['POST', 'PUT'])] - public function groupsAction(Request $request, UpdateGroupHandler $handler): JsonResponse + public function groupsAction(UpdateGroupPayload $payload, UpdateGroupHandler $handler): JsonResponse { - if (!$request->request->has('data')) { + if (!$payload->hasData) { throw new BadRequestHttpException(); } - $data = $this->decodeJson($request->request->get('data')); - $result = $handler($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->item])); + return $this->adminJson(ApiResponse::ok(['data' => $handler($payload)->item])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/collection-relations', name: 'collectionrelationsget', methods: ['GET'])] - public function collectionRelationsGetAction( - Request $request, - GetCollectionRelationsHandler $handler, - #[MapQueryParameter] ?string $dir = null, - #[MapQueryParameter] bool $overrideSort = false, - #[MapQueryParameter] int $limit = 0, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] ?string $filter = null, - #[MapQueryParameter] int $colId = 0, - ): JsonResponse { - $result = $handler( - queryAll: $request->query->all(), - limit: $limit ?: 15, - start: $start, - dir: $dir, - overrideSort: $overrideSort, - filter: $filter, - colId: $colId, - ); + public function collectionRelationsGetAction(GetCollectionRelationsPayload $payload, GetCollectionRelationsHandler $handler): JsonResponse + { + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/collection-relations', name: 'collectionrelations', methods: ['POST', 'PUT'])] - public function collectionRelationsAction(Request $request, SaveCollectionRelationsHandler $handler): JsonResponse + public function collectionRelationsAction(SaveCollectionRelationsPayload $payload, SaveCollectionRelationsHandler $handler): JsonResponse { - if (!$request->request->has('data')) { + if (!$payload->hasData) { throw new BadRequestHttpException(); } - $data = $this->decodeJson($request->request->get('data')); - $result = $handler($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); + return $this->adminJson(ApiResponse::ok(['data' => $handler($payload)->data])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/list-stores', name: 'liststores', methods: ['GET'])] public function listStoresAction(ListStoresHandler $handler): JsonResponse { - $result = $handler(); - - return $this->adminJson($result->storeConfigs); + return $this->adminJson($handler()->storeConfigs); } #[Route('/search-relations', name: 'searchrelations', methods: ['GET'])] - public function searchRelationsAction( - Request $request, - SearchRelationsHandler $handler, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $storeId = null, - #[MapQueryParameter] ?string $dir = null, - #[MapQueryParameter] bool $overrideSort = false, - #[MapQueryParameter] int $limit = 0, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] ?string $filter = null, - #[MapQueryParameter] ?string $searchfilter = null, - ): JsonResponse { - $result = $handler( - queryAll: $request->query->all(), - storeId: $storeId, - limit: $limit ?: 15, - start: $start, - dir: $dir, - overrideSort: $overrideSort, - filter: $filter, - searchfilter: $searchfilter, - ); + public function searchRelationsAction(SearchRelationsPayload $payload, SearchRelationsHandler $handler): JsonResponse + { + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[Route('/relations', name: 'relationsactionget', methods: ['GET'])] - public function relationsActionGet( - Request $request, - GetRelationsHandler $handler, - #[MapQueryParameter] ?string $relationIds = null, - #[MapQueryParameter] ?string $dir = null, - #[MapQueryParameter] bool $overrideSort = false, - #[MapQueryParameter] int $limit = 0, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] ?string $filter = null, - #[MapQueryParameter] ?string $groupId = null, - ): JsonResponse { - $result = $handler( - queryAll: $request->query->all(), - relationIds: $relationIds, - limit: $limit ?: 15, - start: $start, - dir: $dir, - overrideSort: $overrideSort, - filter: $filter, - groupId: $groupId, - ); + public function relationsActionGet(GetRelationsPayload $payload, GetRelationsHandler $handler): JsonResponse + { + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/relations', name: 'relations', methods: ['POST', 'PUT'])] - public function relationsAction(Request $request, SaveRelationHandler $handler): JsonResponse + public function relationsAction(SaveRelationPayload $payload, SaveRelationHandler $handler): JsonResponse { - if (!$request->request->has('data')) { + if (!$payload->hasData) { throw new BadRequestHttpException(); } - $data = $this->decodeJson($request->request->get('data')); - $result = $handler($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); + return $this->adminJson(ApiResponse::ok(['data' => $handler($payload)->data])); } #[IsGranted(CorePermission::Objects->value)] #[Route('/add-collections', name: 'addcollections', methods: ['POST'])] - public function addCollectionsAction(Request $request, AddCollectionsHandler $handler): JsonResponse + public function addCollectionsAction(AddCollectionsPayload $payload, AddCollectionsHandler $handler): JsonResponse { - $ids = $this->decodeJson($request->request->get('collectionIds')); - $result = $handler( - ids: $ids ?: [], - oid: $request->request->getInt('oid'), - fieldname: $request->request->get('fieldname') ?? '', - ); - - return $this->adminJson($result->data); + return $this->adminJson($handler($payload)->data); } #[IsGranted(CorePermission::Objects->value)] #[Route('/add-groups', name: 'addgroups', methods: ['POST'])] - public function addGroupsAction(Request $request, AddGroupsHandler $handler): JsonResponse + public function addGroupsAction(AddGroupsPayload $payload, AddGroupsHandler $handler): JsonResponse { - $ids = $this->decodeJson($request->request->get('groupIds')); - $result = $handler( - ids: $ids, - oid: $request->request->getInt('oid'), - fieldname: $request->request->get('fieldname'), - ); - - return $this->adminJson($result->data); + return $this->adminJson($handler($payload)->data); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/properties', name: 'propertiesget', methods: ['GET'])] - public function propertiesGetAction( - Request $request, - GetPropertiesHandler $handler, - #[MapQueryParameter] int $storeId = 0, - #[MapQueryParameter] ?string $frameName = null, - #[MapQueryParameter] ?string $dir = null, - #[MapQueryParameter] bool $overrideSort = false, - #[MapQueryParameter] int $limit = 0, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] ?string $groupIds = null, - #[MapQueryParameter] ?string $keyIds = null, - #[MapQueryParameter] ?string $searchfilter = null, - #[MapQueryParameter] ?string $filter = null, - ): JsonResponse { - $result = $handler( - queryAll: $request->query->all(), - storeId: $storeId, - frameName: $frameName, - limit: $limit ?: 15, - start: $start, - dir: $dir, - overrideSort: $overrideSort, - groupIds: $groupIds, - keyIds: $keyIds, - searchfilter: $searchfilter, - filter: $filter, - ); + public function propertiesGetAction(GetPropertiesPayload $payload, GetPropertiesHandler $handler): JsonResponse + { + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/properties', name: 'properties', methods: ['POST', 'PUT'])] - public function propertiesAction(Request $request, UpdatePropertyHandler $handler): JsonResponse + public function propertiesAction(UpdatePropertyPayload $payload, UpdatePropertyHandler $handler): JsonResponse { - if (!$request->request->has('data')) { + if (!$payload->hasData) { throw new BadRequestHttpException(); } - $data = $this->decodeJson($request->request->get('data')); - $result = $handler($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->item])); + return $this->adminJson(ApiResponse::ok(['data' => $handler($payload)->item])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/add-property', name: 'addproperty', methods: ['POST'])] - public function addPropertyAction(Request $request, AddPropertyHandler $handler): JsonResponse + public function addPropertyAction(AddPropertyPayload $payload, AddPropertyHandler $handler): JsonResponse { - $result = $handler( - name: $request->request->get('name'), - storeId: $request->request->getInt('storeId'), - ); - - return $this->adminJson(ApiResponse::ok(['id' => $result->name])); + return $this->adminJson(ApiResponse::ok(['id' => $handler($payload)->name])); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/delete-property', name: 'deleteproperty', methods: ['DELETE'])] - public function deletePropertyAction(Request $request, DeletePropertyHandler $handler): JsonResponse + public function deletePropertyAction(DeletePropertyPayload $payload, DeletePropertyHandler $handler): JsonResponse { - $handler($request->request->getInt('id')); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/edit-store', name: 'editstore', methods: ['PUT'])] - public function editStoreAction(Request $request, EditStoreHandler $handler): JsonResponse + public function editStoreAction(EditStorePayload $payload, EditStoreHandler $handler): JsonResponse { - $id = $request->request->getInt('id'); - $data = json_decode($request->request->get('data'), true); - - $handler( - id: $id, - name: $data['name'], - description: $data['description'], - ); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -463,32 +300,13 @@ public function editStoreAction(Request $request, EditStoreHandler $handler): Js #[Route('/storetree', name: 'storetree', methods: ['GET'])] public function storetreeAction(GetStoreTreeHandler $handler): JsonResponse { - $result = $handler(); - - return $this->adminJson($result->items); + return $this->adminJson($handler()->items); } #[IsGranted(CorePermission::Classificationstore->value)] #[Route('/get-page', name: 'getpage', methods: ['GET'])] - public function getPageAction( - GetPageHandler $handler, - #[MapQueryParameter] ?string $table = null, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] int $storeId = 0, - #[MapQueryParameter] int $pageSize = 0, - #[MapQueryParameter] ?string $sortKey = null, - #[MapQueryParameter] ?string $sortDir = null, - ): JsonResponse { - $result = $handler( - table: $table, - id: $id, - storeId: $storeId, - pageSize: $pageSize, - sortKey: $sortKey, - sortDir: $sortDir, - ); - - return $this->adminJson(ApiResponse::ok(['page' => $result->page])); + public function getPageAction(GetPagePayload $payload, GetPageHandler $handler): JsonResponse + { + return $this->adminJson(ApiResponse::ok(['page' => $handler($payload)->page])); } - } diff --git a/src/Controller/Admin/DataObject/CustomLayoutController.php b/src/Controller/Admin/DataObject/CustomLayoutController.php index 07c11a07..e8a6de91 100644 --- a/src/Controller/Admin/DataObject/CustomLayoutController.php +++ b/src/Controller/Admin/DataObject/CustomLayoutController.php @@ -20,21 +20,25 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\AddCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\AddCustomLayout\AddCustomLayoutPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\DeleteCustomLayoutHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayout\ExportCustomLayoutPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetAllLayoutsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitions\GetCustomLayoutDefinitionsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayout\GetCustomLayoutPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ImportCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ImportCustomLayout\ImportCustomLayoutPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SaveCustomLayoutHandler; -use OpenDxp\Model\DataObject; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SaveCustomLayout\SaveCustomLayoutPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifierHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier\SuggestCustomLayoutIdentifierPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -46,9 +50,9 @@ class CustomLayoutController extends AdminAbstractController { #[Route('/get-custom-layout', name: 'getcustomlayout', methods: ['GET'])] - public function getCustomLayoutAction(GetCustomLayoutHandler $getCustomLayout, #[MapQueryParameter] string $id): JsonResponse + public function getCustomLayoutAction(GetCustomLayoutHandler $getCustomLayout, GetCustomLayoutPayload $payload): JsonResponse { - $result = $getCustomLayout($id); + $result = $getCustomLayout($payload); $data = $result->data; $data['isWriteable'] = $result->isWriteable; @@ -56,13 +60,9 @@ public function getCustomLayoutAction(GetCustomLayoutHandler $getCustomLayout, # } #[Route('/add-custom-layout', name: 'addcustomlayout', methods: ['POST'])] - public function addCustomLayoutAction(AddCustomLayoutHandler $addCustomLayout, Request $request): JsonResponse + public function addCustomLayoutAction(AddCustomLayoutHandler $addCustomLayout, AddCustomLayoutPayload $payload): JsonResponse { - $customLayout = $addCustomLayout( - (string) $request->request->get('layoutIdentifier'), - (string) $request->request->get('layoutName'), - (string) $request->request->get('classId'), - ); + $customLayout = $addCustomLayout($payload); $data = $customLayout->getObjectVars(); $data['isWriteable'] = $customLayout->isWriteable(); @@ -71,21 +71,17 @@ public function addCustomLayoutAction(AddCustomLayoutHandler $addCustomLayout, R } #[Route('/save-custom-layout', name: 'savecustomlayout', methods: ['PUT'])] - public function saveCustomLayoutAction(SaveCustomLayoutHandler $saveCustomLayout, Request $request): JsonResponse + public function saveCustomLayoutAction(SaveCustomLayoutHandler $saveCustomLayout, SaveCustomLayoutPayload $payload): JsonResponse { - $customLayout = $saveCustomLayout( - (string) $request->request->get('id'), - $this->decodeJson($request->request->get('configuration')), - $this->decodeJson($request->request->get('values')), - ); + $customLayout = $saveCustomLayout($payload); return $this->adminJson(ApiResponse::ok(['id' => $customLayout->getId(), 'data' => $customLayout->getObjectVars()])); } #[Route('/delete-custom-layout', name: 'deletecustomlayout', methods: ['DELETE'])] - public function deleteCustomLayoutAction(DeleteCustomLayoutHandler $deleteCustomLayout, Request $request): JsonResponse + public function deleteCustomLayoutAction(DeleteCustomLayoutHandler $deleteCustomLayout, StringIdBodyPayload $payload): JsonResponse { - $deleteCustomLayout((string) $request->request->get('id')); + $deleteCustomLayout($payload); return $this->adminJson(ApiResponse::ok()); } @@ -93,21 +89,16 @@ public function deleteCustomLayoutAction(DeleteCustomLayoutHandler $deleteCustom #[Route('/import-custom-layout-definition', name: 'importcustomlayoutdefinition', methods: ['POST', 'PUT'])] public function importCustomLayoutDefinitionAction( ImportCustomLayoutHandler $importCustomLayout, - Request $request, - #[MapQueryParameter] ?string $id = null, + ImportCustomLayoutPayload $payload, ): Response { - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $importData = $this->decodeJson(file_get_contents($file->getPathname())); - - if (isset($importData['name']) && DataObject\ClassDefinition\CustomLayout::getByName($importData['name']) instanceof DataObject\ClassDefinition\CustomLayout) { + if ($payload->nameAlreadyInUse) { $response = $this->adminJson(ApiResponse::error(null, ['nameAlreadyInUse' => true])); $response->headers->set('Content-Type', 'text/html'); return $response; } - $importCustomLayout($id, $importData); + $importCustomLayout($payload); $response = $this->adminJson(ApiResponse::ok()); $response->headers->set('Content-Type', 'text/html'); @@ -116,9 +107,9 @@ public function importCustomLayoutDefinitionAction( } #[Route('/export-custom-layout-definition', name: 'exportcustomlayoutdefinition', methods: ['GET'])] - public function exportCustomLayoutDefinitionAction(ExportCustomLayoutHandler $exportCustomLayout, #[MapQueryParameter] ?string $id = null): Response + public function exportCustomLayoutDefinitionAction(ExportCustomLayoutHandler $exportCustomLayout, ExportCustomLayoutPayload $payload): Response { - $result = $exportCustomLayout($id); + $result = $exportCustomLayout($payload); $response = new Response($result->json); $response->headers->set('Content-type', 'application/json'); @@ -128,9 +119,9 @@ public function exportCustomLayoutDefinitionAction(ExportCustomLayoutHandler $ex } #[Route('/get-custom-layout-definitions', name: 'getcustomlayoutdefinitions', methods: ['GET'])] - public function getCustomLayoutDefinitionsAction(GetCustomLayoutDefinitionsHandler $getDefinitions, #[MapQueryParameter] string $classId): JsonResponse + public function getCustomLayoutDefinitionsAction(GetCustomLayoutDefinitionsHandler $getDefinitions, GetCustomLayoutDefinitionsPayload $payload): JsonResponse { - return $this->adminJson(ApiResponse::ok(['data' => $getDefinitions($classId)->definitions])); + return $this->adminJson(ApiResponse::ok(['data' => $getDefinitions($payload)->definitions])); } #[Route('/get-all-layouts', name: 'getalllayouts', methods: ['GET'])] @@ -140,9 +131,9 @@ public function getAllLayoutsAction(GetAllLayoutsHandler $getAllLayouts): JsonRe } #[Route('/suggest-custom-layout-identifier', name: 'suggestcustomlayoutidentifier', methods: ['GET'])] - public function suggestCustomLayoutIdentifierAction(SuggestCustomLayoutIdentifierHandler $suggestIdentifier, #[MapQueryParameter] string $classId): Response + public function suggestCustomLayoutIdentifierAction(SuggestCustomLayoutIdentifierHandler $suggestIdentifier, SuggestCustomLayoutIdentifierPayload $payload): Response { - $result = $suggestIdentifier($classId); + $result = $suggestIdentifier($payload); return $this->adminJson([ 'suggestedIdentifier' => $result->suggestedIdentifier, diff --git a/src/Controller/Admin/DataObject/DataObjectController.php b/src/Controller/Admin/DataObject/DataObjectController.php index 5ebd4252..b555f3dd 100644 --- a/src/Controller/Admin/DataObject/DataObjectController.php +++ b/src/Controller/Admin/DataObject/DataObjectController.php @@ -13,6 +13,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ +declare(strict_types=1); namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; @@ -26,12 +27,25 @@ use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectFolderHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfo\GetIdPathPagingInfoPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetSelectOptionsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrlHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrl\GetDataObjectPreviewUrlPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectFolderHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject\SaveDataObjectHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\UpdateDataObjectHandler; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject\SaveDataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\AddObjectFolderPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\AddObjectPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\ChangeChildrenSortByPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DataObjectGridProxyPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DeleteDataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\GetDataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\GetSelectOptionsPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\SaveDataObjectFolderPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\TreeGetChildrenByIdPayload; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\UpdateDataObjectPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; @@ -41,9 +55,7 @@ use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\Security\Http\Attribute\IsGranted; use OpenDxp\Controller\Traits\ElementEditLockHelperTrait; -use OpenDxp\Db; use OpenDxp\Model\DataObject; -use OpenDxp\Model\Element; use OpenDxp\Model\Element\ElementInterface; use Override; use Symfony\Component\HttpFoundation\JsonResponse; @@ -81,29 +93,22 @@ public function __construct( #[Route('/tree-get-children-by-id', name: 'treegetchildrenbyid', methods: ['GET'])] public function treeGetChildrenByIdAction( + TreeGetChildrenByIdPayload $payload, TreeGetChildrenByIdHandler $handler, - Request $request, - #[MapQueryParameter] ?string $filter = null, - #[MapQueryParameter] int $node = 0, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] int $limit = 100000000, - #[MapQueryParameter] string $view = '', - #[MapQueryParameter] int $fromPaging = 0, #[MapQueryParameter] int $inSearch = 0, - ): JsonResponse - { - $result = $handler($node, $filter, $start, $limit, $view, $fromPaging, $request->query->all()); + ): JsonResponse { + $result = $handler($payload); if ($result->limit) { return $this->adminJson([ - 'offset' => $result->offset, - 'limit' => $result->limit, - 'total' => $result->total, - 'overflow' => !is_null($result->filter) && ($result->filteredTotalCount > $result->limit), - 'nodes' => $result->objects, + 'offset' => $result->offset, + 'limit' => $result->limit, + 'total' => $result->total, + 'overflow' => !is_null($result->filter) && ($result->filteredTotalCount > $result->limit), + 'nodes' => $result->objects, 'fromPaging' => $result->fromPaging, - 'filter' => $result->filter ?: '', - 'inSearch' => $inSearch, + 'filter' => $result->filter ?: '', + 'inSearch' => $inSearch, ]); } @@ -117,113 +122,88 @@ protected function getTreeNodeConfig(ElementInterface $element): array } #[Route('/get-id-path-paging-info', name: 'getidpathpaginginfo', methods: ['GET'])] - public function getIdPathPagingInfoAction( - GetIdPathPagingInfoHandler $handler, - #[MapQueryParameter] ?string $path = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $limit = null, - ): JsonResponse + public function getIdPathPagingInfoAction(GetIdPathPagingInfoHandler $handler, GetIdPathPagingInfoPayload $payload): JsonResponse { - if ($path === null) { + if ($payload->path === null) { return $this->adminJson(['success' => false]); } - $result = $handler($path, $limit ?? 30); + $result = $handler($payload); return $this->adminJson($result->data); } #[Route('/get', name: 'get', methods: ['GET'])] public function getAction( + GetDataObjectPayload $payload, GetDataObjectHandler $getDataObject, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $layoutId = null, - ): JsonResponse - { + ): JsonResponse { try { - $result = $getDataObject($id, $layoutId); + $result = $getDataObject($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } - $this->sessionService->removeObject('object', $id); + $this->sessionService->removeObject('object', $payload->id); return $this->adminJson($result->data); } #[Route('/get-select-options', name: 'getSelectOptions', methods: ['POST'])] - public function getSelectOptions(Request $request, GetSelectOptionsHandler $handler): JsonResponse - { - $changedData = $request->request->has('changedData') - ? $this->decodeJson($request->request->get('changedData')) - : null; - - $options = $handler( - objectId: $request->request->getInt('objectId'), - changedData: is_array($changedData) ? $changedData : null, - fieldDefinitionConfig: json_decode($request->request->get('fieldDefinition'), true), - context: json_decode($request->request->get('context'), true) ?? [], - ); + public function getSelectOptions( + GetSelectOptionsPayload $payload, + GetSelectOptionsHandler $handler, + ): JsonResponse { + $options = $handler($payload); return $this->adminJson(ApiResponse::ok(['options' => $options])); } #[Route('/get-folder', name: 'getfolder', methods: ['GET'])] public function getFolderAction( + IdQueryPayload $payload, GetDataObjectFolderHandler $handler, - #[MapQueryParameter] int $id = 0, - ): JsonResponse - { - $result = $handler($id); + ): JsonResponse { + $result = $handler($payload); return $this->adminJson($result->data); } #[Route('/add', name: 'add', methods: ['POST'])] - public function addAction(Request $request, AddObjectHandler $handler): JsonResponse - { - $result = $handler( - className: $request->request->get('className'), - classId: $request->request->get('classId'), - parentId: $request->request->getInt('parentId'), - key: $request->request->get('key'), - objectType: $request->request->get('objecttype') ?? '', - variantViaTree: (bool) $request->request->get('variantViaTree'), - ); + public function addAction( + AddObjectPayload $payload, + AddObjectHandler $handler, + ): JsonResponse { + $result = $handler($payload); return $this->adminJson(ApiResponse::ok([ - 'id' => $result->id, + 'id' => $result->id, 'type' => $result->type, ])); } #[Route('/add-folder', name: 'addfolder', methods: ['POST'])] - public function addFolderAction(Request $request, AddObjectFolderHandler $handler): JsonResponse - { - $handler( - parentId: $request->request->getInt('parentId'), - key: $request->request->get('key'), - ); + public function addFolderAction( + AddObjectFolderPayload $payload, + AddObjectFolderHandler $handler, + ): JsonResponse { + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/delete', name: 'delete', methods: ['DELETE'])] - public function deleteAction(DeleteDataObjectHandler $handler, Request $request): JsonResponse - { - $type = $request->request->get('type'); - $id = (int) $request->request->get('id'); - - if ($type !== 'children' && !$id) { + public function deleteAction( + DeleteDataObjectPayload $payload, + DeleteDataObjectHandler $handler, + ): JsonResponse { + if ($payload->type !== 'children' && !$payload->id) { throw new NotFoundHttpException(); } - $result = $handler( - type: $type ?? '', - id: $id, - amount: $request->request->getInt('amount'), - ); + $result = $handler($payload); - if ($type === 'children') { + if ($payload->type === 'children') { return $this->adminJson(ApiResponse::ok(['deleted' => $result->deleted])); } @@ -232,46 +212,33 @@ public function deleteAction(DeleteDataObjectHandler $handler, Request $request) } #[Route('/change-children-sort-by', name: 'changechildrensortby', methods: ['PUT'])] - public function changeChildrenSortByAction(ChangeChildrenSortByHandler $handler, Request $request): JsonResponse - { - $handler( - id: $request->request->getInt('id'), - sortBy: $request->request->get('sortBy') ?? '', - sortOrder: $request->request->get('childrenSortOrder') ?? '', - ); + public function changeChildrenSortByAction( + ChangeChildrenSortByPayload $payload, + ChangeChildrenSortByHandler $handler, + ): JsonResponse { + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/update', name: 'update', methods: ['PUT'])] - public function updateAction(Request $request, UpdateDataObjectHandler $handler): JsonResponse - { - $values = $this->decodeJson($request->request->get('values')); - $ids = $this->decodeJson($request->request->get('id')); - - if (is_array($ids)) { - $result = null; - foreach ($ids as $id) { - try { - $result = $handler((int) $id, $values); - } catch (\Throwable $e) { - return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); - } - } - - return $this->adminJson(ApiResponse::ok(['treeData' => $result?->treeData])); + public function updateAction( + UpdateDataObjectPayload $payload, + UpdateDataObjectHandler $handler, + ): JsonResponse { + try { + $result = $handler($payload); + } catch (\Throwable $e) { + return $this->adminJson(['success' => false, 'message' => $e->getMessage()]); } - $result = $handler((int) $ids, $values); - return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] - public function saveAction(Request $request, SaveDataObjectHandler $handler): JsonResponse + public function saveAction(SaveDataObjectHandler $handler, SaveDataObjectPayload $payload): JsonResponse { - $payload = DataObjectPayload::fromRequest($request); - $result = $handler($request->request->getInt('id'), $payload); + $result = $handler($payload); if ($payload->task === 'session' || $payload->task === 'scheduler') { return $this->adminJson(ApiResponse::ok()); @@ -281,8 +248,8 @@ public function saveAction(Request $request, SaveDataObjectHandler $handler): Js return $this->adminJson(ApiResponse::ok([ 'general' => [ 'modificationDate' => $result->modificationDate, - 'versionDate' => $result->versionDate, - 'versionCount' => $result->versionCount, + 'versionDate' => $result->versionDate, + 'versionCount' => $result->versionCount, ], 'treeData' => $result->treeData, ])); @@ -291,46 +258,34 @@ public function saveAction(Request $request, SaveDataObjectHandler $handler): Js return $this->adminJson(ApiResponse::ok([ 'general' => [ 'modificationDate' => $result->modificationDate, - 'versionDate' => $result->versionDate, - 'versionCount' => $result->versionCount, + 'versionDate' => $result->versionDate, + 'versionCount' => $result->versionCount, ], - 'draft' => $result->draftData, + 'draft' => $result->draftData, 'treeData' => $result->treeData, ])); } #[Route('/save-folder', name: 'savefolder', methods: ['PUT'])] - public function saveFolderAction(SaveDataObjectFolderHandler $handler, Request $request): JsonResponse - { - $propertiesData = $request->request->has('properties') - ? $this->decodeJson($request->request->get('properties')) - : null; - - $handler( - id: $request->request->getInt('id'), - general: $this->decodeJson($request->request->get('general')), - propertiesData: is_array($propertiesData) ? $propertiesData : null, - ); + public function saveFolderAction( + SaveDataObjectFolderPayload $payload, + SaveDataObjectFolderHandler $handler, + ): JsonResponse { + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/grid-proxy', name: 'gridproxy', methods: ['GET', 'POST', 'PUT'])] public function gridProxyAction( - Request $request, + DataObjectGridProxyPayload $payload, DataObjectGridProxyHandler $handler, + Request $request, CsrfProtectionHandler $csrfProtection, ): JsonResponse { $csrfProtection->checkCsrfToken($request); - $allParams = [...$request->request->all(), ...$request->query->all()]; - if (isset($allParams['context']) && $allParams['context']) { - $allParams['context'] = json_decode($allParams['context'], true); - } else { - $allParams['context'] = []; - } - - $result = $handler($allParams, $request->getLocale()); + $result = $handler($payload); if ($result->requestedLanguage && $result->requestedLanguage !== 'default') { $request->setLocale($result->requestedLanguage); } @@ -343,17 +298,16 @@ public function previewAction( Request $request, GetDataObjectPreviewUrlHandler $handler, #[MapQueryParameter] int $id = 0, - ): RedirectResponse|Response - { + ): RedirectResponse|Response { $object = $this->sessionService->getObject('object', $id); if ($object instanceof DataObject\Concrete) { - $redirectUrl = $handler($object, ['context' => $this, ...$request->query->all()]); + $payload = new GetDataObjectPreviewUrlPayload($object, ['context' => $this, ...$request->query->all()]); + $redirectUrl = $handler($payload); return $this->redirect($redirectUrl); } throw new NotFoundHttpException(sprintf('Expected an object of type "%s", got "%s"', DataObject\Concrete::class, get_debug_type($object))); } - } diff --git a/src/Controller/Admin/DataObject/DataObjectCopyController.php b/src/Controller/Admin/DataObject/DataObjectCopyController.php index d846b0f8..c8afcb34 100644 --- a/src/Controller/Admin/DataObject/DataObjectCopyController.php +++ b/src/Controller/Admin/DataObject/DataObjectCopyController.php @@ -19,11 +19,14 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyDataObjectHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIdsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\RewriteDataObjectIdsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyDataObject\CopyDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyDataObject\CopyDataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIds\GetDataObjectChildIdsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIds\GetDataObjectChildIdsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\RewriteDataObjectIds\RewriteDataObjectIdsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\RewriteDataObjectIds\RewriteDataObjectIdsPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use OpenDxp\Tool; +use OpenDxp\Tool\Session; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; @@ -40,19 +43,21 @@ class DataObjectCopyController extends AdminAbstractController { #[Route('/copy-info', name: 'copyinfo', methods: ['GET'])] public function copyInfoAction( + GetDataObjectChildIdsPayload $getChildIdsPayload, GetDataObjectChildIdsHandler $getChildIds, Request $request, #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter] int $sourceId = 0, #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $targetId = null, ): JsonResponse { $transactionId = time(); $pasteJobs = []; - Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { $session->set((string) $transactionId, ['idMapping' => []]); }, 'opendxp_copy'); + $sourceId = $getChildIdsPayload->sourceId; + if ($type === 'recursive' || $type === 'recursive-update-references') { $pasteJobs[] = [[ 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), @@ -66,7 +71,7 @@ public function copyInfoAction( ], ]]; - $childIds = $getChildIds($sourceId)->ids; + $childIds = $getChildIds($getChildIdsPayload)->ids; foreach ($childIds as $id) { $pasteJobs[] = [[ @@ -111,51 +116,37 @@ public function copyInfoAction( } #[Route('/copy-rewrite-ids', name: 'copyrewriteids', methods: ['PUT'])] - public function copyRewriteIdsAction(RewriteDataObjectIdsHandler $rewriteIds, Request $request): JsonResponse - { - $transactionId = $request->request->get('transactionId'); - - $idStore = Tool\Session::useBag($request->getSession(), static fn (AttributeBagInterface $session) => $session->get($transactionId), 'opendxp_copy'); - - if (!array_key_exists('rewrite-stack', $idStore)) { - $idStore['rewrite-stack'] = array_values($idStore['idMapping']); - } - - $id = array_shift($idStore['rewrite-stack']); - - $rewriteIds((int) $id, $idStore['idMapping']); + public function copyRewriteIdsAction( + RewriteDataObjectIdsPayload $payload, + RewriteDataObjectIdsHandler $rewriteIds, + Request $request, + ): JsonResponse { + $rewriteIds($payload); - Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId, $idStore): void { - $session->set($transactionId, $idStore); + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($payload): void { + $session->set($payload->transactionId, $payload->updatedIdStore); }, 'opendxp_copy'); - return $this->adminJson(ApiResponse::ok(['id' => $id])); + return $this->adminJson(ApiResponse::ok(['id' => $payload->objectId])); } #[Route('/copy', name: 'copy', methods: ['POST'])] - public function copyAction(CopyDataObjectHandler $copyObject, Request $request): JsonResponse - { - $sourceId = $request->request->getInt('sourceId'); - $targetId = $request->request->getInt('targetId'); - $type = (string) $request->request->get('type'); - - $session = Tool\Session::getSessionBag($request->getSession(), 'opendxp_copy'); - $sessionBag = $session->get($request->request->get('transactionId')); - - $sourceParentId = $request->request->has('targetParentId') ? $request->request->getInt('sourceParentId') : null; - $targetParentId = $request->request->has('targetParentId') ? $request->request->getInt('targetParentId') : null; - $sessionParentId = !empty($sessionBag['parentId']) ? (int) $sessionBag['parentId'] : null; - - $result = $copyObject($sourceId, $targetId, $type, $sourceParentId, $targetParentId, $sessionParentId); + public function copyAction( + CopyDataObjectPayload $payload, + CopyDataObjectHandler $copyObject, + Request $request, + ): JsonResponse { + $result = $copyObject($payload); if ($result->newObject !== null) { + $sessionBag = $payload->sessionBag; $sessionBag['idMapping'][$result->sourceId] = $result->newObject->getId(); - if ($request->request->get('saveParentId')) { + if ($payload->saveParentId) { $sessionBag['parentId'] = $result->newObject->getId(); } - $session->set($request->request->get('transactionId'), $sessionBag); + Session::getSessionBag($request->getSession(), 'opendxp_copy')->set($payload->transactionId, $sessionBag); } return $this->adminJson(ApiResponse::ok([ diff --git a/src/Controller/Admin/DataObject/DataObjectHelperController.php b/src/Controller/Admin/DataObject/DataObjectHelperController.php index 1e8f0916..e1ebfa4f 100644 --- a/src/Controller/Admin/DataObject/DataObjectHelperController.php +++ b/src/Controller/Admin/DataObject/DataObjectHelperController.php @@ -18,33 +18,43 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAllHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAllPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteDataObjectGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteDataObjectGridColumnConfigPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteGridColumnConfig\DeleteGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteGridColumnConfig\DeleteGridColumnConfigPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExportPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatchHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatchPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFieldsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFieldsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteDataObjectGridColumnConfigHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteGridColumnConfigHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetGridColumnConfigHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetGridColumnConfig\GetGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetGridColumnConfig\GetGridColumnConfigPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUploadHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUploadPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFieldsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectDataPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavouriteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavouritePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfigPayload; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\File; -use OpenDxp\Tool; -use stdClass; use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; +use Symfony\Component\Routing\Attribute\Route; /** * @internal @@ -56,149 +66,77 @@ public function __construct(private readonly GridExportService $gridExportServic #[Route('/load-object-data', name: 'loadobjectdata', methods: ['GET'])] public function loadObjectDataAction( + LoadObjectDataPayload $payload, LoadObjectDataHandler $handler, - Request $request, - #[MapQueryParameter] int $id = 0, ): JsonResponse { - return $this->adminJson(ApiResponse::ok(['fields' => $handler($id, $request->query->all('fields'))])); + return $this->adminJson(ApiResponse::ok(['fields' => $handler($payload)])); } #[Route('/get-export-configs', name: 'getexportconfigs', methods: ['GET'])] public function getExportConfigsAction( + GetExportConfigsPayload $payload, GetExportConfigsHandler $getExportConfigs, - #[MapQueryParameter] ?string $classId = null, ): JsonResponse { - return $this->adminJson(ApiResponse::ok(['data' => $getExportConfigs($classId)])); + return $this->adminJson(ApiResponse::ok(['data' => $getExportConfigs($payload)])); } #[Route('/grid-delete-column-config', name: 'griddeletecolumnconfig', methods: ['DELETE'])] public function gridDeleteColumnConfigAction( - DeleteDataObjectGridColumnConfigHandler $deleteGridColumnConfig, + DeleteGridColumnConfigPayload $payload, DeleteGridColumnConfigHandler $handler, - Request $request, - #[MapQueryParameter(name: 'no_system_columns')] bool $noSystemColumns = false, - #[MapQueryParameter(name: 'no_brick_columns')] bool $noBrickColumns = false, + DeleteDataObjectGridColumnConfigHandler $deleteGridColumnConfig, ): JsonResponse { - $params = [ - 'id' => $request->request->get('id'), - 'objectId' => $request->request->get('objectId'), - 'name' => $request->request->get('name'), - 'type' => $request->request->get('type'), - 'types' => $request->request->get('types'), - 'gridtype' => $request->request->get('gridtype'), - 'gridConfigId' => $request->request->get('gridConfigId'), - 'searchType' => $request->request->get('searchType'), - 'noSystemColumns' => $noSystemColumns, - 'noBrickColumns' => $noBrickColumns, - 'locale' => $request->getLocale(), - ]; - - $deleteGridColumnConfig((int) $request->request->get('gridConfigId')); - - return $this->adminJson($handler($request, $params)); + $deleteGridColumnConfig(new DeleteDataObjectGridColumnConfigPayload(gridConfigId: (int) $payload->gridConfigId)); + + return $this->adminJson($handler($payload)); } #[Route('/grid-get-column-config', name: 'gridgetcolumnconfig', methods: ['GET'])] public function gridGetColumnConfigAction( + GetGridColumnConfigPayload $payload, GetGridColumnConfigHandler $handler, - Request $request, - #[MapQueryParameter] ?string $id = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $objectId = null, - #[MapQueryParameter] ?string $name = null, - #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter] ?string $types = null, - #[MapQueryParameter] ?string $gridtype = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $gridConfigId = null, - #[MapQueryParameter] ?string $searchType = null, - #[MapQueryParameter(name: 'no_system_columns')] bool $noSystemColumns = false, - #[MapQueryParameter(name: 'no_brick_columns')] bool $noBrickColumns = false, ): JsonResponse { - $params = [ - 'id' => $id, - 'objectId' => $objectId, - 'name' => $name, - 'type' => $type, - 'types' => $types, - 'gridtype' => $gridtype, - 'gridConfigId' => $gridConfigId, - 'searchType' => $searchType, - 'noSystemColumns' => $noSystemColumns, - 'noBrickColumns' => $noBrickColumns, - ]; - - return $this->adminJson($handler($request, $params)); + return $this->adminJson($handler($payload)); } #[Route('/prepare-helper-column-configs', name: 'preparehelpercolumnconfigs', methods: ['POST'])] - public function prepareHelperColumnConfigs(Request $request, PrepareHelperColumnConfigsHandler $prepareHelperColumns): JsonResponse - { - /** @var stdClass[] $columns */ - $columns = json_decode($request->request->get('columns')); - - $existingHelperColumns = Tool\Session::useBag( - $request->getSession(), - static fn(AttributeBagInterface $bag) => $bag->get('helpercolumns', []), - 'opendxp_gridconfig', - ); - - $result = $prepareHelperColumns($columns, $existingHelperColumns); - - Tool\Session::useBag( - $request->getSession(), - static function (AttributeBagInterface $bag) use ($result): void { - $bag->set('helpercolumns', $result['helperColumns']); - }, - 'opendxp_gridconfig', - ); + public function prepareHelperColumnConfigs( + PrepareHelperColumnConfigsPayload $payload, + PrepareHelperColumnConfigsHandler $prepareHelperColumns, + ): JsonResponse { + $result = $prepareHelperColumns($payload); + + $payload->helperColumnsBag->set('helpercolumns', $result['helperColumns']); return $this->adminJson(ApiResponse::ok(['columns' => $result['newData']])); } #[Route('/grid-config-apply-to-all', name: 'gridconfigapplytoall', methods: ['POST'])] - public function gridConfigApplyToAllAction(ApplyGridConfigToAllHandler $applyToAll, Request $request): JsonResponse - { - $applyToAll( - $request->request->getInt('objectId'), - (string) $request->request->get('classId'), - (string) $request->request->get('searchType'), - ); + public function gridConfigApplyToAllAction( + ApplyGridConfigToAllPayload $payload, + ApplyGridConfigToAllHandler $applyToAll, + ): JsonResponse { + $applyToAll($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/grid-mark-favourite-column-config', name: 'gridmarkfavouritecolumnconfig', methods: ['POST'])] public function gridMarkFavouriteColumnConfigAction( + MarkDataObjectGridConfigFavouritePayload $payload, MarkDataObjectGridConfigFavouriteHandler $markFavourite, - Request $request, ): JsonResponse { - $result = $markFavourite( - $request->request->getInt('objectId'), - $request->request->get('classId'), - (int) $request->request->get('gridConfigId'), - $request->request->get('searchType'), - (bool) $request->request->get('global'), - $request->request->get('type'), - ); + $result = $markFavourite($payload); return $this->adminJson(ApiResponse::ok(['specializedConfigs' => $result->specializedConfigs])); } #[Route('/grid-save-column-config', name: 'gridsavecolumnconfig', methods: ['POST'])] public function gridSaveColumnConfigAction( + SaveDataObjectGridColumnConfigPayload $payload, SaveDataObjectGridColumnConfigHandler $saveGridColumnConfig, - Request $request, ): JsonResponse { - $gridConfigData = $this->decodeJson($request->request->get('gridconfig')); - $metadata = json_decode($request->request->get('settings'), true); - - $result = $saveGridColumnConfig( - $request->request->getInt('id'), - $request->request->get('class_id'), - $request->request->get('context'), - $request->request->get('searchType'), - $gridConfigData, - $metadata, - ); + $result = $saveGridColumnConfig($payload); return $this->adminJson(ApiResponse::ok([ 'settings' => $result->settings, @@ -211,12 +149,11 @@ public function gridSaveColumnConfigAction( * IMPORTER */ #[Route('/import-upload', name: 'importupload', methods: ['POST'])] - public function importUploadAction(Request $request, ImportUploadHandler $importUpload): JsonResponse - { - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - - $importUpload(file_get_contents($file->getPathname()), (string) $request->request->get('importId')); + public function importUploadAction( + ImportUploadPayload $payload, + ImportUploadHandler $importUpload, + ): JsonResponse { + $importUpload($payload); $response = $this->adminJson(ApiResponse::ok()); @@ -228,45 +165,31 @@ public function importUploadAction(Request $request, ImportUploadHandler $import } #[Route('/get-export-jobs', name: 'getexportjobs', methods: ['POST'])] - public function getExportJobsAction(Request $request, GetExportJobsHandler $handler): JsonResponse - { - $requestedLanguage = $this->extractLanguage($request); - $allParams = [...$request->request->all(), ...$request->query->all()]; + public function getExportJobsAction( + GetExportJobsPayload $payload, + GetExportJobsHandler $handler, + Request $request, + ): JsonResponse { + if ($payload->requestedLanguage !== $request->getLocale()) { + $request->setLocale($payload->requestedLanguage); + } - $result = $handler($allParams, $requestedLanguage); + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs, 'fileHandle' => $result->fileHandle])); } #[Route('/do-export', name: 'doexport', methods: ['POST'])] - public function doExportAction(DoDataObjectExportHandler $doExport, Request $request): JsonResponse - { - $fileHandle = File::getValidFilename($request->request->get('fileHandle')); - $settings = json_decode($request->request->get('settings'), true); - $fields = json_decode($request->request->all('fields')[0], true); - - $allParams = [...$request->request->all(), ...$request->query->all()]; - - $context = ['source' => 'opendxp-export']; - $contextFromRequest = $request->request->get('context'); - if ($contextFromRequest) { - $context = [...$context, ...json_decode($contextFromRequest, true)]; + public function doExportAction( + DoDataObjectExportPayload $payload, + DoDataObjectExportHandler $doExport, + Request $request, + ): JsonResponse { + if ($payload->requestedLanguage !== $request->getLocale()) { + $request->setLocale($payload->requestedLanguage); } - $doExport( - $fileHandle, - $request->request->all('ids'), - (string) $request->request->get('classId'), - $settings['delimiter'] ?? ';', - $settings['header'] ?? 'title', - $request->request->get('userTimezone'), - $allParams, - $this->extractLanguage($request), - $fields, - (bool) $request->request->get('initial'), - (bool) ($settings['enableInheritance'] ?? false), - $context, - ); + $doExport($payload); return $this->adminJson(ApiResponse::ok()); } @@ -294,52 +217,41 @@ public function downloadXlsxFileAction( } #[Route('/get-batch-jobs', name: 'getbatchjobs', methods: ['POST'])] - public function getBatchJobsAction(Request $request, GetBatchJobsHandler $handler): JsonResponse - { - if ($request->request->get('language')) { - $request->setLocale($request->request->get('language')); + public function getBatchJobsAction( + GetBatchJobsPayload $payload, + GetBatchJobsHandler $handler, + Request $request, + ): JsonResponse { + if ($payload->locale !== $request->getLocale()) { + $request->setLocale($payload->locale); } - $allParams = [...$request->request->all(), ...$request->query->all()]; - $result = $handler($allParams, $request->getLocale()); + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs])); } #[Route('/batch', name: 'batch', methods: ['PUT'])] - public function batchAction(Request $request, ExecuteBatchHandler $handler): JsonResponse - { - if ($request->request->has('data')) { - $params = $this->decodeJson($request->request->get('data'), true); - $saved = $handler($params, $request->getLocale()); - - return $this->adminJson(ApiResponse::fromBool($saved)); + public function batchAction( + ExecuteBatchPayload $payload, + ExecuteBatchHandler $handler, + ): JsonResponse { + if (!$payload->hasData) { + return $this->adminJson(ApiResponse::ok()); } - return $this->adminJson(ApiResponse::ok()); + $saved = $handler($payload); + + return $this->adminJson(ApiResponse::fromBool($saved)); } #[Route('/get-available-visible-vields', name: 'getavailablevisiblefields', methods: ['GET'])] public function getAvailableVisibleFieldsAction( + GetAvailableVisibleFieldsPayload $payload, GetAvailableVisibleFieldsHandler $getAvailableFields, - #[MapQueryParameter] ?string $classes = null, ): JsonResponse { - $result = $getAvailableFields($classes); + $result = $getAvailableFields($payload); return $this->adminJson(['availableFields' => $result->availableFields]); } - - private function extractLanguage(Request $request): string - { - $requestedLanguage = $request->request->get('language'); - if ($requestedLanguage) { - if ($requestedLanguage !== 'default') { - $request->setLocale($requestedLanguage); - } - } else { - $requestedLanguage = $request->getLocale(); - } - - return $requestedLanguage; - } } diff --git a/src/Controller/Admin/DataObject/DataObjectVersionController.php b/src/Controller/Admin/DataObject/DataObjectVersionController.php index 2ba49e56..f2e60285 100644 --- a/src/Controller/Admin/DataObject/DataObjectVersionController.php +++ b/src/Controller/Admin/DataObject/DataObjectVersionController.php @@ -19,13 +19,15 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\DiffVersionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PreviewVersionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PublishVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\DiffVersions\DiffVersionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\DiffVersions\DiffVersionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PreviewVersion\PreviewVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PublishVersion\PublishVersionHandler; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Tool; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; @@ -41,9 +43,9 @@ class DataObjectVersionController extends AdminAbstractController { #[Route('/publish-version', name: 'publishversion', methods: ['POST'])] - public function publishVersionAction(Request $request, PublishVersionHandler $publishVersion): JsonResponse + public function publishVersionAction(IdBodyPayload $payload, PublishVersionHandler $publishVersion): JsonResponse { - $result = $publishVersion($request->request->getInt('id')); + $result = $publishVersion($payload); return $this->adminJson(ApiResponse::ok([ 'general' => ['modificationDate' => $result->modificationDate], @@ -55,11 +57,11 @@ public function publishVersionAction(Request $request, PublishVersionHandler $pu public function previewVersionAction( Environment $twig, PreviewVersionHandler $previewVersion, - #[MapQueryParameter] int $id = 0, + IdQueryPayload $payload, #[MapQueryParameter] ?string $userTimezone = null, ): Response { - $result = $previewVersion($id); + $result = $previewVersion($payload); Tool\UserTimezone::setUserTimezone($userTimezone); if ($timezone = Tool\UserTimezone::getUserTimezone()) { @@ -77,12 +79,11 @@ public function previewVersionAction( public function diffVersionsAction( Environment $twig, DiffVersionsHandler $diffVersions, - int $from, - int $to, + DiffVersionsPayload $payload, #[MapQueryParameter] ?string $userTimezone = null, ): Response { - $result = $diffVersions($from, $to); + $result = $diffVersions($payload); Tool\UserTimezone::setUserTimezone($userTimezone); if ($timezone = Tool\UserTimezone::getUserTimezone()) { diff --git a/src/Controller/Admin/DataObject/FieldCollectionController.php b/src/Controller/Admin/DataObject/FieldCollectionController.php index 16046aca..bde1fd3e 100644 --- a/src/Controller/Admin/DataObject/FieldCollectionController.php +++ b/src/Controller/Admin/DataObject/FieldCollectionController.php @@ -21,18 +21,23 @@ use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\DeleteFieldCollectionHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollection\ExportFieldCollectionPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollection\GetFieldCollectionPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionList\GetFieldCollectionListPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTree\GetFieldCollectionTreePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionUsages\GetFieldCollectionUsagesPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ImportFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ImportFieldCollection\ImportFieldCollectionPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\UpdateFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\UpdateFieldCollection\UpdateFieldCollectionPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -44,9 +49,9 @@ class FieldCollectionController extends AdminAbstractController { #[Route('/fieldcollection-get', name: 'fieldcollectionget', methods: ['GET'])] - public function fieldcollectionGetAction(GetFieldCollectionHandler $getFieldCollection, #[MapQueryParameter] string $id): JsonResponse + public function fieldcollectionGetAction(GetFieldCollectionHandler $getFieldCollection, GetFieldCollectionPayload $payload): JsonResponse { - $result = $getFieldCollection($id); + $result = $getFieldCollection($payload); $data = $result->data; $data['isWriteable'] = $result->isWriteable; @@ -54,44 +59,27 @@ public function fieldcollectionGetAction(GetFieldCollectionHandler $getFieldColl } #[Route('/fieldcollection-update', name: 'fieldcollectionupdate', methods: ['PUT', 'POST'])] - public function fieldcollectionUpdateAction(UpdateFieldCollectionHandler $updateFieldCollection, Request $request): JsonResponse + public function fieldcollectionUpdateAction(UpdateFieldCollectionHandler $updateFieldCollection, UpdateFieldCollectionPayload $payload): JsonResponse { - $fcDef = $updateFieldCollection( - (string) $request->request->get('key'), - (string) $request->request->get('title'), - (string) $request->request->get('group'), - $request->request->get('task') === 'add', - $request->request->has('values') ? $this->decodeJson($request->request->get('values')) : null, - $request->request->has('configuration') ? $this->decodeJson($request->request->get('configuration')) : null, - ); + $fcDef = $updateFieldCollection($payload); return $this->adminJson(ApiResponse::ok(['id' => $fcDef->getKey()])); } #[Route('/fieldcollection-delete', name: 'fieldcollectiondelete', methods: ['DELETE'])] - public function fieldcollectionDeleteAction(DeleteFieldCollectionHandler $deleteFieldCollection, Request $request): JsonResponse + public function fieldcollectionDeleteAction(DeleteFieldCollectionHandler $deleteFieldCollection, StringIdBodyPayload $payload): JsonResponse { - $deleteFieldCollection((string) $request->request->get('id')); + $deleteFieldCollection($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/fieldcollection-tree', name: 'fieldcollectiontree', methods: ['GET', 'POST'])] - public function fieldcollectionTreeAction( - GetFieldCollectionTreeHandler $getTree, - #[MapQueryParameter] ?string $forObjectEditor = null, - #[MapQueryParameter] ?string $allowedTypes = null, - #[MapQueryParameter(name: 'object_id')] int $objectId = 0, - #[MapQueryParameter] ?string $layoutId = null, - ): JsonResponse { - $result = $getTree( - $forObjectEditor !== null, - $allowedTypes !== null ? explode(',', $allowedTypes) : null, - $objectId, - $layoutId, - ); - - if ($forObjectEditor) { + public function fieldcollectionTreeAction(GetFieldCollectionTreeHandler $getTree, GetFieldCollectionTreePayload $payload): JsonResponse + { + $result = $getTree($payload); + + if ($payload->forObjectEditor) { return $this->adminJson(['fieldcollections' => $result->definitions, 'layoutDefinitions' => $result->layoutDefinitions]); } @@ -99,29 +87,17 @@ public function fieldcollectionTreeAction( } #[Route('/fieldcollection-list', name: 'fieldcollectionlist', methods: ['GET'])] - public function fieldcollectionListAction( - GetFieldCollectionListHandler $getList, - #[MapQueryParameter] ?string $layoutId = null, - #[MapQueryParameter] ?string $allowedTypes = null, - #[MapQueryParameter(name: 'field_name')] ?string $fieldName = null, - #[MapQueryParameter(name: 'object_id')] int $objectId = 0, - ): JsonResponse { - $result = $getList( - $allowedTypes !== null ? explode(',', $allowedTypes) : null, - $fieldName, - $objectId, - $layoutId, - ); + public function fieldcollectionListAction(GetFieldCollectionListHandler $getList, GetFieldCollectionListPayload $payload): JsonResponse + { + $result = $getList($payload); return $this->adminJson(['fieldcollections' => $result->fieldcollections]); } #[Route('/import-fieldcollection', name: 'importfieldcollection', methods: ['POST'])] - public function importFieldcollectionAction(ImportFieldCollectionHandler $importFieldCollection, Request $request, #[MapQueryParameter] string $id): Response + public function importFieldcollectionAction(ImportFieldCollectionHandler $importFieldCollection, ImportFieldCollectionPayload $payload): Response { - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $importFieldCollection($id, file_get_contents($file->getPathname())); + $importFieldCollection($payload); $response = $this->adminJson(ApiResponse::ok()); $response->headers->set('Content-Type', 'text/html'); @@ -130,9 +106,9 @@ public function importFieldcollectionAction(ImportFieldCollectionHandler $import } #[Route('/export-fieldcollection', name: 'exportfieldcollection', methods: ['GET'])] - public function exportFieldcollectionAction(ExportFieldCollectionHandler $exportFieldCollection, #[MapQueryParameter] string $id): Response + public function exportFieldcollectionAction(ExportFieldCollectionHandler $exportFieldCollection, ExportFieldCollectionPayload $payload): Response { - $result = $exportFieldCollection($id); + $result = $exportFieldCollection($payload); $response = new Response($result->json); $response->headers->set('Content-type', 'application/json'); @@ -142,10 +118,8 @@ public function exportFieldcollectionAction(ExportFieldCollectionHandler $export } #[Route('/get-fieldcollection-usages', name: 'getfieldcollectionusages', methods: ['GET'])] - public function getFieldcollectionUsagesAction( - GetFieldCollectionUsagesHandler $getUsages, - #[MapQueryParameter] string $key, - ): Response { - return $this->adminJson($getUsages($key)); + public function getFieldcollectionUsagesAction(GetFieldCollectionUsagesHandler $getUsages, GetFieldCollectionUsagesPayload $payload): Response + { + return $this->adminJson($getUsages($payload)); } } diff --git a/src/Controller/Admin/DataObject/ObjectBrickController.php b/src/Controller/Admin/DataObject/ObjectBrickController.php index 18454411..3a569935 100644 --- a/src/Controller/Admin/DataObject/ObjectBrickController.php +++ b/src/Controller/Admin/DataObject/ObjectBrickController.php @@ -21,18 +21,23 @@ use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\DeleteObjectBrickHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrick\ExportObjectBrickPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsages\GetBrickUsagesPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrick\GetObjectBrickPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickList\GetObjectBrickListPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTree\GetObjectBrickTreePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ImportObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ImportObjectBrick\ImportObjectBrickPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\UpdateObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\UpdateObjectBrick\UpdateObjectBrickPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -44,9 +49,9 @@ class ObjectBrickController extends AdminAbstractController { #[Route('/objectbrick-get', name: 'objectbrickget', methods: ['GET'])] - public function objectbrickGetAction(GetObjectBrickHandler $getObjectBrick, #[MapQueryParameter] string $id): JsonResponse + public function objectbrickGetAction(GetObjectBrickHandler $getObjectBrick, GetObjectBrickPayload $payload): JsonResponse { - $result = $getObjectBrick($id); + $result = $getObjectBrick($payload); $data = $result->data; $data['isWriteable'] = $result->isWriteable; @@ -54,46 +59,27 @@ public function objectbrickGetAction(GetObjectBrickHandler $getObjectBrick, #[Ma } #[Route('/objectbrick-update', name: 'objectbrickupdate', methods: ['PUT', 'POST'])] - public function objectbrickUpdateAction(UpdateObjectBrickHandler $updateObjectBrick, Request $request): JsonResponse + public function objectbrickUpdateAction(UpdateObjectBrickHandler $updateObjectBrick, UpdateObjectBrickPayload $payload): JsonResponse { - $brickDef = $updateObjectBrick( - (string) $request->request->get('key'), - (string) $request->request->get('title'), - (string) $request->request->get('group'), - $request->request->get('task') === 'add', - $request->request->has('values') ? $this->decodeJson($request->request->get('values')) : null, - $request->request->has('configuration') ? $this->decodeJson($request->request->get('configuration')) : null, - ); + $brickDef = $updateObjectBrick($payload); return $this->adminJson(ApiResponse::ok(['id' => $brickDef->getKey()])); } #[Route('/objectbrick-delete', name: 'objectbrickdelete', methods: ['DELETE'])] - public function objectbrickDeleteAction(DeleteObjectBrickHandler $deleteObjectBrick, Request $request): JsonResponse + public function objectbrickDeleteAction(DeleteObjectBrickHandler $deleteObjectBrick, StringIdBodyPayload $payload): JsonResponse { - $deleteObjectBrick((string) $request->request->get('id')); + $deleteObjectBrick($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/objectbrick-tree', name: 'objectbricktree', methods: ['GET', 'POST'])] - public function objectbrickTreeAction( - GetObjectBrickTreeHandler $getTree, - #[MapQueryParameter] ?string $forObjectEditor = null, - #[MapQueryParameter(name: 'object_id')] int $objectId = 0, - #[MapQueryParameter(name: 'class_id')] ?string $classId = null, - #[MapQueryParameter(name: 'field_name')] ?string $fieldName = null, - #[MapQueryParameter] ?string $layoutId = null, - ): JsonResponse { - $result = $getTree( - $forObjectEditor !== null, - $objectId, - $classId, - $fieldName, - $layoutId, - ); - - if ($forObjectEditor) { + public function objectbrickTreeAction(GetObjectBrickTreeHandler $getTree, GetObjectBrickTreePayload $payload): JsonResponse + { + $result = $getTree($payload); + + if ($payload->forObjectEditor) { return $this->adminJson(['objectbricks' => $result->definitions, 'layoutDefinitions' => $result->layoutDefinitions]); } @@ -101,24 +87,17 @@ public function objectbrickTreeAction( } #[Route('/objectbrick-list', name: 'objectbricklist', methods: ['GET'])] - public function objectbrickListAction( - GetObjectBrickListHandler $getList, - #[MapQueryParameter(name: 'class_id')] ?string $classId = null, - #[MapQueryParameter(name: 'field_name')] ?string $fieldName = null, - #[MapQueryParameter] ?string $layoutId = null, - #[MapQueryParameter(name: 'object_id')] int $objectId = 0, - ): JsonResponse { - $result = $getList($classId, $fieldName, $layoutId, $objectId); + public function objectbrickListAction(GetObjectBrickListHandler $getList, GetObjectBrickListPayload $payload): JsonResponse + { + $result = $getList($payload); return $this->adminJson(['objectbricks' => $result->objectbricks]); } #[Route('/import-objectbrick', name: 'importobjectbrick', methods: ['POST'])] - public function importObjectbrickAction(ImportObjectBrickHandler $importObjectBrick, Request $request, #[MapQueryParameter] string $id): JsonResponse + public function importObjectbrickAction(ImportObjectBrickHandler $importObjectBrick, ImportObjectBrickPayload $payload): JsonResponse { - /** @var UploadedFile $file */ - $file = $request->files->get('Filedata'); - $importObjectBrick($id, file_get_contents($file->getPathname())); + $importObjectBrick($payload); $response = $this->adminJson(ApiResponse::ok()); $response->headers->set('Content-Type', 'text/html'); @@ -126,9 +105,9 @@ public function importObjectbrickAction(ImportObjectBrickHandler $importObjectBr } #[Route('/export-objectbrick', name: 'exportobjectbrick', methods: ['GET'])] - public function exportObjectbrickAction(ExportObjectBrickHandler $exportObjectBrick, #[MapQueryParameter] string $id): Response + public function exportObjectbrickAction(ExportObjectBrickHandler $exportObjectBrick, ExportObjectBrickPayload $payload): Response { - $result = $exportObjectBrick($id); + $result = $exportObjectBrick($payload); $response = new Response($result->json); $response->headers->set('Content-type', 'application/json'); @@ -138,8 +117,8 @@ public function exportObjectbrickAction(ExportObjectBrickHandler $exportObjectBr } #[Route('/get-bricks-usages', name: 'getbrickusages', methods: ['GET'])] - public function getBrickUsagesAction(GetBrickUsagesHandler $getBrickUsages, #[MapQueryParameter] string $classId): Response + public function getBrickUsagesAction(GetBrickUsagesHandler $getBrickUsages, GetBrickUsagesPayload $payload): Response { - return $this->adminJson($getBrickUsages($classId)->usages); + return $this->adminJson($getBrickUsages($payload)->usages); } } diff --git a/src/Controller/Admin/DataObject/QuantityValueController.php b/src/Controller/Admin/DataObject/QuantityValueController.php index 0307e8ca..0d0b23ee 100644 --- a/src/Controller/Admin/DataObject/QuantityValueController.php +++ b/src/Controller/Admin/DataObject/QuantityValueController.php @@ -19,16 +19,22 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValuesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValues\ConvertAllQuantityValuesPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValueHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValue\ConvertQuantityValuePayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\CreateQuantityValueUnit\CreateQuantityValueUnitHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\DeleteQuantityValueUnit\DeleteQuantityValueUnitHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ExportQuantityValueUnitsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitList\GetQuantityValueUnitListPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnits\GetQuantityValueUnitsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnitsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ManageQuantityValueUnitHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnits\ImportQuantityValueUnitsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\QuantityValueUnitPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\UpdateQuantityValueUnit\UpdateQuantityValueUnitHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -42,12 +48,9 @@ class QuantityValueController extends AdminAbstractController { #[Route('/unit-import', name: 'unitimport', methods: ['POST', 'PUT'])] - public function unitImportAction(Request $request, ImportQuantityValueUnitsHandler $importUnits): JsonResponse + public function unitImportAction(ImportQuantityValueUnitsPayload $payload, ImportQuantityValueUnitsHandler $importUnits): JsonResponse { - /** @var UploadedFile $uploadFile */ - $uploadFile = $request->files->get('Filedata'); - - $success = $importUnits(file_get_contents($uploadFile->getPathname())); + $success = $importUnits($payload); $response = $this->adminJson(ApiResponse::fromBool($success)); $response->headers->set('Content-Type', 'text/html'); @@ -66,14 +69,9 @@ public function unitExportAction(ExportQuantityValueUnitsHandler $exportUnits): #[Route('/unit-proxy', name: 'unitproxyget', methods: ['GET'])] #[IsGranted(CorePermission::QuantityValueUnits->value)] - public function unitProxyGetAction( - GetQuantityValueUnitsHandler $getUnits, - Request $request, - #[MapQueryParameter] int $limit = 25, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] ?string $filter = null, - ): JsonResponse { - $result = $getUnits($request->query->all(), $limit, $start, $filter); + public function unitProxyGetAction(GetQuantityValueUnitsHandler $getUnits, GetQuantityValueUnitsPayload $payload): JsonResponse + { + $result = $getUnits($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } @@ -81,51 +79,42 @@ public function unitProxyGetAction( #[Route('/unit-proxy', name: 'unitproxy', methods: ['POST', 'PUT'])] #[IsGranted(CorePermission::QuantityValueUnits->value)] public function unitProxyAction( - ManageQuantityValueUnitHandler $manageUnit, - Request $request, + QuantityValueUnitPayload $payload, + CreateQuantityValueUnitHandler $createUnit, + UpdateQuantityValueUnitHandler $updateUnit, + DeleteQuantityValueUnitHandler $deleteUnit, #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - if (!$request->request->has('data')) { - throw new BadRequestHttpException(); - } - - $data = json_decode($request->request->get('data'), true); - $result = $manageUnit((string) $xaction, $data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); + return match ($xaction) { + 'destroy' => $this->adminJson(ApiResponse::ok(['data' => $deleteUnit($payload)->data])), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $updateUnit($payload)->data])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $createUnit($payload)->data])), + default => throw new BadRequestHttpException(), + }; } #[Route('/unit-list', name: 'unitlist', methods: ['GET'])] - public function unitListAction( - GetQuantityValueUnitListHandler $getUnitList, - #[MapQueryParameter] ?string $filter = null, - ): JsonResponse { - $result = $getUnitList($filter); + public function unitListAction(GetQuantityValueUnitListHandler $getUnitList, GetQuantityValueUnitListPayload $payload): JsonResponse + { + $result = $getUnitList($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[Route('/convert', name: 'convert', methods: ['GET'])] #[IsGranted(CorePermission::Objects->value)] - public function convertAction( - ConvertQuantityValueHandler $convert, - #[MapQueryParameter] ?string $fromUnit = null, - #[MapQueryParameter] ?string $toUnit = null, - #[MapQueryParameter] ?string $value = null, - ): JsonResponse { - $result = $convert($fromUnit, $toUnit, $value); + public function convertAction(ConvertQuantityValueHandler $convert, ConvertQuantityValuePayload $payload): JsonResponse + { + $result = $convert($payload); return $this->adminJson(ApiResponse::ok(['value' => $result->value])); } #[Route('/convert-all', name: 'convertall', methods: ['GET'])] #[IsGranted(CorePermission::Objects->value)] - public function convertAllAction( - ConvertAllQuantityValuesHandler $convertAll, - #[MapQueryParameter] ?string $unit = null, - #[MapQueryParameter] ?string $value = null, - ): JsonResponse { - $result = $convertAll($unit, $value); + public function convertAllAction(ConvertAllQuantityValuesHandler $convertAll, ConvertAllQuantityValuesPayload $payload): JsonResponse + { + $result = $convertAll($payload); return $this->adminJson(ApiResponse::ok([ 'value' => $result->value, diff --git a/src/Controller/Admin/DataObject/VariantsController.php b/src/Controller/Admin/DataObject/VariantsController.php index 4b468ddb..75a93ab3 100644 --- a/src/Controller/Admin/DataObject/VariantsController.php +++ b/src/Controller/Admin/DataObject/VariantsController.php @@ -19,7 +19,9 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariantsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariants\GetVariantsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKeyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKey\UpdateObjectKeyPayload; use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -32,12 +34,9 @@ class VariantsController extends AdminAbstractController { #[Route('/update-key', name: 'updatekey', methods: ['PUT'])] - public function updateKeyAction(UpdateObjectKeyHandler $updateObjectKey, Request $request): JsonResponse + public function updateKeyAction(UpdateObjectKeyHandler $updateObjectKey, UpdateObjectKeyPayload $payload): JsonResponse { - $result = $updateObjectKey( - $request->request->getInt('id'), - $request->request->get('key'), - ); + $result = $updateObjectKey($payload); return $this->adminJson($result->data); } @@ -45,24 +44,17 @@ public function updateKeyAction(UpdateObjectKeyHandler $updateObjectKey, Request #[Route('/get-variants', name: 'getvariants', methods: ['POST'])] public function getVariantsAction( GetVariantsHandler $getVariants, + GetVariantsPayload $payload, Request $request, CsrfProtectionHandler $csrfProtection, ): JsonResponse { $csrfProtection->checkCsrfToken($request); - $allParams = [...$request->request->all(), ...$request->query->all()]; - $requestedLanguage = $allParams['language'] ?? null; - if ($requestedLanguage && $requestedLanguage !== 'default') { - $request->setLocale($requestedLanguage); - } else { - $requestedLanguage = $request->getLocale(); + if ($payload->requestedLanguage !== $request->getLocale()) { + $request->setLocale($payload->requestedLanguage); } - $result = $getVariants( - (int) $request->request->get('objectId'), - $allParams, - $requestedLanguage, - ); + $result = $getVariants($payload); return $this->adminJson($result->data); } diff --git a/src/Controller/Admin/Document/DocumentController.php b/src/Controller/Admin/Document/DocumentController.php index f1bde27b..5fba39c7 100644 --- a/src/Controller/Admin/Document/DocumentController.php +++ b/src/Controller/Admin/Document/DocumentController.php @@ -20,29 +20,49 @@ use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; use OpenDxp\Controller\Traits\ElementEditLockHelperTrait; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocument\AddDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocument\AddDocumentPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\ConvertDocument\ConvertDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\ConvertDocument\ConvertDocumentPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\DeleteDocument\DeleteDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\DeleteDocument\DeleteDocumentPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\DocTypes\CreateDocType\CreateDocTypeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\DocTypes\DeleteDocType\DeleteDocTypeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\DocTypes\DocTypePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\DocTypes\UpdateDocType\UpdateDocTypeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocTypesByType\GetDocTypesByTypeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocTypesByType\GetDocTypesByTypePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\DocTypes\GetDocTypesList\GetDocTypesListHandler; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData\GetDocumentDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData\GetDocumentDataPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentIdForPath\GetDocumentIdForPathHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentIdForPath\GetDocumentIdForPathPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\GetSiteCustomSettings\GetSiteCustomSettingsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\GetSiteCustomSettings\GetSiteCustomSettingsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\RemoveSite\RemoveSiteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\RemoveSite\RemoveSitePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\UpdateSite\UpdateSiteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\UpdateSite\UpdateSitePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\AddDocumentTranslation\AddDocumentTranslationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\AddDocumentTranslation\AddDocumentTranslationPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\CheckTranslationLanguage\CheckTranslationLanguageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\CheckTranslationLanguage\CheckTranslationLanguagePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\DetermineTranslationParent\DetermineTranslationParentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\DetermineTranslationParent\DetermineTranslationParentPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTree\GetLanguageTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTree\GetLanguageTreePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTreeRoot\GetLanguageTreeRootHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTreeRoot\GetLanguageTreeRootPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\RemoveDocumentTranslation\RemoveDocumentTranslationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\RemoveDocumentTranslation\RemoveDocumentTranslationPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildren\TreeGetDocumentChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildren\TreeGetDocumentChildrenPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateDocument\UpdateDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateDocument\UpdateDocumentPayload; use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfoHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\ConvertDocumentHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\DeleteDocumentHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildrenHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentIdForPathHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocTypesByTypeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocTypesListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\ManageDocTypesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\GetSiteCustomSettingsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Site\RemoveSiteHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\AddDocumentTranslationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\CheckTranslationLanguageHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\DetermineTranslationParentHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTreeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTreeRootHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\RemoveDocumentTranslationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateDocumentHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateSiteHandler; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Model\Element\ElementInterface; -use OpenDxp\Tool; use Override; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -58,9 +78,8 @@ class DocumentController extends ElementControllerBase { use ElementEditLockHelperTrait; - public function __construct( - ElementServiceInterface $elementService, - ) { + public function __construct(ElementServiceInterface $elementService) + { parent::__construct($elementService); } @@ -90,11 +109,11 @@ public function deleteInfoAction( #[Route('/get-data-by-id', name: 'opendxp_admin_document_document_getdatabyid', methods: ['GET'])] public function getDataByIdAction( GetDocumentDataHandler $handler, - #[MapQueryParameter] int $id = 0, + GetDocumentDataPayload $payload, ): JsonResponse { try { - $result = $handler($id); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } @@ -106,11 +125,10 @@ public function getDataByIdAction( #[Route('/tree-get-children-by-id', name: 'opendxp_admin_document_document_treegetchildrenbyid', methods: ['GET'])] public function treeGetChildrenByIdAction( TreeGetDocumentChildrenHandler $handler, - Request $request, - #[MapQueryParameter] int $inSearch = 0, + TreeGetDocumentChildrenPayload $payload, ): JsonResponse { - $result = $handler($request->query->all()); + $result = $handler($payload); if ($result->paginated) { return $this->adminJson([ @@ -119,7 +137,7 @@ public function treeGetChildrenByIdAction( 'total' => $result->total, 'nodes' => $result->documents, 'filter' => $result->filter ?: '', - 'inSearch' => $inSearch, + 'inSearch' => $payload->inSearch, ]); } @@ -128,19 +146,12 @@ public function treeGetChildrenByIdAction( #[IsGranted(CorePermission::Documents->value)] #[Route('/add', name: 'opendxp_admin_document_document_add', methods: ['POST'])] - public function addAction(Request $request, AddDocumentHandler $handler): JsonResponse + public function addAction( + AddDocumentPayload $payload, + AddDocumentHandler $handler, + ): JsonResponse { - $result = $handler( - parentId: $request->request->getInt('parentId'), - type: $request->request->getString('type'), - key: $request->request->getString('key'), - docTypeId: $request->request->get('docTypeId'), - translationsBaseDocumentId: $request->request->get('translationsBaseDocument'), - language: $request->request->get('language'), - inheritanceSource: $request->request->has('inheritanceSource') ? $request->request->get('inheritanceSource') : null, - title: $request->request->get('title'), - name: $request->request->get('name'), - ); + $result = $handler($payload); return $this->adminJson(ApiResponse::ok([ 'id' => $result->document->getId(), @@ -150,15 +161,14 @@ public function addAction(Request $request, AddDocumentHandler $handler): JsonRe #[IsGranted(CorePermission::Documents->value)] #[Route('/delete', name: 'opendxp_admin_document_document_delete', methods: ['DELETE'])] - public function deleteAction(Request $request, DeleteDocumentHandler $handler): JsonResponse + public function deleteAction( + DeleteDocumentPayload $payload, + DeleteDocumentHandler $handler, + ): JsonResponse { - $type = $request->request->getString('type'); - $id = $request->request->getInt('id'); - $amount = $request->request->getInt('amount'); - - $result = $handler($type, $id, $amount); + $result = $handler($payload); - if ($type === 'children') { + if ($payload->type === 'children') { return $this->adminJson(ApiResponse::ok(['deleted' => $result->deleted])); } @@ -167,19 +177,23 @@ public function deleteAction(Request $request, DeleteDocumentHandler $handler): #[IsGranted(CorePermission::Documents->value)] #[Route('/update', name: 'opendxp_admin_document_document_update', methods: ['PUT'])] - public function updateAction(Request $request, UpdateDocumentHandler $handler): JsonResponse + public function updateAction( + UpdateDocumentPayload $payload, + UpdateDocumentHandler $handler, + ): JsonResponse { - $updateData = [...$request->request->all(), ...$request->query->all()]; - - $result = $handler((int) $request->request->get('id'), $updateData); + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } #[Route('/doc-types', name: 'opendxp_admin_document_document_doctypesget', methods: ['GET'])] - public function docTypesGetAction(GetDocTypesListHandler $getDocTypesList): JsonResponse + public function docTypesGetAction( + EmptyPayload $payload, + GetDocTypesListHandler $getDocTypesList, + ): JsonResponse { - $result = $getDocTypesList(); + $result = $getDocTypesList($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->docTypes, 'total' => $result->total])); } @@ -188,76 +202,65 @@ public function docTypesGetAction(GetDocTypesListHandler $getDocTypesList): Json #[IsGranted(CorePermission::DocumentTypes->value)] #[Route('/doc-types', name: 'opendxp_admin_document_document_doctypes', methods: ['PUT', 'POST', 'DELETE'])] public function docTypesAction( - Request $request, - ManageDocTypesHandler $handler, + DocTypePayload $payload, + DeleteDocTypeHandler $delete, + UpdateDocTypeHandler $update, + CreateDocTypeHandler $create, #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - if ($request->request->get('data')) { - $data = $this->decodeJson($request->request->get('data')); - $result = $handler($xaction, $data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); - } - - return $this->adminJson(false); + return match ($xaction) { + 'destroy' => $this->adminJson(ApiResponse::ok(['data' => $delete($payload)->data])), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $update($payload)->data])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $create($payload)->data])), + default => $this->adminJson(false), + }; } #[IsGranted(CorePermission::Documents->value)] #[Route('/get-doc-types', name: 'opendxp_admin_document_document_getdoctypes', methods: ['GET'])] public function getDocTypesAction( + GetDocTypesByTypePayload $payload, GetDocTypesByTypeHandler $getDocTypesByType, - #[MapQueryParameter] ?string $type = null, ): JsonResponse { - $result = $getDocTypesByType($type); + $result = $getDocTypesByType($payload); return $this->adminJson(['docTypes' => $result->docTypes]); } #[IsGranted(CorePermission::Documents->value)] #[Route('/get-site-custom-settings', name: 'opendxp_admin_document_document_get_site_custom_settings', methods: ['POST'])] - public function getSiteCustomSettingsAction(Request $request, GetSiteCustomSettingsHandler $getSiteCustomSettings): JsonResponse + public function getSiteCustomSettingsAction( + GetSiteCustomSettingsPayload $payload, + GetSiteCustomSettingsHandler $getSiteCustomSettings, + ): JsonResponse { - $result = $getSiteCustomSettings($request->request->getInt('id')); + $result = $getSiteCustomSettings($payload); return $this->adminJson(['data' => $result->nodes]); } #[IsGranted(CorePermission::Documents->value)] #[Route('/update-site', name: 'opendxp_admin_document_document_updatesite', methods: ['PUT'])] - public function updateSiteAction(Request $request, UpdateSiteHandler $handler): JsonResponse + public function updateSiteAction( + UpdateSitePayload $payload, + UpdateSiteHandler $handler, + ): JsonResponse { - $domains = $request->request->getString('domains'); - $domains = str_replace(' ', '', $domains); - $domains = $domains ? explode("\n", $domains) : []; - - $localizedErrorDocuments = []; - foreach (Tool::getValidLanguages() as $language) { - $requestValue = $request->request->get(sprintf('errorDocument_localized_%s', $language)); - if (isset($requestValue)) { - $localizedErrorDocuments[$language] = $requestValue; - } - } - - $result = $handler( - rootId: $request->request->getInt('id'), - domains: $domains, - mainDomain: $request->request->getString('mainDomain'), - errorDocument: $request->request->getString('errorDocument'), - localizedErrorDocuments: $localizedErrorDocuments, - redirectToMainDomain: $request->request->getBoolean('redirectToMainDomain'), - requestCustomSettings: $request->request->all(), - ); + $result = $handler($payload); return $this->adminJson($result->siteVars); } #[IsGranted(CorePermission::Documents->value)] #[Route('/remove-site', name: 'opendxp_admin_document_document_removesite', methods: ['DELETE'])] - public function removeSiteAction(Request $request, RemoveSiteHandler $removeSite): JsonResponse + public function removeSiteAction( + RemoveSitePayload $payload, + RemoveSiteHandler $removeSite, + ): JsonResponse { - $removeSite($request->request->getInt('id')); + $removeSite($payload); return $this->adminJson(ApiResponse::ok()); } @@ -265,11 +268,11 @@ public function removeSiteAction(Request $request, RemoveSiteHandler $removeSite #[IsGranted(CorePermission::Documents->value)] #[Route('/get-id-for-path', name: 'opendxp_admin_document_document_getidforpath', methods: ['GET'])] public function getIdForPathAction( + GetDocumentIdForPathPayload $payload, GetDocumentIdForPathHandler $getDocumentIdForPath, - #[MapQueryParameter] ?string $path = null, ): JsonResponse { - $result = $getDocumentIdForPath($path); + $result = $getDocumentIdForPath($payload); if (!$result) { return $this->adminJson(false); } @@ -280,12 +283,11 @@ public function getIdForPathAction( #[IsGranted(CorePermission::Documents->value)] #[Route('/language-tree', name: 'opendxp_admin_document_document_languagetree', methods: ['GET'])] public function languageTreeAction( + GetLanguageTreePayload $payload, GetLanguageTreeHandler $handler, - #[MapQueryParameter] int $node = 0, - #[MapQueryParameter] ?string $languages = null, ): JsonResponse { - $result = $handler($node, explode(',', (string) $languages)); + $result = $handler($payload); return $this->adminJson($result->nodes); } @@ -293,11 +295,11 @@ public function languageTreeAction( #[IsGranted(CorePermission::Documents->value)] #[Route('/language-tree-root', name: 'opendxp_admin_document_document_languagetreeroot', methods: ['GET'])] public function languageTreeRootAction( + GetLanguageTreeRootPayload $payload, GetLanguageTreeRootHandler $handler, - #[MapQueryParameter] int $id = 0, ): JsonResponse { - $result = $handler($id); + $result = $handler($payload); return $this->adminJson([ 'root' => $result->root, @@ -308,9 +310,12 @@ public function languageTreeRootAction( #[IsGranted(CorePermission::Documents->value)] #[Route('/convert', name: 'opendxp_admin_document_document_convert', methods: ['PUT'])] - public function convertAction(Request $request, ConvertDocumentHandler $handler): JsonResponse + public function convertAction( + ConvertDocumentPayload $payload, + ConvertDocumentHandler $handler, + ): JsonResponse { - $handler((int) $request->request->get('id'), $request->request->get('type')); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -318,12 +323,11 @@ public function convertAction(Request $request, ConvertDocumentHandler $handler) #[IsGranted(CorePermission::Documents->value)] #[Route('/translation-determine-parent', name: 'opendxp_admin_document_document_translationdetermineparent', methods: ['GET'])] public function translationDetermineParentAction( + DetermineTranslationParentPayload $payload, DetermineTranslationParentHandler $handler, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $language = null, ): JsonResponse { - $result = $handler($id, $language); + $result = $handler($payload); return $this->adminJson(ApiResponse::fromBool($result->found, [ 'targetPath' => $result->targetPath, @@ -333,18 +337,24 @@ public function translationDetermineParentAction( #[IsGranted(CorePermission::Documents->value)] #[Route('/translation-add', name: 'opendxp_admin_document_document_translationadd', methods: ['POST'])] - public function translationAddAction(Request $request, AddDocumentTranslationHandler $handler): JsonResponse + public function translationAddAction( + AddDocumentTranslationPayload $payload, + AddDocumentTranslationHandler $handler, + ): JsonResponse { - $handler($request->request->getInt('sourceId'), $request->request->getString('targetPath')); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Documents->value)] #[Route('/translation-remove', name: 'opendxp_admin_document_document_translationremove', methods: ['DELETE'])] - public function translationRemoveAction(Request $request, RemoveDocumentTranslationHandler $handler): JsonResponse + public function translationRemoveAction( + RemoveDocumentTranslationPayload $payload, + RemoveDocumentTranslationHandler $handler, + ): JsonResponse { - $handler($request->request->getInt('sourceId'), $request->request->getInt('targetId')); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -352,11 +362,11 @@ public function translationRemoveAction(Request $request, RemoveDocumentTranslat #[IsGranted(CorePermission::Documents->value)] #[Route('/translation-check-language', name: 'opendxp_admin_document_document_translationchecklanguage', methods: ['GET'])] public function translationCheckLanguageAction( + CheckTranslationLanguagePayload $payload, CheckTranslationLanguageHandler $handler, - #[MapQueryParameter] ?string $path = null, ): JsonResponse { - $result = $handler($path); + $result = $handler($payload); return $this->adminJson(ApiResponse::fromBool($result->found, [ 'language' => $result->language, diff --git a/src/Controller/Admin/Document/DocumentControllerBase.php b/src/Controller/Admin/Document/DocumentControllerBase.php index fd4d619c..41f8d7ef 100644 --- a/src/Controller/Admin/Document/DocumentControllerBase.php +++ b/src/Controller/Admin/Document/DocumentControllerBase.php @@ -20,23 +20,19 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Handler\Document\ChangeMainDocumentHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Document\EmailPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Document\FolderPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Document\HardlinkPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Document\LinkPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Document\PagePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\ChangeMainDocument\ChangeMainDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\ChangeMainDocument\ChangeMainDocumentPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\RemoveFromSession\RemoveFromSessionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\SaveToSession\SaveToSessionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\SaveToSession\SaveToSessionPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper; -use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Controller\Traits\ElementEditLockHelperTrait; use OpenDxp\Model; -use OpenDxp\Model\Document; use OpenDxp\Model\Element\ElementInterface; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -64,45 +60,24 @@ abstract class DocumentControllerBase extends AdminAbstractController public function __construct( protected ElementServiceInterface $elementService, - protected readonly SessionService $sessionService, - protected readonly DocumentPayloadMapper $mapper, ) {} #[Route('/save-to-session', name: 'savetosession', methods: ['POST'])] - public function saveToSessionAction(Request $request): JsonResponse - { - if (!($documentId = (int) $request->request->get('id'))) { - return $this->adminJson(ApiResponse::ok()); - } - - $document = $this->sessionService->getOrLoadDocument($documentId); - if (!$document) { - throw $this->createNotFoundException(); - } - - $document->setInDumpState(true); - - if ($document instanceof Document\Email) { - $this->mapper->applyPagePayload(EmailPayload::fromRequest($request), $document); - } elseif ($document instanceof Document\PageSnippet) { - $this->mapper->applyPagePayload(PagePayload::fromRequest($request), $document); - } elseif ($document instanceof Document\Link) { - $this->mapper->applyLinkPayload(LinkPayload::fromRequest($request), $document); - } elseif ($document instanceof Document\Hardlink) { - $this->mapper->applyHardlinkPayload(HardlinkPayload::fromRequest($request), $document); - } elseif ($document instanceof Document\Folder) { - $this->mapper->applyFolderPayload(FolderPayload::fromRequest($request), $document); - } - - $this->sessionService->saveDocument($document); + public function saveToSessionAction( + SaveToSessionPayload $payload, + SaveToSessionHandler $handler, + ): JsonResponse { + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/remove-from-session', name: 'removefromsession', methods: ['DELETE'])] - public function removeFromSessionAction(Request $request): JsonResponse - { - $this->sessionService->removeDocument((int) $request->request->get('id')); + public function removeFromSessionAction( + IdBodyPayload $payload, + RemoveFromSessionHandler $handler, + ): JsonResponse { + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -111,12 +86,11 @@ public function removeFromSessionAction(Request $request): JsonResponse * This is used for pages and snippets to change the main document (which is not saved with the normal save button) */ #[Route('/change-main-document', name: 'changemaindocument', methods: ['PUT'])] - public function changeMainDocumentAction(Request $request, ChangeMainDocumentHandler $changeMainDocument): JsonResponse - { - $changeMainDocument( - (int) $request->request->get('id'), - (string) $request->request->get('contentMainDocumentPath'), - ); + public function changeMainDocumentAction( + ChangeMainDocumentPayload $payload, + ChangeMainDocumentHandler $changeMainDocument, + ): JsonResponse { + $changeMainDocument($payload); return $this->adminJson(ApiResponse::ok()); } @@ -135,6 +109,7 @@ protected function preSendDataActions(array $data, Model\Document $document): Js 'data' => $data, 'document' => $document, ]); + OpenDxp::getEventDispatcher()->dispatch($event, AdminEvents::DOCUMENT_GET_PRE_SEND_DATA); $data = $event->getArgument('data'); diff --git a/src/Controller/Admin/Document/DocumentCopyController.php b/src/Controller/Admin/Document/DocumentCopyController.php index c0417d39..40b63be6 100644 --- a/src/Controller/Admin/Document/DocumentCopyController.php +++ b/src/Controller/Admin/Document/DocumentCopyController.php @@ -19,9 +19,12 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyDocumentHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\GetDocumentChildIdsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\RewriteDocumentIdsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyDocument\CopyDocumentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyDocument\CopyDocumentPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\GetDocumentChildIds\GetDocumentChildIdsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\GetDocumentChildIds\GetDocumentChildIdsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\RewriteDocumentIds\RewriteDocumentIdsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\RewriteDocumentIds\RewriteDocumentIdsPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Tool\Session; use Symfony\Component\HttpFoundation\JsonResponse; @@ -40,10 +43,10 @@ class DocumentCopyController extends AdminAbstractController { #[Route('/copy-info', name: 'opendxp_admin_document_document_copyinfo', methods: ['GET'])] public function copyInfoAction( + GetDocumentChildIdsPayload $getChildIdsPayload, GetDocumentChildIdsHandler $getChildIds, Request $request, #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter] int $sourceId = 0, #[MapQueryParameter] ?string $targetId = null, #[MapQueryParameter] ?string $language = null, #[MapQueryParameter] ?string $enableInheritance = null, @@ -55,6 +58,8 @@ public function copyInfoAction( $session->set((string) $transactionId, ['idMapping' => []]); }, 'opendxp_copy'); + $sourceId = $getChildIdsPayload->sourceId; + if ($type === 'recursive' || $type === 'recursive-update-references') { $pasteJobs[] = [[ 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), @@ -71,7 +76,7 @@ public function copyInfoAction( ], ]]; - $childIds = $getChildIds($sourceId)->ids; + $childIds = $getChildIds($getChildIdsPayload)->ids; foreach ($childIds as $id) { $pasteJobs[] = [[ @@ -122,66 +127,37 @@ public function copyInfoAction( } #[Route('/copy-rewrite-ids', name: 'opendxp_admin_document_document_copyrewriteids', methods: ['PUT'])] - public function copyRewriteIdsAction(RewriteDocumentIdsHandler $rewriteIds, Request $request): JsonResponse - { - $transactionId = $request->request->get('transactionId'); - - $idStore = Session::useBag($request->getSession(), static fn (AttributeBagInterface $session) => $session->get($transactionId), 'opendxp_copy'); - - if (!array_key_exists('rewrite-stack', $idStore)) { - $idStore['rewrite-stack'] = array_values($idStore['idMapping']); - } - - $id = array_shift($idStore['rewrite-stack']); - $enableInheritance = $request->request->get('enableInheritance') === 'true'; - - $rewriteIds((int) $id, $idStore['idMapping'], $enableInheritance); + public function copyRewriteIdsAction( + RewriteDocumentIdsPayload $payload, + RewriteDocumentIdsHandler $rewriteIds, + Request $request, + ): JsonResponse { + $rewriteIds($payload); - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId, $idStore): void { - $session->set($transactionId, $idStore); + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($payload): void { + $session->set($payload->transactionId, $payload->updatedIdStore); }, 'opendxp_copy'); - return $this->adminJson(ApiResponse::ok(['id' => $id])); + return $this->adminJson(ApiResponse::ok(['id' => $payload->documentId])); } #[Route('/copy', name: 'opendxp_admin_document_document_copy', methods: ['POST'])] - public function copyAction(CopyDocumentHandler $copyDocument, Request $request): JsonResponse - { - $sourceId = (int) $request->request->get('sourceId'); - $targetId = (int) $request->request->get('targetId'); - $type = (string) $request->request->get('type'); - - $session = Session::getSessionBag($request->getSession(), 'opendxp_copy'); - $sessionBag = $session->get($request->request->get('transactionId')); - - $sourceParentId = $request->request->get('targetParentId') ? (int) $request->request->get('sourceParentId') : null; - $targetParentId = $request->request->get('targetParentId') ? (int) $request->request->get('targetParentId') : null; - $sessionParentId = !empty($sessionBag['parentId']) ? (int) $sessionBag['parentId'] : null; - - $enableInheritance = $request->request->get('enableInheritance') === 'true'; - $resetIndex = $request->request->get('resetIndex') === 'true'; - $language = ($request->request->get('language') ?: null); - - $result = $copyDocument( - $sourceId, - $targetId, - $type, - $sourceParentId, - $targetParentId, - $sessionParentId, - $enableInheritance, - $resetIndex, - $language, - ); + public function copyAction( + CopyDocumentPayload $payload, + CopyDocumentHandler $copyDocument, + Request $request, + ): JsonResponse { + $result = $copyDocument($payload); if ($result->newDocument !== null) { + $sessionBag = $payload->sessionBag; $sessionBag['idMapping'][$result->sourceId] = $result->newDocument->getId(); - if ($request->request->get('saveParentId')) { + if ($payload->saveParentId) { $sessionBag['parentId'] = $result->newDocument->getId(); } - $session->set($request->request->get('transactionId'), $sessionBag); + Session::getSessionBag($request->getSession(), 'opendxp_copy')->set($payload->transactionId, $sessionBag); } return $this->adminJson(ApiResponse::ok()); diff --git a/src/Controller/Admin/Document/DocumentVersionController.php b/src/Controller/Admin/Document/DocumentVersionController.php index 452e371e..a5b8c882 100644 --- a/src/Controller/Admin/Document/DocumentVersionController.php +++ b/src/Controller/Admin/Document/DocumentVersionController.php @@ -19,13 +19,14 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Version\DiffVersionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Version\PublishVersionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Version\SaveVersionToSessionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Version\DiffVersions\DiffVersionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Version\DiffVersions\DiffVersionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Version\PublishVersion\PublishVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Version\SaveVersionToSession\SaveVersionToSessionHandler; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; @@ -39,27 +40,33 @@ class DocumentVersionController extends AdminAbstractController { #[IsGranted(CorePermission::Documents->value)] #[Route('/version-to-session', name: 'opendxp_admin_document_document_versiontosession', methods: ['POST'])] - public function versionToSessionAction(Request $request, SaveVersionToSessionHandler $saveToSession): Response - { - $saveToSession($request->request->getInt('id')); + public function versionToSessionAction( + IdBodyPayload $payload, + SaveVersionToSessionHandler $saveToSession, + ): Response { + $saveToSession($payload); return new Response(); } #[IsGranted(CorePermission::Documents->value)] #[Route('/publish-version', name: 'opendxp_admin_document_document_publishversion', methods: ['POST'])] - public function publishVersionAction(Request $request, PublishVersionHandler $publishVersion): JsonResponse - { - $result = $publishVersion($request->request->getInt('id')); + public function publishVersionAction( + IdBodyPayload $payload, + PublishVersionHandler $publishVersion, + ): JsonResponse { + $result = $publishVersion($payload); return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } #[IsGranted(CorePermission::Documents->value)] #[Route('/diff-versions/from/{from}/to/{to}', name: 'opendxp_admin_document_document_diffversions', requirements: ['from' => "\d+", 'to' => "\d+"], methods: ['GET'])] - public function diffVersionsAction(Request $request, DiffVersionsHandler $diffVersions, int $from, int $to): Response - { - $result = $diffVersions($from, $to, $request->getSchemeAndHttpHost()); + public function diffVersionsAction( + DiffVersionsPayload $payload, + DiffVersionsHandler $diffVersions, + ): Response { + $result = $diffVersions($payload); if (!$result->supported) { return $this->render('@OpenDxpAdmin/admin/document/document/diff_versions_unsupported.html.twig'); @@ -74,8 +81,7 @@ public function diffVersionsAction(Request $request, DiffVersionsHandler $diffVe public function diffVersionsHtmlAction( #[MapQueryParameter] ?string $id = null, - ): BinaryFileResponse - { + ): BinaryFileResponse { $file = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . basename($id); if (file_exists($file)) { return new BinaryFileResponse($file); diff --git a/src/Controller/Admin/Document/EmailController.php b/src/Controller/Admin/Document/EmailController.php index 5948a886..7a0040c0 100644 --- a/src/Controller/Admin/Document/EmailController.php +++ b/src/Controller/Admin/Document/EmailController.php @@ -19,12 +19,11 @@ use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\SaveEmailHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Document\EmailPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailData\GetEmailDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\SaveEmail\SaveEmailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\SaveEmail\SaveEmailPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; /** @@ -36,11 +35,11 @@ class EmailController extends DocumentControllerBase #[Route('/get-data-by-id', name: 'getdatabyid', methods: ['GET'])] public function getDataByIdAction( GetEmailDataHandler $handler, - #[MapQueryParameter] int $id = 0, + IdQueryPayload $payload, ): JsonResponse { try { - $result = $handler($id); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } @@ -49,10 +48,10 @@ public function getDataByIdAction( } #[Route('/save', name: 'save', methods: ['PUT', 'POST'])] - public function saveAction(Request $request, SaveEmailHandler $handler): JsonResponse + public function saveAction(SaveEmailPayload $payload, SaveEmailHandler $handler): JsonResponse { try { - $result = $handler((int) $request->request->get('id'), EmailPayload::fromRequest($request)); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } diff --git a/src/Controller/Admin/Document/FolderController.php b/src/Controller/Admin/Document/FolderController.php index 7a423b1b..979754f2 100644 --- a/src/Controller/Admin/Document/FolderController.php +++ b/src/Controller/Admin/Document/FolderController.php @@ -19,12 +19,11 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\SaveFolderHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Document\FolderPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData\GetFolderDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\SaveFolder\SaveFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData\GetFolderDataPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\SaveFolder\SaveFolderPayload; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; /** @@ -39,10 +38,11 @@ class FolderController extends DocumentControllerBase #[Route('/get-data-by-id', name: 'getdatabyid', methods: ['GET'])] public function getDataByIdAction( GetFolderDataHandler $handler, - #[MapQueryParameter] int $id = 0, + GetFolderDataPayload $payload, ): JsonResponse { - $result = $handler($id); + $result = $handler($payload); + return $this->preSendDataActions($result->data, $result->folder); } @@ -50,9 +50,9 @@ public function getDataByIdAction( * @throws Exception */ #[Route('/save', name: 'save', methods: ['PUT', 'POST'])] - public function saveAction(Request $request, SaveFolderHandler $handler): JsonResponse + public function saveAction(SaveFolderPayload $payload, SaveFolderHandler $handler): JsonResponse { - $result = $handler((int) $request->request->get('id'), FolderPayload::fromRequest($request)); + $result = $handler($payload); return $this->adminJson(ApiResponse::ok(['treeData' => $result->treeData])); } diff --git a/src/Controller/Admin/Document/HardlinkController.php b/src/Controller/Admin/Document/HardlinkController.php index d2d2cce4..bd2d3ded 100644 --- a/src/Controller/Admin/Document/HardlinkController.php +++ b/src/Controller/Admin/Document/HardlinkController.php @@ -20,12 +20,11 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\SaveHardlinkHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Document\HardlinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkData\GetHardlinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\SaveHardlink\SaveHardlinkHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\SaveHardlink\SaveHardlinkPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; /** @@ -40,11 +39,11 @@ class HardlinkController extends DocumentControllerBase #[Route('/get-data-by-id', name: 'getdatabyid', methods: ['GET'])] public function getDataByIdAction( GetHardlinkDataHandler $handler, - #[MapQueryParameter] int $id = 0, + IdQueryPayload $payload, ): JsonResponse { try { - $result = $handler($id); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } @@ -56,9 +55,9 @@ public function getDataByIdAction( * @throws Exception */ #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] - public function saveAction(Request $request, SaveHardlinkHandler $handler): JsonResponse + public function saveAction(SaveHardlinkPayload $payload, SaveHardlinkHandler $handler): JsonResponse { - $result = $handler((int) $request->request->get('id'), HardlinkPayload::fromRequest($request)); + $result = $handler($payload); return $this->adminJson(ApiResponse::ok([ 'data' => [ diff --git a/src/Controller/Admin/Document/LinkController.php b/src/Controller/Admin/Document/LinkController.php index c953ef11..22326710 100644 --- a/src/Controller/Admin/Document/LinkController.php +++ b/src/Controller/Admin/Document/LinkController.php @@ -20,12 +20,11 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\SaveLinkHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Document\LinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkData\GetLinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\SaveLink\SaveLinkHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\SaveLink\SaveLinkPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; /** @@ -40,11 +39,11 @@ class LinkController extends DocumentControllerBase #[Route('/get-data-by-id', name: 'getdatabyid', methods: ['GET'])] public function getDataByIdAction( GetLinkDataHandler $handler, - #[MapQueryParameter] int $id = 0, + IdQueryPayload $payload, ): JsonResponse { try { - $result = $handler($id); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } @@ -56,9 +55,9 @@ public function getDataByIdAction( * @throws Exception */ #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] - public function saveAction(Request $request, SaveLinkHandler $handler): JsonResponse + public function saveAction(SaveLinkPayload $payload, SaveLinkHandler $handler): JsonResponse { - $result = $handler((int) $request->request->get('id'), LinkPayload::fromRequest($request)); + $result = $handler($payload); return $this->adminJson(ApiResponse::ok([ 'data' => [ diff --git a/src/Controller/Admin/Document/PageController.php b/src/Controller/Admin/Document/PageController.php index 89c2ad25..3bed21f9 100644 --- a/src/Controller/Admin/Document/PageController.php +++ b/src/Controller/Admin/Document/PageController.php @@ -19,23 +19,28 @@ use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\CheckPrettyUrlHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GeneratePagePreviewsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GenerateQrCodeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPagePreviewImagePathHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\RenderAreabrickIndexEditmodeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\ResetEditablesSessionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\SavePageHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Document\PagePayload; -use OpenDxp\Bundle\AdminBundle\Payload\Document\RenderAreabrickIndexEditmodePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\CheckPrettyUrl\CheckPrettyUrlHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\CheckPrettyUrl\CheckPrettyUrlPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GeneratePagePreviews\GeneratePagePreviewsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GenerateQrCode\GenerateQrCodeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GenerateQrCode\GenerateQrCodePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPagePreviewImagePath\GetPagePreviewImagePathHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPagePreviewImagePath\GetPagePreviewImagePathPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\RenderAreabrickIndexEditmode\RenderAreabrickIndexEditmodeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\ResetEditablesSession\ResetEditablesSessionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\ResetEditablesSession\ResetEditablesSessionPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\SavePage\SavePageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\SavePage\SavePagePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\RenderAreabrickIndexEditmode\RenderAreabrickIndexEditmodePayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Document\StaticPageGenerator; use OpenDxp\Http\Request\Resolver\DocumentResolver; use OpenDxp\Http\Request\Resolver\EditmodeResolver; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Attribute\Route; use Twig\Environment; @@ -49,11 +54,10 @@ class PageController extends DocumentControllerBase #[Route('/get-data-by-id', name: 'getdatabyid', methods: ['GET'])] public function getDataByIdAction( GetPageDataHandler $handler, - #[MapQueryParameter] int $id = 0, - ): JsonResponse - { + GetPageDataPayload $payload, + ): JsonResponse { try { - $result = $handler($id); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } @@ -62,17 +66,17 @@ public function getDataByIdAction( } #[Route('/save', name: 'save', methods: ['PUT', 'POST'])] - public function saveAction(Request $request, StaticPageGenerator $staticPageGenerator, SavePageHandler $handler): JsonResponse + public function saveAction(SavePagePayload $payload, StaticPageGenerator $staticPageGenerator, SavePageHandler $handler): JsonResponse { try { - $result = $handler((int) $request->request->get('id'), PagePayload::fromRequest($request)); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } if ($result->task === self::TASK_PUBLISH || $result->task === self::TASK_UNPUBLISH) { $data = [ - 'versionDate' => $result->page->getModificationDate(), + 'versionDate' => $result->page->getModificationDate(), 'versionCount' => $result->page->getVersionCount(), ]; if ($staticGeneratorEnabled = $result->page->getStaticGeneratorEnabled()) { @@ -86,9 +90,9 @@ public function saveAction(Request $request, StaticPageGenerator $staticPageGene $draftData = []; if ($result->version) { $draftData = [ - 'id' => $result->version->getId(), + 'id' => $result->version->getId(), 'modificationDate' => $result->version->getDate(), - 'isAutoSave' => $result->version->isAutoSave(), + 'isAutoSave' => $result->version->isAutoSave(), ]; } @@ -96,9 +100,9 @@ public function saveAction(Request $request, StaticPageGenerator $staticPageGene } #[Route('/generate-previews', name: 'generatepreviews', methods: ['GET'])] - public function generatePreviewsAction(GeneratePagePreviewsHandler $handler): JsonResponse + public function generatePreviewsAction(GeneratePagePreviewsHandler $handler, EmptyPayload $payload): JsonResponse { - $handler(); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -106,10 +110,9 @@ public function generatePreviewsAction(GeneratePagePreviewsHandler $handler): Js #[Route('/display-preview-image', name: 'display_preview_image', methods: ['GET'])] public function displayPreviewImageAction( GetPagePreviewImagePathHandler $handler, - #[MapQueryParameter] int $id = 0, - ): BinaryFileResponse - { - $filePath = $handler($id); + GetPagePreviewImagePathPayload $payload, + ): BinaryFileResponse { + $filePath = $handler($payload); return new BinaryFileResponse($filePath, 200, [ 'Content-Type' => 'image/jpg', @@ -117,20 +120,17 @@ public function displayPreviewImageAction( } #[Route('/check-pretty-url', name: 'checkprettyurl', methods: ['POST'])] - public function checkPrettyUrlAction(Request $request, CheckPrettyUrlHandler $handler): JsonResponse + public function checkPrettyUrlAction(CheckPrettyUrlPayload $payload, CheckPrettyUrlHandler $handler): JsonResponse { - $docId = $request->request->getInt('id'); - $path = trim($request->request->get('path', '')); - - $result = $handler($docId, $path); + $result = $handler($payload); return $this->adminJson(ApiResponse::fromBool($result->success, ['message' => implode('
', $result->messages)])); } #[Route('/clear-editable-data', name: 'cleareditabledata', methods: ['PUT'])] - public function clearEditableDataAction(Request $request, ResetEditablesSessionHandler $handler): JsonResponse + public function clearEditableDataAction(ResetEditablesSessionPayload $payload, ResetEditablesSessionHandler $handler): JsonResponse { - $handler($request->request->getInt('id')); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -138,16 +138,14 @@ public function clearEditableDataAction(Request $request, ResetEditablesSessionH #[Route('/qr-code', name: 'qrcode', methods: ['GET'])] public function qrCodeAction( GenerateQrCodeHandler $handler, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $download = null, - ): BinaryFileResponse - { - $tmpFile = $handler($id, (bool) $download); + GenerateQrCodePayload $payload, + ): BinaryFileResponse { + $tmpFile = $handler($payload); $response = new BinaryFileResponse($tmpFile); $response->headers->set('Content-Type', 'image/png'); - if ($download) { + if ($payload->download) { $response->setContentDisposition('attachment', 'qrcode-preview.png'); } @@ -162,13 +160,15 @@ public function qrCodeAction( #[Route('/areabrick-render-index-editmode', name: 'areabrick-render-index-editmode', methods: ['POST'])] public function areabrickRenderIndexEditmode( Request $request, - RenderAreabrickIndexEditmodeHandler $renderAreabrickIndexEditmode, + RenderAreabrickIndexEditmodePayload $payload, + RenderAreabrickIndexEditmodeHandler $handler, DocumentResolver $documentResolver, Environment $twig, ): JsonResponse { + $request->attributes->set(EditmodeResolver::ATTRIBUTE_EDITMODE, true); - $result = $renderAreabrickIndexEditmode(RenderAreabrickIndexEditmodePayload::fromRequest($request)); + $result = $handler($payload); $documentResolver->setDocument($request, $result->document); $twig->addGlobal('document', $result->document); @@ -176,7 +176,7 @@ public function areabrickRenderIndexEditmode( return new JsonResponse([ 'editableDefinitions' => $result->editableDefinitions, - 'htmlCode' => $result->htmlCode, + 'htmlCode' => $result->htmlCode, ]); } } diff --git a/src/Controller/Admin/Document/RenderletController.php b/src/Controller/Admin/Document/RenderletController.php index a8ca9c4e..fdf80b90 100644 --- a/src/Controller/Admin/Document/RenderletController.php +++ b/src/Controller/Admin/Document/RenderletController.php @@ -18,9 +18,8 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\Document; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet\RenderRenderletHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Document\RenderRenderletPayload; -use Symfony\Component\HttpFoundation\Request; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet\RenderRenderlet\RenderRenderletHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet\RenderRenderlet\RenderRenderletPayload; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; @@ -34,10 +33,10 @@ class RenderletController extends AdminAbstractController */ #[Route('/document_tag/renderlet', name: 'opendxp_admin_document_renderlet_renderlet', methods: ['GET'])] public function renderletAction( - Request $request, + RenderRenderletPayload $payload, RenderRenderletHandler $handler, ): Response { - $result = $handler(RenderRenderletPayload::fromRequest($request)); + $result = $handler($payload); return new Response($result->html); } diff --git a/src/Controller/Admin/Document/SnippetController.php b/src/Controller/Admin/Document/SnippetController.php index cbf7a60b..7cbcc4ad 100644 --- a/src/Controller/Admin/Document/SnippetController.php +++ b/src/Controller/Admin/Document/SnippetController.php @@ -20,12 +20,11 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\SaveSnippetHandler; -use OpenDxp\Bundle\AdminBundle\Payload\Document\SnippetPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData\GetSnippetDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\SaveSnippet\SaveSnippetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\SaveSnippet\SaveSnippetPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; /** @@ -39,13 +38,12 @@ class SnippetController extends DocumentControllerBase */ #[Route('/get-data-by-id', name: 'getdatabyid', methods: ['GET'])] public function getDataByIdAction( - Request $request, GetSnippetDataHandler $handler, - #[MapQueryParameter] int $id = 0, + IdQueryPayload $payload, ): JsonResponse { try { - $result = $handler($id); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } @@ -56,10 +54,10 @@ public function getDataByIdAction( * @throws Exception */ #[Route('/save', name: 'save', methods: ['POST', 'PUT'])] - public function saveAction(Request $request, SaveSnippetHandler $handler): JsonResponse + public function saveAction(SaveSnippetPayload $payload, SaveSnippetHandler $handler): JsonResponse { try { - $result = $handler((int) $request->request->get('id'), SnippetPayload::fromRequest($request)); + $result = $handler($payload); } catch (ElementLockedException $e) { return $this->getEditLockResponse($e->getElementId(), $e->getElementType()); } diff --git a/src/Controller/Admin/ElementController.php b/src/Controller/Admin/ElementController.php index 71f97b62..1e8c337b 100644 --- a/src/Controller/Admin/ElementController.php +++ b/src/Controller/Admin/ElementController.php @@ -35,18 +35,19 @@ use OpenDxp\Bundle\AdminBundle\Handler\Element\GetSubtypeHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\GetVersionsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\LockElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\NoteListPayload; use OpenDxp\Bundle\AdminBundle\Handler\Element\ReplaceAssignmentsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\TypePathHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElementHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElementsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockPropagateHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\VersionUpdateHandler; -use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -121,33 +122,30 @@ public function noteTypes( #[Route('/element/note-list', name: 'opendxp_admin_element_notelist', methods: ['POST'])] #[IsGranted(CorePermission::NotesEvents->value)] public function noteListAction( - Request $request, + NoteListPayload $payload, GetNoteListHandler $getNoteList, DeleteNoteHandler $deleteNote, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse - { - - if ($xaction === 'destroy') { - $data = $this->decodeJson($request->request->get('data')); - $success = $deleteNote((int) $data['id']); - - return $this->adminJson(ApiResponse::fromBool($success)); + ): JsonResponse { + if ($payload->hasData) { + return match ($xaction) { + 'destroy' => $this->destroyNote($deleteNote, $payload), + default => throw new BadRequestHttpException(), + }; } - $result = ($getNoteList)( - offset: $request->request->getInt('start', 0), - limit: $request->request->getInt('limit') ?: null, - sortingSettings: QueryParams::extractSortingSettings($request->request->all()), - filterText: $request->request->get('filterText'), - filterJson: $request->request->get('filter'), - cid: $request->request->has('cid') ? $request->request->get('cid') : null, - ctype: $request->request->has('ctype') ? $request->request->get('ctype') : null, - ); + $result = $getNoteList($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } + private function destroyNote(DeleteNoteHandler $handler, NoteListPayload $payload): JsonResponse + { + $handler($payload); + + return $this->adminJson(ApiResponse::ok(['data' => []])); + } + #[Route('/element/note-add', name: 'opendxp_admin_element_noteadd', methods: ['POST'])] #[IsGranted(CorePermission::NotesEvents->value)] public function noteAddAction(Request $request, AddNoteHandler $addNote): JsonResponse diff --git a/src/Controller/Admin/EmailController.php b/src/Controller/Admin/EmailController.php index c29f4353..5dcc864a 100644 --- a/src/Controller/Admin/EmailController.php +++ b/src/Controller/Admin/EmailController.php @@ -23,6 +23,7 @@ use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Security\Http\Attribute\IsGranted; +use OpenDxp\Bundle\AdminBundle\Handler\Email\BlocklistPayload; use OpenDxp\Bundle\AdminBundle\Handler\Email\CreateBlocklistEntryHandler; use OpenDxp\Bundle\AdminBundle\Handler\Email\DeleteBlocklistEntryHandler; use OpenDxp\Bundle\AdminBundle\Handler\Email\DeleteEmailLogHandler; @@ -33,7 +34,6 @@ use OpenDxp\Bundle\AdminBundle\Handler\Email\ResendEmailHandler; use OpenDxp\Bundle\AdminBundle\Handler\Email\SendTestEmailHandler; use OpenDxp\Bundle\AdminBundle\Handler\Email\UpdateBlocklistEntryHandler; -use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Http\RequestHelper; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -153,59 +153,31 @@ public function sendTestEmailAction(Request $request, SendTestEmailHandler $send #[IsGranted(CorePermission::Emails->value)] #[Route('/blocklist', name: 'opendxp_admin_email_blocklist', methods: ['POST'])] public function blocklistAction( - Request $request, + BlocklistPayload $payload, GetBlocklistHandler $getBlocklist, CreateBlocklistEntryHandler $createBlocklistEntry, UpdateBlocklistEntryHandler $updateBlocklistEntry, DeleteBlocklistEntryHandler $deleteBlocklistEntry, #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data')); - - if (is_array($data)) { - foreach ($data as $key => &$value) { - if (is_string($value)) { - if ($key === 'address') { - $value = filter_var($value, FILTER_SANITIZE_EMAIL); - } - - $value = trim($value); - } - } - unset($value); - } - - if ($xaction === 'destroy') { - $deleteBlocklistEntry(address: $data['address']); - - return $this->adminJson(ApiResponse::ok(['data' => []])); - } - - if ($xaction === 'update') { - $entryData = $updateBlocklistEntry(data: $data); - - return $this->adminJson(ApiResponse::ok(['data' => $entryData])); - } - - if ($xaction === 'create') { - $entryData = $createBlocklistEntry(data: $data); - - return $this->adminJson(ApiResponse::ok(['data' => $entryData])); - } - } else { - $sortingSettings = QueryParams::extractSortingSettings($request->request->all()); - - $result = $getBlocklist( - limit: (int)$request->request->get('limit', 50), - offset: (int)$request->request->get('start', 0), - sortingSettings: $sortingSettings, - filter: $request->request->has('filter') ? $request->request->get('filter') : null, - ); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + if ($payload->hasData) { + return match ($xaction) { + 'destroy' => $this->destroyBlocklistEntry($deleteBlocklistEntry, $payload), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $updateBlocklistEntry($payload)])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $createBlocklistEntry($payload)])), + default => throw new BadRequestHttpException(), + }; } - throw new BadRequestHttpException(); + $result = $getBlocklist($payload); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + } + + private function destroyBlocklistEntry(DeleteBlocklistEntryHandler $handler, BlocklistPayload $payload): JsonResponse + { + $handler($payload); + + return $this->adminJson(ApiResponse::ok(['data' => []])); } } diff --git a/src/Controller/Admin/IndexController.php b/src/Controller/Admin/IndexController.php index 1fd9590d..e247d9a8 100644 --- a/src/Controller/Admin/IndexController.php +++ b/src/Controller/Admin/IndexController.php @@ -19,8 +19,10 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Admin\IndexActionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Admin\StatisticsActionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Admin\Settings\SettingsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Admin\Settings\SettingsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Admin\Statistics\StatisticsHandler; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Controller\KernelResponseEventInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -38,33 +40,38 @@ class IndexController extends AdminAbstractController implements KernelResponseE #[Route('/', name: 'opendxp_admin_index', methods: ['GET'])] public function indexAction( Request $request, - IndexActionHandler $indexAction, + SettingsHandler $settingsHandler, + SettingsPayload $payload, TranslatorInterface $translator, ): Response { $user = $this->getAdminUser(); + if ($user->getTwoFactorAuthentication('required') && !$user->getTwoFactorAuthentication('enabled')) { + return $this->redirectToRoute('opendxp_admin_2fa_setup'); + } + $request->setLocale($user->getLanguage()); if ($translator instanceof LocaleAwareInterface) { $translator->setLocale($user->getLanguage()); } - if ($user->getTwoFactorAuthentication('required') && !$user->getTwoFactorAuthentication('enabled')) { - return $this->redirectToRoute('opendxp_admin_2fa_setup'); - } - - $result = $indexAction($request); + $result = $settingsHandler($payload); return $this->render($result->template ?? '@OpenDxpAdmin/admin/index/index.html.twig', $result->templateParams); } #[Route('/index/statistics', name: 'opendxp_admin_index_statistics', methods: ['GET'])] - public function statisticsAction(Request $request, StatisticsActionHandler $statisticsAction): JsonResponse - { + public function statisticsAction( + Request $request, + StatisticsHandler $statisticsHandler, + EmptyPayload $payload, + ): JsonResponse { + if (!$request->isXmlHttpRequest()) { throw $this->createAccessDeniedHttpException(); } - $statisticsAction(); + $statisticsHandler($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/RecyclebinController.php b/src/Controller/Admin/RecyclebinController.php index b9dd7e25..a493904f 100644 --- a/src/Controller/Admin/RecyclebinController.php +++ b/src/Controller/Admin/RecyclebinController.php @@ -22,8 +22,8 @@ use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\AddToRecyclebinHandler; use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\DeleteRecyclebinItemHandler; use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\ListRecyclebinHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RecyclebinPayload; use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RestoreRecyclebinItemHandler; -use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Controller\KernelControllerEventInterface; use OpenDxp\Model\Element; @@ -31,6 +31,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Event\ControllerEvent; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -42,33 +43,28 @@ class RecyclebinController extends AdminAbstractController implements KernelCont #[IsGranted(CorePermission::Recyclebin->value)] #[Route('/recyclebin/list', name: 'opendxp_admin_recyclebin_list', methods: ['POST'])] public function listAction( + RecyclebinPayload $payload, ListRecyclebinHandler $listRecyclebin, DeleteRecyclebinItemHandler $deleteRecyclebinItem, - Request $request, #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - if ($xaction === 'destroy') { - $deleteRecyclebinItem(QueryParams::getRecordIdForGridRequest($request->request->get('data'))); - - return $this->adminJson(ApiResponse::ok(['data' => []])); + if ($payload->hasData) { + return match ($xaction) { + 'destroy' => $this->destroyRecyclebinItem($deleteRecyclebinItem, $payload), + default => throw new BadRequestHttpException(), + }; } - $sortingSettings = QueryParams::extractSortingSettings($request->request->all()); - $orderKey = $sortingSettings['orderKey'] ?: 'date'; - $order = $sortingSettings['orderKey'] ? $sortingSettings['order'] : 'DESC'; + $result = $listRecyclebin($payload); - $parsedFilters = $this->decodeJson($request->request->get('filter') ?? '[]'); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + } - $result = $listRecyclebin( - limit: (int) $request->request->get('limit', 50), - offset: (int) $request->request->get('start', 0), - orderKey: $orderKey, - order: $order, - filterFullText: $request->request->get('filterFullText') ?: null, - filters: $parsedFilters, - ); + private function destroyRecyclebinItem(DeleteRecyclebinItemHandler $handler, RecyclebinPayload $payload): JsonResponse + { + $handler($payload); - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + return $this->adminJson(ApiResponse::ok(['data' => []])); } #[IsGranted(CorePermission::Recyclebin->value)] diff --git a/src/Controller/Admin/SettingsController.php b/src/Controller/Admin/SettingsController.php index ac8c0142..31464e8f 100644 --- a/src/Controller/Admin/SettingsController.php +++ b/src/Controller/Admin/SettingsController.php @@ -30,6 +30,9 @@ use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeletePredefinedMetadataHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeletePredefinedPropertyHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteWebsiteSettingHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedMetadataPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedPropertyPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\WebsiteSettingPayload; use OpenDxp\Bundle\AdminBundle\Handler\Settings\DisplayCustomLogoHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAppearanceSettingsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAdminLanguagesHandler; @@ -48,7 +51,6 @@ use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedPropertyHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateWebsiteSettingHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\UploadCustomLogoHandler; -use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Logger; @@ -113,41 +115,32 @@ public function deleteCustomLogoAction(DeleteCustomLogoHandler $handler): JsonRe #[IsGranted(CorePermission::AssetMetadata->value)] #[Route('/predefined-metadata', name: 'opendxp_admin_settings_metadata', methods: ['POST'])] public function metadataAction( - Request $request, + PredefinedMetadataPayload $payload, GetPredefinedMetadataListHandler $getPredefinedMetadataList, CreatePredefinedMetadataHandler $createPredefinedMetadata, UpdatePredefinedMetadataHandler $updatePredefinedMetadata, DeletePredefinedMetadataHandler $deletePredefinedMetadata, #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data')); - - if ($xaction === 'destroy') { - $deletePredefinedMetadata((string) $data['id']); - - return $this->adminJson(ApiResponse::ok(['data' => []])); - } - - if ($xaction === 'update') { - $result = $updatePredefinedMetadata($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); - } + if ($payload->hasData) { + return match ($xaction) { + 'destroy' => $this->destroyPredefinedMetadata($deletePredefinedMetadata, $payload), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedMetadata($payload)->data])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $createPredefinedMetadata($payload)->data])), + default => throw new BadRequestHttpException(), + }; + } - if ($xaction === 'create') { - unset($data['id']); - $result = $createPredefinedMetadata($data); + $result = $getPredefinedMetadataList($payload); - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); - } - } else { - $result = $getPredefinedMetadataList($request->request->get('filter')); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + } - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); - } + private function destroyPredefinedMetadata(DeletePredefinedMetadataHandler $handler, PredefinedMetadataPayload $payload): JsonResponse + { + $handler($payload); - throw new BadRequestHttpException(); + return $this->adminJson(ApiResponse::ok(['data' => []])); } #[Route('/get-predefined-metadata', name: 'opendxp_admin_settings_getpredefinedmetadata', methods: ['GET'])] @@ -163,41 +156,32 @@ public function getPredefinedMetadataAction( #[IsGranted(CorePermission::PredefinedProperties->value)] #[Route('/properties', name: 'opendxp_admin_settings_properties', methods: ['POST'])] public function propertiesAction( - Request $request, + PredefinedPropertyPayload $payload, GetPredefinedPropertiesListHandler $getPredefinedPropertiesList, CreatePredefinedPropertyHandler $createPredefinedProperty, UpdatePredefinedPropertyHandler $updatePredefinedProperty, DeletePredefinedPropertyHandler $deletePredefinedProperty, #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data')); - - if ($xaction === 'destroy') { - $deletePredefinedProperty((string) $data['id']); - - return $this->adminJson(ApiResponse::ok(['data' => []])); - } - - if ($xaction === 'update') { - $result = $updatePredefinedProperty($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); - } + if ($payload->hasData) { + return match ($xaction) { + 'destroy' => $this->destroyPredefinedProperty($deletePredefinedProperty, $payload), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedProperty($payload)->data])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $createPredefinedProperty($payload)->data])), + default => throw new BadRequestHttpException(), + }; + } - if ($xaction === 'create') { - unset($data['id']); - $result = $createPredefinedProperty($data); + $result = $getPredefinedPropertiesList($payload); - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); - } - } else { - $result = $getPredefinedPropertiesList($request->request->get('filter')); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + } - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); - } + private function destroyPredefinedProperty(DeletePredefinedPropertyHandler $handler, PredefinedPropertyPayload $payload): JsonResponse + { + $handler($payload); - throw new BadRequestHttpException(); + return $this->adminJson(ApiResponse::ok(['data' => []])); } #[IsGranted(AdminPermission::SystemAppearance->value)] @@ -337,54 +321,32 @@ public function thumbnailAdapterCheckAction(ThumbnailAdapterCheckHandler $handle #[IsGranted(CorePermission::WebsiteSettings->value)] #[Route('/website-settings', name: 'opendxp_admin_settings_websitesettings', methods: ['POST'])] public function websiteSettingsAction( - Request $request, + WebsiteSettingPayload $payload, GetWebsiteSettingsListHandler $getWebsiteSettingsList, CreateWebsiteSettingHandler $createWebsiteSetting, UpdateWebsiteSettingHandler $updateWebsiteSetting, DeleteWebsiteSettingHandler $deleteWebsiteSetting, #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data')); - - if (is_array($data)) { - foreach ($data as &$value) { - if (is_string($value)) { - $value = trim($value); - } - } - unset($value); - } - - if ($xaction === 'destroy') { - $deleteWebsiteSetting((int) $data['id']); - - return $this->adminJson(ApiResponse::ok(['data' => []])); - } elseif ($xaction === 'update') { - $result = $updateWebsiteSetting($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); - } elseif ($xaction === 'create') { - unset($data['id']); - $result = $createWebsiteSetting($data); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data])); - } - } else { - $sortingSettings = QueryParams::extractSortingSettings([...$request->request->all(), ...$request->query->all()]); - - $result = $getWebsiteSettingsList( - limit: (int) $request->request->get('limit', 50), - offset: (int) $request->request->get('start', 0), - orderKey: $sortingSettings['orderKey'] ?: null, - order: $sortingSettings['order'] ?? null, - filter: $request->request->has('filter') ? $request->request->get('filter') : null, - ); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + if ($payload->hasData) { + return match ($xaction) { + 'destroy' => $this->destroyWebsiteSetting($deleteWebsiteSetting, $payload), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $updateWebsiteSetting($payload)->data])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $createWebsiteSetting($payload)->data])), + default => throw new BadRequestHttpException(), + }; } - return $this->adminJson(ApiResponse::error()); + $result = $getWebsiteSettingsList($payload); + + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + } + + private function destroyWebsiteSetting(DeleteWebsiteSettingHandler $handler, WebsiteSettingPayload $payload): JsonResponse + { + $handler($payload); + + return $this->adminJson(ApiResponse::ok(['data' => []])); } #[Route('/get-available-algorithms', name: 'opendxp_admin_settings_getavailablealgorithms', methods: ['GET'])] diff --git a/src/Controller/Admin/TranslationController.php b/src/Controller/Admin/TranslationController.php index b6ec5de0..e1f91fa6 100644 --- a/src/Controller/Admin/TranslationController.php +++ b/src/Controller/Admin/TranslationController.php @@ -30,6 +30,7 @@ use OpenDxp\Bundle\AdminBundle\Handler\Translation\GetWebsiteTranslationLanguagesHandler; use OpenDxp\Bundle\AdminBundle\Handler\Translation\ImportTranslationsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Translation\MergeTranslationItemsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationPayload; use OpenDxp\Bundle\AdminBundle\Handler\Translation\UpdateTranslationHandler; use OpenDxp\Bundle\AdminBundle\Handler\Translation\UploadTranslationImportFileHandler; use OpenDxp\Model\Translation; @@ -39,6 +40,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; /** @@ -155,64 +157,62 @@ public function addAdminTranslationKeysAction( #[Route('/translations', name: 'opendxp_admin_translation_translations', methods: ['POST'])] public function translationsAction( - Request $request, + TranslationPayload $payload, DeleteTranslationHandler $deleteTranslation, UpdateTranslationHandler $updateTranslation, CreateTranslationHandler $createTranslation, GetTranslationsHandler $getTranslations, #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - $domain = $request->request->get('domain', Translation::DOMAIN_DEFAULT); - $admin = $domain === Translation::DOMAIN_ADMIN; - - $this->checkPermission(($admin ? 'admin_' : '') . 'translations'); - - if ($request->request->has('data')) { - $data = $this->decodeJson($request->request->get('data')); - - if ($xaction === 'destroy') { - $deleteTranslation($data['key'], $domain); - - return $this->adminJson(ApiResponse::ok(['data' => []])); - } - - if ($xaction === 'update') { - $result = $updateTranslation($data, $domain); - - return $this->adminJson(ApiResponse::ok(['data' => [ - 'key' => $result->key, - 'creationDate' => $result->creationDate, - 'modificationDate' => $result->modificationDate, - 'type' => $result->type, - ...$this->prefixTranslations($result->translations), - ]])); - } - - if ($xaction === 'create') { - $result = $createTranslation($data, $domain); - - return $this->adminJson(ApiResponse::ok(['data' => [ - 'key' => $result->key, - 'creationDate' => $result->creationDate, - 'modificationDate' => $result->modificationDate, - 'type' => $result->type, - ...$this->prefixTranslations($result->translations), - ]])); - } + $this->checkPermission(($payload->domain === Translation::DOMAIN_ADMIN ? 'admin_' : '') . 'translations'); + + if ($payload->hasData) { + return match ($xaction) { + 'destroy' => $this->handleDestroyTranslation($deleteTranslation, $payload), + 'update' => $this->handleUpdateTranslation($updateTranslation, $payload), + 'create' => $this->handleCreateTranslation($createTranslation, $payload), + default => throw new BadRequestHttpException(), + }; } - $result = $getTranslations( - domain: $domain, - requestParams: [...$request->request->all(), ...$request->query->all()], - limit: (int) $request->request->get('limit', 50), - offset: (int) $request->request->get('start', 0), - filter: $request->request->get('filter'), - searchString: $request->request->get('searchString'), - ); + $result = $getTranslations($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->translations, 'total' => $result->total])); } + private function handleDestroyTranslation(DeleteTranslationHandler $handler, TranslationPayload $payload): JsonResponse + { + $handler($payload); + + return $this->adminJson(ApiResponse::ok(['data' => []])); + } + + private function handleUpdateTranslation(UpdateTranslationHandler $handler, TranslationPayload $payload): JsonResponse + { + $result = $handler($payload); + + return $this->adminJson(ApiResponse::ok(['data' => [ + 'key' => $result->key, + 'creationDate' => $result->creationDate, + 'modificationDate' => $result->modificationDate, + 'type' => $result->type, + ...$this->prefixTranslations($result->translations), + ]])); + } + + private function handleCreateTranslation(CreateTranslationHandler $handler, TranslationPayload $payload): JsonResponse + { + $result = $handler($payload); + + return $this->adminJson(ApiResponse::ok(['data' => [ + 'key' => $result->key, + 'creationDate' => $result->creationDate, + 'modificationDate' => $result->modificationDate, + 'type' => $result->type, + ...$this->prefixTranslations($result->translations), + ]])); + } + protected function prefixTranslations(array $translations): array { $prefixedTranslations = []; diff --git a/src/Handler/Admin/IndexActionHandler.php b/src/Handler/Admin/Settings/SettingsHandler.php similarity index 96% rename from src/Handler/Admin/IndexActionHandler.php rename to src/Handler/Admin/Settings/SettingsHandler.php index a602f254..5ff1ecc3 100644 --- a/src/Handler/Admin/IndexActionHandler.php +++ b/src/Handler/Admin/Settings/SettingsHandler.php @@ -14,21 +14,20 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Admin; +namespace OpenDxp\Bundle\AdminBundle\Handler\Admin\Settings; +use OpenDxp\Bundle\AdminBundle\Builder\AdminSettingsAssembler; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Event\IndexActionSettingsEvent; -use OpenDxp\Bundle\AdminBundle\Builder\AdminSettingsAssembler; use OpenDxp\Bundle\AdminBundle\Perspective\Config as PerspectiveConfig; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\System\AdminConfig; use OpenDxp\Config; use OpenDxp\Extension\Bundle\OpenDxpBundleManager; use OpenDxp\SystemSettingsConfig; -use Symfony\Component\HttpFoundation\Request; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -final class IndexActionHandler +final class SettingsHandler { public function __construct( private readonly AdminSettingsAssembler $factory, @@ -38,10 +37,10 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function __invoke(Request $request): SettingsResult + public function __invoke(SettingsPayload $payload): SettingsResult { $user = $this->userContext->getAdminUser(); - $dto = $this->factory->createSettings($request, $user); + $dto = $this->factory->createSettings($payload, $user); $settings = [ 'instanceId' => $dto->instanceId, diff --git a/src/Handler/Admin/Settings/SettingsPayload.php b/src/Handler/Admin/Settings/SettingsPayload.php new file mode 100644 index 00000000..e1e3b268 --- /dev/null +++ b/src/Handler/Admin/Settings/SettingsPayload.php @@ -0,0 +1,36 @@ +getSession()->getId(), + locale: $request->getLocale(), + ); + } +} diff --git a/src/Handler/Admin/SettingsResult.php b/src/Handler/Admin/Settings/SettingsResult.php similarity index 90% rename from src/Handler/Admin/SettingsResult.php rename to src/Handler/Admin/Settings/SettingsResult.php index db9f227e..231cabbf 100644 --- a/src/Handler/Admin/SettingsResult.php +++ b/src/Handler/Admin/Settings/SettingsResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Admin; +namespace OpenDxp\Bundle\AdminBundle\Handler\Admin\Settings; final readonly class SettingsResult { diff --git a/src/Handler/Admin/StatisticsActionHandler.php b/src/Handler/Admin/Statistics/StatisticsHandler.php similarity index 87% rename from src/Handler/Admin/StatisticsActionHandler.php rename to src/Handler/Admin/Statistics/StatisticsHandler.php index 70c6f871..98c14743 100644 --- a/src/Handler/Admin/StatisticsActionHandler.php +++ b/src/Handler/Admin/Statistics/StatisticsHandler.php @@ -14,20 +14,21 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Admin; +namespace OpenDxp\Bundle\AdminBundle\Handler\Admin\Statistics; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\GuzzleException; use OpenDxp\Bundle\AdminBundle\Builder\AdminSettingsAssembler; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; -final class StatisticsActionHandler +final class StatisticsHandler { public function __construct( private readonly AdminSettingsAssembler $factory, private readonly ClientInterface $httpClient, ) {} - public function __invoke(): void + public function __invoke(EmptyPayload $payload): void { try { $dto = $this->factory->createStatistics(); diff --git a/src/Handler/Asset/ClearAssetThumbnail/ClearAssetThumbnailPayload.php b/src/Handler/Asset/ClearAssetThumbnail/ClearAssetThumbnailPayload.php new file mode 100644 index 00000000..30843c7d --- /dev/null +++ b/src/Handler/Asset/ClearAssetThumbnail/ClearAssetThumbnailPayload.php @@ -0,0 +1,35 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/Asset/ClearAssetThumbnailHandler.php b/src/Handler/Asset/ClearAssetThumbnailHandler.php index b9166f65..9d8a5dbc 100644 --- a/src/Handler/Asset/ClearAssetThumbnailHandler.php +++ b/src/Handler/Asset/ClearAssetThumbnailHandler.php @@ -17,18 +17,19 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnail\ClearAssetThumbnailPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class ClearAssetThumbnailHandler { - public function __invoke(int $assetId): void + public function __invoke(ClearAssetThumbnailPayload $payload): void { - $asset = Asset::getById($assetId); + $asset = Asset::getById($payload->assetId); if (!$asset) { - throw new NotFoundHttpException('Asset not found: ' . $assetId); + throw new NotFoundHttpException('Asset not found: ' . $payload->assetId); } if (!$asset->isAllowed('publish')) { diff --git a/src/Handler/Asset/Copy/CopyAsset/CopyAssetPayload.php b/src/Handler/Asset/Copy/CopyAsset/CopyAssetPayload.php new file mode 100644 index 00000000..2391aa2c --- /dev/null +++ b/src/Handler/Asset/Copy/CopyAsset/CopyAssetPayload.php @@ -0,0 +1,30 @@ +sourceId; + $targetId = $payload->targetId; + $type = $payload->type; + $sourceParentId = $payload->sourceParentId; + $targetParentId = $payload->targetParentId; + $sessionParentId = $payload->sessionParentId; $source = Asset::getById($sourceId); if ($source === null) { diff --git a/src/Handler/Asset/Copy/GetAssetChildIds/GetAssetChildIdsPayload.php b/src/Handler/Asset/Copy/GetAssetChildIds/GetAssetChildIdsPayload.php new file mode 100644 index 00000000..851088bc --- /dev/null +++ b/src/Handler/Asset/Copy/GetAssetChildIds/GetAssetChildIdsPayload.php @@ -0,0 +1,35 @@ +query->getInt('sourceId'), + ); + } +} diff --git a/src/Handler/Asset/Copy/GetAssetChildIdsHandler.php b/src/Handler/Asset/Copy/GetAssetChildIdsHandler.php index 3235d209..2b550f4a 100644 --- a/src/Handler/Asset/Copy/GetAssetChildIdsHandler.php +++ b/src/Handler/Asset/Copy/GetAssetChildIdsHandler.php @@ -18,12 +18,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\GetAssetChildIds\GetAssetChildIdsPayload; use OpenDxp\Model\Asset; final class GetAssetChildIdsHandler { - public function __invoke(int $sourceId): ChildIdsResult + public function __invoke(GetAssetChildIdsPayload $payload): ChildIdsResult { + $sourceId = $payload->sourceId; $asset = Asset::getById($sourceId) ?? throw new AssetNotFoundException($sourceId); if (!$asset->hasChildren()) { diff --git a/src/Handler/Asset/CreateAssetFolder/CreateAssetFolderPayload.php b/src/Handler/Asset/CreateAssetFolder/CreateAssetFolderPayload.php new file mode 100644 index 00000000..ad445064 --- /dev/null +++ b/src/Handler/Asset/CreateAssetFolder/CreateAssetFolderPayload.php @@ -0,0 +1,37 @@ +request->getInt('parentId'), + name: $request->request->getString('name'), + ); + } +} diff --git a/src/Handler/Asset/CreateAssetFolderHandler.php b/src/Handler/Asset/CreateAssetFolderHandler.php index f0ef83a7..c33131c6 100644 --- a/src/Handler/Asset/CreateAssetFolderHandler.php +++ b/src/Handler/Asset/CreateAssetFolderHandler.php @@ -17,8 +17,8 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolder\CreateAssetFolderPayload; use OpenDxp\Model\Asset; -use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -29,8 +29,10 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $parentId, string $name): void + public function __invoke(CreateAssetFolderPayload $payload): void { + $parentId = $payload->parentId; + $name = $payload->name; $adminUser = $this->userContext->getAdminUser(); $parentAsset = Asset::getById($parentId); diff --git a/src/Handler/Asset/DeleteAsset/DeleteAssetPayload.php b/src/Handler/Asset/DeleteAsset/DeleteAssetPayload.php new file mode 100644 index 00000000..c501b408 --- /dev/null +++ b/src/Handler/Asset/DeleteAsset/DeleteAssetPayload.php @@ -0,0 +1,39 @@ +request->getString('type'), + id: $request->request->getInt('id'), + amount: $request->request->getInt('amount'), + ); + } +} diff --git a/src/Handler/Asset/DeleteAssetHandler.php b/src/Handler/Asset/DeleteAssetHandler.php index b4a484c3..a205c807 100644 --- a/src/Handler/Asset/DeleteAssetHandler.php +++ b/src/Handler/Asset/DeleteAssetHandler.php @@ -17,9 +17,9 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAsset\DeleteAssetPayload; use OpenDxp\Db\Helper; use OpenDxp\Model\Asset; -use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -30,8 +30,11 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(string $type, int $id, int $amount): DeleteAssetResult + public function __invoke(DeleteAssetPayload $payload): DeleteAssetResult { + $type = $payload->type; + $id = $payload->id; + $amount = $payload->amount; $adminUser = $this->userContext->getAdminUser(); if ($type === 'children') { $parentAsset = Asset::getById($id); diff --git a/src/Handler/Asset/Download/AddFilesToZip/AddFilesToZipPayload.php b/src/Handler/Asset/Download/AddFilesToZip/AddFilesToZipPayload.php new file mode 100644 index 00000000..29e9fc97 --- /dev/null +++ b/src/Handler/Asset/Download/AddFilesToZip/AddFilesToZipPayload.php @@ -0,0 +1,43 @@ +query->getInt('id'), + selectedIds: $request->query->getString('selectedIds') ?: null, + offset: $request->query->getInt('offset'), + limit: $request->query->getInt('limit'), + jobId: $request->query->getString('jobId'), + ); + } +} diff --git a/src/Handler/Asset/Download/AddFilesToZipHandler.php b/src/Handler/Asset/Download/AddFilesToZipHandler.php index 299d53dc..013a0cb1 100644 --- a/src/Handler/Asset/Download/AddFilesToZipHandler.php +++ b/src/Handler/Asset/Download/AddFilesToZipHandler.php @@ -18,9 +18,9 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\AddFilesToZip\AddFilesToZipPayload; use OpenDxp\Db\Helper; use OpenDxp\Model\Asset; -use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use ZipArchive; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -31,13 +31,13 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke( - int $id, - ?string $selectedIds, - int $offset, - int $limit, - string $jobId, - ): void { + public function __invoke(AddFilesToZipPayload $payload): void + { + $id = $payload->id; + $selectedIds = $payload->selectedIds; + $offset = $payload->offset; + $limit = $payload->limit; + $jobId = $payload->jobId; $adminUser = $this->userContext->getAdminUser(); $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/download-zip-' . $jobId . '.zip'; $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); diff --git a/src/Handler/Asset/Download/DownloadAsset/DownloadAssetPayload.php b/src/Handler/Asset/Download/DownloadAsset/DownloadAssetPayload.php new file mode 100644 index 00000000..4f18dcc9 --- /dev/null +++ b/src/Handler/Asset/Download/DownloadAsset/DownloadAssetPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/Asset/Download/DownloadAssetHandler.php b/src/Handler/Asset/Download/DownloadAssetHandler.php index bce36660..55e7a2b4 100644 --- a/src/Handler/Asset/Download/DownloadAssetHandler.php +++ b/src/Handler/Asset/Download/DownloadAssetHandler.php @@ -19,13 +19,15 @@ use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\AssetResult; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAsset\DownloadAssetPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; final class DownloadAssetHandler { - public function __invoke(int $id): AssetResult + public function __invoke(DownloadAssetPayload $payload): AssetResult { + $id = $payload->id; $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); if (!$asset->isAllowed('view')) { diff --git a/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailPayload.php b/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailPayload.php new file mode 100644 index 00000000..372df8dc --- /dev/null +++ b/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailPayload.php @@ -0,0 +1,57 @@ +query->getString('thumbnail') ?: null; + $config = $request->query->getString('config') ?: null; + $type = $request->query->getString('type') ?: null; + + $configData = null; + if ($config !== null) { + $configData = json_decode($config, true); + } elseif ($type !== null) { + $predefined = [ + 'web' => ['resize_mode' => 'scaleByWidth', 'width' => 3500, 'dpi' => 72, 'format' => 'JPEG', 'quality' => 85], + 'print' => ['resize_mode' => 'scaleByWidth', 'width' => 6000, 'dpi' => 300, 'format' => 'JPEG', 'quality' => 95], + 'office' => ['resize_mode' => 'scaleByWidth', 'width' => 1190, 'dpi' => 144, 'format' => 'JPEG', 'quality' => 90], + ]; + $configData = $predefined[$type] ?? null; + } + + return new static( + id: $request->query->getInt('id'), + thumbnailName: $thumbnail, + config: $config, + configData: $configData, + ); + } +} diff --git a/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php b/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php index 1afa0bcf..9b736bd4 100644 --- a/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php +++ b/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php @@ -18,18 +18,19 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnail\DownloadImageThumbnailPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Process\Process; final class DownloadImageThumbnailHandler { - public function __invoke( - int $id, - ?string $thumbnailName, - ?string $config, - ?array $configData, - ): DownloadImageThumbnailResult { + public function __invoke(DownloadImageThumbnailPayload $payload): DownloadImageThumbnailResult + { + $id = $payload->id; + $thumbnailName = $payload->thumbnailName; + $config = $payload->config; + $configData = $payload->configData; $image = Asset\Image::getById($id) ?? throw new AssetNotFoundException($id); if (!$image->isAllowed('view')) { diff --git a/src/Handler/Asset/Download/DownloadZip/DownloadZipPayload.php b/src/Handler/Asset/Download/DownloadZip/DownloadZipPayload.php new file mode 100644 index 00000000..7a4fba70 --- /dev/null +++ b/src/Handler/Asset/Download/DownloadZip/DownloadZipPayload.php @@ -0,0 +1,37 @@ +query->getInt('id'), + jobId: $request->query->getString('jobId'), + ); + } +} diff --git a/src/Handler/Asset/Download/DownloadZipHandler.php b/src/Handler/Asset/Download/DownloadZipHandler.php index 31710319..0fbb608f 100644 --- a/src/Handler/Asset/Download/DownloadZipHandler.php +++ b/src/Handler/Asset/Download/DownloadZipHandler.php @@ -18,12 +18,15 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZip\DownloadZipPayload; use OpenDxp\Model\Asset; final class DownloadZipHandler { - public function __invoke(int $id, string $jobId): DownloadZipResult + public function __invoke(DownloadZipPayload $payload): DownloadZipResult { + $id = $payload->id; + $jobId = $payload->jobId; $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/download-zip-' . $jobId . '.zip'; $suggestedFilename = $asset->getFilename() ?: 'assets'; diff --git a/src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsPayload.php b/src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsPayload.php new file mode 100644 index 00000000..00381e06 --- /dev/null +++ b/src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsPayload.php @@ -0,0 +1,37 @@ +query->getInt('id'), + selectedIds: $request->query->getString('selectedIds'), + ); + } +} diff --git a/src/Handler/Asset/Download/GetDownloadZipJobsHandler.php b/src/Handler/Asset/Download/GetDownloadZipJobsHandler.php index 384e26bb..348651d5 100644 --- a/src/Handler/Asset/Download/GetDownloadZipJobsHandler.php +++ b/src/Handler/Asset/Download/GetDownloadZipJobsHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobs\GetDownloadZipJobsPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Db\Helper; use OpenDxp\Model\Asset; @@ -32,8 +33,10 @@ public function __construct( private readonly RouterInterface $router, ) {} - public function __invoke(int $id, string $selectedIds): GetDownloadZipJobsResult + public function __invoke(GetDownloadZipJobsPayload $payload): GetDownloadZipJobsResult { + $id = $payload->id; + $selectedIds = $payload->selectedIds; $adminUser = $this->userContext->getAdminUser(); $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); diff --git a/src/Handler/Asset/Editor/LoadAssetForEditor/LoadAssetForEditorPayload.php b/src/Handler/Asset/Editor/LoadAssetForEditor/LoadAssetForEditorPayload.php new file mode 100644 index 00000000..b20a252e --- /dev/null +++ b/src/Handler/Asset/Editor/LoadAssetForEditor/LoadAssetForEditorPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/Asset/Editor/LoadAssetForEditorHandler.php b/src/Handler/Asset/Editor/LoadAssetForEditorHandler.php index 3891f405..b9dbde53 100644 --- a/src/Handler/Asset/Editor/LoadAssetForEditorHandler.php +++ b/src/Handler/Asset/Editor/LoadAssetForEditorHandler.php @@ -19,13 +19,15 @@ use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\AssetResult; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\LoadAssetForEditor\LoadAssetForEditorPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; final class LoadAssetForEditorHandler { - public function __invoke(int $id): AssetResult + public function __invoke(LoadAssetForEditorPayload $payload): AssetResult { + $id = $payload->id; $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); if (!$asset->isAllowed('view')) { diff --git a/src/Handler/Asset/Editor/SaveImageEditor/SaveImageEditorPayload.php b/src/Handler/Asset/Editor/SaveImageEditor/SaveImageEditorPayload.php new file mode 100644 index 00000000..8cdbf448 --- /dev/null +++ b/src/Handler/Asset/Editor/SaveImageEditor/SaveImageEditorPayload.php @@ -0,0 +1,37 @@ +query->getInt('id'), + dataUri: $request->request->getString('dataUri'), + ); + } +} diff --git a/src/Handler/Asset/Editor/SaveImageEditorHandler.php b/src/Handler/Asset/Editor/SaveImageEditorHandler.php index cdbb2b0a..c741d2cc 100644 --- a/src/Handler/Asset/Editor/SaveImageEditorHandler.php +++ b/src/Handler/Asset/Editor/SaveImageEditorHandler.php @@ -19,6 +19,7 @@ use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\AssetResult; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\SaveImageEditor\SaveImageEditorPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -29,8 +30,10 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $id, string $dataUri): AssetResult + public function __invoke(SaveImageEditorPayload $payload): AssetResult { + $id = $payload->id; + $dataUri = $payload->dataUri; $userId = $this->userContext->getAdminUser()?->getId() ?? 0; $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); diff --git a/src/Handler/Asset/GetAssetChildren/GetAssetChildrenPayload.php b/src/Handler/Asset/GetAssetChildren/GetAssetChildrenPayload.php new file mode 100644 index 00000000..b1a640ba --- /dev/null +++ b/src/Handler/Asset/GetAssetChildren/GetAssetChildrenPayload.php @@ -0,0 +1,51 @@ +query->getString('filter') ?: null; + $limit = $request->query->getInt('limit', 0); + if ($filter !== null) { + $limit = 100; + } elseif (!$limit) { + $limit = 100000000; + } + + return new static( + nodeId: $request->query->getInt('node'), + customViewId: ($request->query->getString('view') ?: null), + filter: $filter, + limit: $limit, + offset: $request->query->getInt('start'), + ); + } +} diff --git a/src/Handler/Asset/GetAssetChildrenHandler.php b/src/Handler/Asset/GetAssetChildrenHandler.php index 2d2e7860..96ab22d2 100644 --- a/src/Handler/Asset/GetAssetChildrenHandler.php +++ b/src/Handler/Asset/GetAssetChildrenHandler.php @@ -19,10 +19,10 @@ use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildren\GetAssetChildrenPayload; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Model\Asset; use OpenDxp\Model\Element; -use OpenDxp\Model\User; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -35,13 +35,13 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke( - int $nodeId, - ?string $customViewId, - ?string $filter, - int $limit, - int $offset, - ): GetAssetChildrenResult { + public function __invoke(GetAssetChildrenPayload $payload): GetAssetChildrenResult + { + $nodeId = $payload->nodeId; + $customViewId = $payload->customViewId; + $filter = $payload->filter; + $limit = $payload->limit; + $offset = $payload->offset; $asset = Asset::getById($nodeId); if (!$asset instanceof Asset) { throw new AssetNotFoundException($nodeId); diff --git a/src/Handler/Asset/GetAssetData/GetAssetDataPayload.php b/src/Handler/Asset/GetAssetData/GetAssetDataPayload.php new file mode 100644 index 00000000..79494937 --- /dev/null +++ b/src/Handler/Asset/GetAssetData/GetAssetDataPayload.php @@ -0,0 +1,37 @@ +query->getInt('id'), + requestSchemeAndHost: $request->getSchemeAndHttpHost(), + ); + } +} diff --git a/src/Handler/Asset/GetAssetDataHandler.php b/src/Handler/Asset/GetAssetDataHandler.php index da8d8fbc..56505945 100644 --- a/src/Handler/Asset/GetAssetDataHandler.php +++ b/src/Handler/Asset/GetAssetDataHandler.php @@ -20,6 +20,7 @@ use Exception; use OpenDxp; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetData\GetAssetDataPayload; use OpenDxp\Bundle\AdminBundle\Event\ElementAdminStyleEvent; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; @@ -47,8 +48,10 @@ public function __construct( private readonly EditLockService $editLockService, ) {} - public function __invoke(int $id, string $requestSchemeAndHost): GetAssetDataResult + public function __invoke(GetAssetDataPayload $payload): GetAssetDataResult { + $id = $payload->id; + $requestSchemeAndHost = $payload->requestSchemeAndHost; $asset = Asset::getById($id); if (!$asset instanceof Asset) { throw new AssetNotFoundException($id); diff --git a/src/Handler/Asset/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigPayload.php b/src/Handler/Asset/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigPayload.php new file mode 100644 index 00000000..91171085 --- /dev/null +++ b/src/Handler/Asset/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigPayload.php @@ -0,0 +1,35 @@ +request->getInt('gridConfigId'), + ); + } +} diff --git a/src/Handler/Asset/Helper/DeleteGridColumnConfigHandler.php b/src/Handler/Asset/Helper/DeleteGridColumnConfigHandler.php index 202e308b..20cd074f 100644 --- a/src/Handler/Asset/Helper/DeleteGridColumnConfigHandler.php +++ b/src/Handler/Asset/Helper/DeleteGridColumnConfigHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DeleteGridColumnConfig\DeleteGridColumnConfigPayload; use OpenDxp\Bundle\AdminBundle\Model\GridConfig; use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -29,8 +30,9 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $gridConfigId): void + public function __invoke(DeleteGridColumnConfigPayload $payload): void { + $gridConfigId = $payload->gridConfigId; $adminUser = $this->userContext->getAdminUser(); $gridConfig = GridConfig::getById($gridConfigId); if (!$gridConfig) { diff --git a/src/Handler/Asset/Helper/DoAssetExport/DoAssetExportPayload.php b/src/Handler/Asset/Helper/DoAssetExport/DoAssetExportPayload.php new file mode 100644 index 00000000..d35a4b1c --- /dev/null +++ b/src/Handler/Asset/Helper/DoAssetExport/DoAssetExportPayload.php @@ -0,0 +1,51 @@ +request->getString('settings'), true) ?? []; + $fields = json_decode($request->request->all('fields')[0] ?? '[]', true) ?? []; + + return new static( + fileHandle: File::getValidFilename($request->request->getString('fileHandle')), + ids: $request->request->all('ids'), + delimiter: $settings['delimiter'] ?? ';', + language: str_replace('default', '', $request->request->getString('language')), + header: $settings['header'] ?? 'title', + fields: $fields, + addTitles: (bool) $request->request->get('initial'), + ); + } +} diff --git a/src/Handler/Asset/Helper/DoAssetExportHandler.php b/src/Handler/Asset/Helper/DoAssetExportHandler.php index 1ae21ad9..3f2c6984 100644 --- a/src/Handler/Asset/Helper/DoAssetExportHandler.php +++ b/src/Handler/Asset/Helper/DoAssetExportHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; use League\Flysystem\UnableToReadFile; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExport\DoAssetExportPayload; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridColumnConfigService; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; use OpenDxp\Logger; @@ -33,15 +34,15 @@ public function __construct( private readonly GridColumnConfigService $gridColumnConfigService, ) {} - public function __invoke( - string $fileHandle, - array $ids, - string $delimiter, - string $language, - string $header, - array $fields, - bool $addTitles, - ): void { + public function __invoke(DoAssetExportPayload $payload): void + { + $fileHandle = $payload->fileHandle; + $ids = $payload->ids; + $delimiter = $payload->delimiter; + $language = $payload->language; + $header = $payload->header; + $fields = $payload->fields; + $addTitles = $payload->addTitles; $list = new Asset\Listing(); $quotedIds = []; diff --git a/src/Handler/Asset/Helper/ExecuteAssetBatch/ExecuteAssetBatchPayload.php b/src/Handler/Asset/Helper/ExecuteAssetBatch/ExecuteAssetBatchPayload.php new file mode 100644 index 00000000..e1313561 --- /dev/null +++ b/src/Handler/Asset/Helper/ExecuteAssetBatch/ExecuteAssetBatchPayload.php @@ -0,0 +1,37 @@ +request->getString('data'); + + return new static( + data: $raw !== '' ? json_decode($raw, true) : null, + ); + } +} diff --git a/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php b/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php index 75b7678e..aa24b5d6 100644 --- a/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php +++ b/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatch\ExecuteAssetBatchPayload; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService; use OpenDxp\Model\User; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -28,8 +29,9 @@ public function __construct( private readonly GridBatchService $gridBatchService, ) {} - public function __invoke(array $data): void + public function __invoke(ExecuteAssetBatchPayload $payload): void { + $data = $payload->data ?? []; $adminUser = $this->userContext->getAdminUser(); // Returns false when there is no asset to update (job already completed) — not an error. // Throws on permission denied or save failure. diff --git a/src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsPayload.php b/src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsPayload.php new file mode 100644 index 00000000..c0b908cd --- /dev/null +++ b/src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsPayload.php @@ -0,0 +1,39 @@ +request->getString('language') ?: null; + + return new static( + allParams: [...$request->request->all(), ...$request->query->all()], + language: $language, + ); + } +} diff --git a/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php b/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php index 6cab2f70..0bd1cf2c 100644 --- a/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php +++ b/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobs\GetAssetBatchJobsPayload; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService; use OpenDxp\Model\User; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -28,8 +29,9 @@ public function __construct( private readonly GridBatchService $gridBatchService, ) {} - public function __invoke(array $allParams): GetAssetBatchJobsResult + public function __invoke(GetAssetBatchJobsPayload $payload): GetAssetBatchJobsResult { + $allParams = $payload->allParams; $adminUser = $this->userContext->getAdminUser(); return new GetAssetBatchJobsResult( $this->gridBatchService->getAssetBatchJobIds($allParams, $adminUser), diff --git a/src/Handler/Asset/Helper/GetExportJobs/GetExportJobsPayload.php b/src/Handler/Asset/Helper/GetExportJobs/GetExportJobsPayload.php new file mode 100644 index 00000000..f7ad1c47 --- /dev/null +++ b/src/Handler/Asset/Helper/GetExportJobs/GetExportJobsPayload.php @@ -0,0 +1,35 @@ +request->all(), ...$request->query->all()], + ); + } +} diff --git a/src/Handler/Asset/Helper/GetExportJobsHandler.php b/src/Handler/Asset/Helper/GetExportJobsHandler.php index 89e788ec..389e7adc 100644 --- a/src/Handler/Asset/Helper/GetExportJobsHandler.php +++ b/src/Handler/Asset/Helper/GetExportJobsHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobs\GetExportJobsPayload; use OpenDxp\Bundle\AdminBundle\Helper\GridHelperService; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; use OpenDxp\Model\User; @@ -31,8 +32,9 @@ public function __construct( private readonly GridExportService $gridExportService, ) {} - public function __invoke(array $allParams): GetExportJobsResult + public function __invoke(GetExportJobsPayload $payload): GetExportJobsResult { + $allParams = $payload->allParams; $adminUser = $this->userContext->getAdminUser(); $list = $this->gridHelperService->prepareAssetListingForGrid($allParams, $adminUser); diff --git a/src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouritePayload.php b/src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouritePayload.php new file mode 100644 index 00000000..6f79f285 --- /dev/null +++ b/src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouritePayload.php @@ -0,0 +1,41 @@ +request->getString('classId') ?: null, + gridConfigId: $request->request->getInt('gridConfigId'), + searchType: $request->request->getString('searchType') ?: null, + type: $request->request->getString('type') ?: null, + ); + } +} diff --git a/src/Handler/Asset/Helper/MarkGridConfigFavouriteHandler.php b/src/Handler/Asset/Helper/MarkGridConfigFavouriteHandler.php index aff2acc6..9e1ac5e8 100644 --- a/src/Handler/Asset/Helper/MarkGridConfigFavouriteHandler.php +++ b/src/Handler/Asset/Helper/MarkGridConfigFavouriteHandler.php @@ -18,10 +18,10 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; use Exception; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavourite\MarkGridConfigFavouritePayload; use OpenDxp\Bundle\AdminBundle\Model\GridConfig; use OpenDxp\Bundle\AdminBundle\Model\GridConfigFavourite; use OpenDxp\Model\Asset; -use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -31,12 +31,12 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke( - ?string $classId, - int $gridConfigId, - ?string $searchType, - ?string $type, - ): MarkGridConfigFavouriteResult { + public function __invoke(MarkGridConfigFavouritePayload $payload): MarkGridConfigFavouriteResult + { + $classId = $payload->classId; + $gridConfigId = $payload->gridConfigId; + $searchType = $payload->searchType; + $type = $payload->type; $adminUser = $this->userContext->getAdminUser(); $asset = Asset::getById(is_numeric($classId) ? (int) $classId : 0); diff --git a/src/Handler/Asset/Helper/SaveGridColumnConfig/SaveGridColumnConfigPayload.php b/src/Handler/Asset/Helper/SaveGridColumnConfig/SaveGridColumnConfigPayload.php new file mode 100644 index 00000000..8687ad63 --- /dev/null +++ b/src/Handler/Asset/Helper/SaveGridColumnConfig/SaveGridColumnConfigPayload.php @@ -0,0 +1,50 @@ +request->getString('gridconfig'); + $settings = $request->request->getString('settings'); + + return new static( + assetId: $request->request->getInt('id'), + classId: $request->request->getString('class_id') ?: null, + context: $request->request->getString('context') ?: null, + searchType: $request->request->getString('searchType') ?: null, + type: $request->request->getString('type') ?: null, + gridConfigData: $gridconfig ? (json_decode($gridconfig, true) ?? []) : [], + metadata: $settings ? json_decode($settings, true) : null, + ); + } +} diff --git a/src/Handler/Asset/Helper/SaveGridColumnConfigHandler.php b/src/Handler/Asset/Helper/SaveGridColumnConfigHandler.php index a566e188..c79f9454 100644 --- a/src/Handler/Asset/Helper/SaveGridColumnConfigHandler.php +++ b/src/Handler/Asset/Helper/SaveGridColumnConfigHandler.php @@ -17,10 +17,10 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\SaveGridColumnConfig\SaveGridColumnConfigPayload; use OpenDxp\Bundle\AdminBundle\Model\GridConfig; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridColumnConfigService; use OpenDxp\Model\Asset; -use OpenDxp\Model\User; use OpenDxp\Version; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -34,15 +34,15 @@ public function __construct( private readonly GridColumnConfigService $gridColumnConfigService, ) {} - public function __invoke( - int $assetId, - ?string $classId, - ?string $context, - ?string $searchType, - ?string $type, - array $gridConfigData, - ?array $metadata, - ): SaveGridColumnConfigResult { + public function __invoke(SaveGridColumnConfigPayload $payload): SaveGridColumnConfigResult + { + $assetId = $payload->assetId; + $classId = $payload->classId; + $context = $payload->context; + $searchType = $payload->searchType; + $type = $payload->type; + $gridConfigData = $payload->gridConfigData; + $metadata = $payload->metadata; $adminUser = $this->userContext->getAdminUser(); $asset = Asset::getById($assetId); if (!$asset) { diff --git a/src/Handler/Asset/Media/GetAssetText/GetAssetTextPayload.php b/src/Handler/Asset/Media/GetAssetText/GetAssetTextPayload.php new file mode 100644 index 00000000..34c7adba --- /dev/null +++ b/src/Handler/Asset/Media/GetAssetText/GetAssetTextPayload.php @@ -0,0 +1,39 @@ +query->has('page') ? $request->query->getInt('page') : null; + + return new static( + id: $request->query->getInt('id'), + page: $page, + ); + } +} diff --git a/src/Handler/Asset/Media/GetAssetTextHandler.php b/src/Handler/Asset/Media/GetAssetTextHandler.php index 0b4035a3..5235c22c 100644 --- a/src/Handler/Asset/Media/GetAssetTextHandler.php +++ b/src/Handler/Asset/Media/GetAssetTextHandler.php @@ -18,13 +18,16 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Media; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetAssetText\GetAssetTextPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; final class GetAssetTextHandler { - public function __invoke(int $id, ?int $page): AssetTextResult + public function __invoke(GetAssetTextPayload $payload): AssetTextResult { + $id = $payload->id; + $page = $payload->page; $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); if (!$asset->isAllowed('view')) { diff --git a/src/Handler/Asset/Media/GetDocumentPreview/GetDocumentPreviewPayload.php b/src/Handler/Asset/Media/GetDocumentPreview/GetDocumentPreviewPayload.php new file mode 100644 index 00000000..d8a04f75 --- /dev/null +++ b/src/Handler/Asset/Media/GetDocumentPreview/GetDocumentPreviewPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/Asset/Media/GetDocumentPreviewHandler.php b/src/Handler/Asset/Media/GetDocumentPreviewHandler.php index fe674287..0af811b7 100644 --- a/src/Handler/Asset/Media/GetDocumentPreviewHandler.php +++ b/src/Handler/Asset/Media/GetDocumentPreviewHandler.php @@ -19,6 +19,7 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetDocumentPreview\GetDocumentPreviewPayload; use OpenDxp\Config; use OpenDxp\Model\Asset; use OpenDxp\Model\Asset\Enum\PdfScanStatus; @@ -28,8 +29,9 @@ final class GetDocumentPreviewHandler { private const string PDF_MIMETYPE = 'application/pdf'; - public function __invoke(int $id): PreviewDocumentResult + public function __invoke(GetDocumentPreviewPayload $payload): PreviewDocumentResult { + $id = $payload->id; $asset = Asset\Document::getById($id); if (!$asset instanceof Asset\Document) { throw new AssetNotFoundException($id); diff --git a/src/Handler/Asset/Media/GetVideoPreview/GetVideoPreviewPayload.php b/src/Handler/Asset/Media/GetVideoPreview/GetVideoPreviewPayload.php new file mode 100644 index 00000000..f475b493 --- /dev/null +++ b/src/Handler/Asset/Media/GetVideoPreview/GetVideoPreviewPayload.php @@ -0,0 +1,37 @@ +query->getInt('id'), + configName: $request->query->getString('config') ?: null, + ); + } +} diff --git a/src/Handler/Asset/Media/GetVideoPreviewHandler.php b/src/Handler/Asset/Media/GetVideoPreviewHandler.php index 84c01568..4bbf0747 100644 --- a/src/Handler/Asset/Media/GetVideoPreviewHandler.php +++ b/src/Handler/Asset/Media/GetVideoPreviewHandler.php @@ -18,13 +18,16 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Media; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetVideoPreview\GetVideoPreviewPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; final class GetVideoPreviewHandler { - public function __invoke(int $id, ?string $configName): PreviewVideoResult + public function __invoke(GetVideoPreviewPayload $payload): PreviewVideoResult { + $id = $payload->id; + $configName = $payload->configName; $asset = Asset\Video::getById($id) ?? throw new AssetNotFoundException($id); if (!$asset->isAllowed('view')) { diff --git a/src/Handler/Asset/Media/ServeVideoPreview/ServeVideoPreviewPayload.php b/src/Handler/Asset/Media/ServeVideoPreview/ServeVideoPreviewPayload.php new file mode 100644 index 00000000..cf3facbf --- /dev/null +++ b/src/Handler/Asset/Media/ServeVideoPreview/ServeVideoPreviewPayload.php @@ -0,0 +1,37 @@ +query->getInt('id'), + configName: $request->query->getString('config') ?: null, + ); + } +} diff --git a/src/Handler/Asset/Media/ServeVideoPreviewHandler.php b/src/Handler/Asset/Media/ServeVideoPreviewHandler.php index 9b9b8ad0..d4c579fb 100644 --- a/src/Handler/Asset/Media/ServeVideoPreviewHandler.php +++ b/src/Handler/Asset/Media/ServeVideoPreviewHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Media; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\ServeVideoPreview\ServeVideoPreviewPayload; use OpenDxp\Model\Asset; use OpenDxp\Tool; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -25,8 +26,10 @@ final class ServeVideoPreviewHandler { - public function __invoke(int $id, ?string $configName): ServeVideoPreviewResult + public function __invoke(ServeVideoPreviewPayload $payload): ServeVideoPreviewResult { + $id = $payload->id; + $configName = $payload->configName; $asset = Asset\Video::getById($id) ?? throw new AssetNotFoundException($id); if (!$asset->isAllowed('view')) { diff --git a/src/Handler/Asset/SaveAssetHandler.php b/src/Handler/Asset/SaveAsset/SaveAssetHandler.php similarity index 81% rename from src/Handler/Asset/SaveAssetHandler.php rename to src/Handler/Asset/SaveAsset/SaveAssetHandler.php index 1b126d79..d2cb2968 100644 --- a/src/Handler/Asset/SaveAssetHandler.php +++ b/src/Handler/Asset/SaveAsset/SaveAssetHandler.php @@ -15,9 +15,8 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset; -use OpenDxp\Bundle\AdminBundle\Payload\Asset\AssetPayload; use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetPayloadMapper; use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetPersistenceCoordinator; use OpenDxp\Model\Asset; @@ -31,9 +30,9 @@ public function __construct( private readonly AssetPersistenceCoordinator $coordinator, ) {} - public function __invoke(int $assetId, AssetPayload $payload): SaveAssetResult + public function __invoke(SaveAssetPayload $payload): SaveAssetResult { - $asset = Asset::getById($assetId) ?? throw new NotFoundHttpException('Asset not found'); + $asset = Asset::getById($payload->id) ?? throw new NotFoundHttpException('Asset not found'); if (!$asset->isAllowed('publish')) { throw new AccessDeniedHttpException(); diff --git a/src/Payload/Asset/AssetPayload.php b/src/Handler/Asset/SaveAsset/SaveAssetPayload.php similarity index 69% rename from src/Payload/Asset/AssetPayload.php rename to src/Handler/Asset/SaveAsset/SaveAssetPayload.php index 1ab5c177..ec607b25 100644 --- a/src/Payload/Asset/AssetPayload.php +++ b/src/Handler/Asset/SaveAsset/SaveAssetPayload.php @@ -14,13 +14,15 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -final class AssetPayload +final readonly class SaveAssetPayload implements ExtJsPayloadInterface { public function __construct( + public readonly int $id, public readonly string $task, public readonly ?array $metadata, public readonly ?array $propertiesData, @@ -30,13 +32,14 @@ public function __construct( public readonly ?array $imageData, ) {} - public static function fromRequest(Request $request): self + public static function fromRequest(Request $request): static { $hasImage = $request->request->has('image'); - return new self( - task: $request->request->getString('task'), - metadata: $request->request->has('metadata') + return new static( + id: $request->request->getInt('id'), + task: $request->request->getString('task'), + metadata: $request->request->has('metadata') ? (json_decode($request->request->getString('metadata'), true) ?? null) : null, propertiesData: $request->request->has('properties') @@ -45,9 +48,9 @@ public static function fromRequest(Request $request): self schedulerData: $request->request->has('scheduler') ? (json_decode($request->request->getString('scheduler'), true) ?? null) : null, - rawData: $request->request->get('data'), - hasImage: $hasImage, - imageData: $hasImage + rawData: $request->request->get('data'), + hasImage: $hasImage, + imageData: $hasImage ? (json_decode($request->request->getString('image'), true) ?? null) : null, ); diff --git a/src/Handler/Asset/SaveAssetResult.php b/src/Handler/Asset/SaveAsset/SaveAssetResult.php similarity index 91% rename from src/Handler/Asset/SaveAssetResult.php rename to src/Handler/Asset/SaveAsset/SaveAssetResult.php index 8dc766b3..d0b69404 100644 --- a/src/Handler/Asset/SaveAssetResult.php +++ b/src/Handler/Asset/SaveAsset/SaveAssetResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset; final readonly class SaveAssetResult { diff --git a/src/Handler/Asset/Thumbnail/GetDocumentThumbnail/GetDocumentThumbnailPayload.php b/src/Handler/Asset/Thumbnail/GetDocumentThumbnail/GetDocumentThumbnailPayload.php new file mode 100644 index 00000000..5f07ba20 --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetDocumentThumbnail/GetDocumentThumbnailPayload.php @@ -0,0 +1,43 @@ +query->getInt('id'), + hasThumbnailPreview: $request->query->has('treepreview'), + page: $request->query->has('page') ? $request->query->getInt('page') : null, + origin: $request->query->getString('origin') ?: null, + queryAll: $request->query->all(), + ); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetDocumentThumbnailHandler.php b/src/Handler/Asset/Thumbnail/GetDocumentThumbnailHandler.php index a8fe33a5..7a913b20 100644 --- a/src/Handler/Asset/Thumbnail/GetDocumentThumbnailHandler.php +++ b/src/Handler/Asset/Thumbnail/GetDocumentThumbnailHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetDocumentThumbnail\GetDocumentThumbnailPayload; use OpenDxp\Messenger\AssetPreviewImageMessage; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -28,13 +29,13 @@ final class GetDocumentThumbnailHandler { public function __construct(private readonly MessageBusInterface $messageBus) {} - public function __invoke( - int $id, - bool $hasThumbnailPreview, - ?int $page, - ?string $origin, - array $queryAll, - ): GetDocumentThumbnailResult { + public function __invoke(GetDocumentThumbnailPayload $payload): GetDocumentThumbnailResult + { + $id = $payload->id; + $hasThumbnailPreview = $payload->hasThumbnailPreview; + $page = $payload->page; + $origin = $payload->origin; + $queryAll = $payload->queryAll; $document = Asset\Document::getById($id) ?? throw new AssetNotFoundException($id); if (!$document->isAllowed('view')) { diff --git a/src/Handler/Asset/Thumbnail/GetFolderContentPreview/GetFolderContentPreviewPayload.php b/src/Handler/Asset/Thumbnail/GetFolderContentPreview/GetFolderContentPreviewPayload.php new file mode 100644 index 00000000..88d5f6c6 --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetFolderContentPreview/GetFolderContentPreviewPayload.php @@ -0,0 +1,35 @@ +query->all(), + ); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetFolderContentPreviewHandler.php b/src/Handler/Asset/Thumbnail/GetFolderContentPreviewHandler.php index faf2d023..aae3a6e5 100644 --- a/src/Handler/Asset/Thumbnail/GetFolderContentPreviewHandler.php +++ b/src/Handler/Asset/Thumbnail/GetFolderContentPreviewHandler.php @@ -18,12 +18,12 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderContentPreview\GetFolderContentPreviewPayload; use OpenDxp\Bundle\AdminBundle\Helper\GridHelperService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Db\Helper; use OpenDxp\Model\Asset; use OpenDxp\Model\Element; -use OpenDxp\Model\User; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -37,8 +37,9 @@ public function __construct( private readonly ElementServiceInterface $elementService, ) {} - public function __invoke(array $requestParams): GetFolderContentPreviewResult + public function __invoke(GetFolderContentPreviewPayload $payload): GetFolderContentPreviewResult { + $requestParams = $payload->requestParams; $adminUser = $this->userContext->getAdminUser(); $filterPrepareEvent = new GenericEvent(null, ['requestParams' => $requestParams]); $this->eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE); diff --git a/src/Handler/Asset/Thumbnail/GetFolderThumbnail/GetFolderThumbnailPayload.php b/src/Handler/Asset/Thumbnail/GetFolderThumbnail/GetFolderThumbnailPayload.php new file mode 100644 index 00000000..d8330d24 --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetFolderThumbnail/GetFolderThumbnailPayload.php @@ -0,0 +1,35 @@ +query->has('id') ? $request->query->getInt('id') : null; + + return new static(id: $id); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetFolderThumbnailHandler.php b/src/Handler/Asset/Thumbnail/GetFolderThumbnailHandler.php index 5175299f..0e5da8d4 100644 --- a/src/Handler/Asset/Thumbnail/GetFolderThumbnailHandler.php +++ b/src/Handler/Asset/Thumbnail/GetFolderThumbnailHandler.php @@ -17,14 +17,16 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderThumbnail\GetFolderThumbnailPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class GetFolderThumbnailHandler { - public function __invoke(?int $id): FolderThumbnailResult + public function __invoke(GetFolderThumbnailPayload $payload): FolderThumbnailResult { + $id = $payload->id; if ($id === null) { throw new NotFoundHttpException('could not load asset folder'); } diff --git a/src/Handler/Asset/Thumbnail/GetImageThumbnail/GetImageThumbnailPayload.php b/src/Handler/Asset/Thumbnail/GetImageThumbnail/GetImageThumbnailPayload.php new file mode 100644 index 00000000..1dba5d1e --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetImageThumbnail/GetImageThumbnailPayload.php @@ -0,0 +1,60 @@ +query->getString('config') ?: null; + $cropPercent = $request->query->getString('cropPercent') ?: null; + + return new static( + id: $request->query->getInt('id'), + hasFileinfo: $request->query->has('fileinfo'), + thumbnailParam: $request->query->all('thumbnail') ?: null, + configDecoded: $config !== null ? json_decode($config, true) : null, + queryAll: $request->query->all(), + hasThumbnailPreview: $request->query->has('treepreview'), + origin: $request->query->getString('origin') ?: null, + hasCropPercent: $cropPercent !== null && filter_var($cropPercent, FILTER_VALIDATE_BOOLEAN), + cropWidth: $request->query->getString('cropWidth') ?: null, + cropHeight: $request->query->getString('cropHeight') ?: null, + cropTop: $request->query->getString('cropTop') ?: null, + cropLeft: $request->query->getString('cropLeft') ?: null, + ); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetImageThumbnailHandler.php b/src/Handler/Asset/Thumbnail/GetImageThumbnailHandler.php index 71957390..cb4b9603 100644 --- a/src/Handler/Asset/Thumbnail/GetImageThumbnailHandler.php +++ b/src/Handler/Asset/Thumbnail/GetImageThumbnailHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetImageThumbnail\GetImageThumbnailPayload; use OpenDxp\Messenger\AssetPreviewImageMessage; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -27,20 +28,20 @@ final class GetImageThumbnailHandler { public function __construct(private readonly MessageBusInterface $messageBus) {} - public function __invoke( - int $id, - bool $hasFileinfo, - ?array $thumbnailParam, - ?array $configDecoded, - array $queryAll, - bool $hasThumbnailPreview, - ?string $origin, - bool $hasCropPercent, - ?string $cropWidth, - ?string $cropHeight, - ?string $cropTop, - ?string $cropLeft, - ): GetImageThumbnailResult { + public function __invoke(GetImageThumbnailPayload $payload): GetImageThumbnailResult + { + $id = $payload->id; + $hasFileinfo = $payload->hasFileinfo; + $thumbnailParam = $payload->thumbnailParam; + $configDecoded = $payload->configDecoded; + $queryAll = $payload->queryAll; + $hasThumbnailPreview = $payload->hasThumbnailPreview; + $origin = $payload->origin; + $hasCropPercent = $payload->hasCropPercent; + $cropWidth = $payload->cropWidth; + $cropHeight = $payload->cropHeight; + $cropTop = $payload->cropTop; + $cropLeft = $payload->cropLeft; $image = Asset\Image::getById($id) ?? throw new AssetNotFoundException($id); if (!$image->isAllowed('view')) { diff --git a/src/Handler/Asset/Thumbnail/GetVideoThumbnail/GetVideoThumbnailPayload.php b/src/Handler/Asset/Thumbnail/GetVideoThumbnail/GetVideoThumbnailPayload.php new file mode 100644 index 00000000..7b201aaf --- /dev/null +++ b/src/Handler/Asset/Thumbnail/GetVideoThumbnail/GetVideoThumbnailPayload.php @@ -0,0 +1,53 @@ +query->has('id') ? $request->query->getInt('id') : null, + path: $request->query->getString('path') ?: null, + hasThumbnailPreview: $request->query->has('treepreview'), + hasSetTime: $request->query->has('settime'), + hasSetImage: $request->query->has('setimage'), + hasImage: $request->query->has('image'), + imageId: $request->query->getInt('image'), + time: $request->query->getString('time') ?: null, + origin: $request->query->getString('origin') ?: null, + queryAll: $request->query->all(), + ); + } +} diff --git a/src/Handler/Asset/Thumbnail/GetVideoThumbnailHandler.php b/src/Handler/Asset/Thumbnail/GetVideoThumbnailHandler.php index 379968be..d72d6e19 100644 --- a/src/Handler/Asset/Thumbnail/GetVideoThumbnailHandler.php +++ b/src/Handler/Asset/Thumbnail/GetVideoThumbnailHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetVideoThumbnail\GetVideoThumbnailPayload; use OpenDxp\Messenger\AssetPreviewImageMessage; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -28,18 +29,18 @@ final class GetVideoThumbnailHandler { public function __construct(private readonly MessageBusInterface $messageBus) {} - public function __invoke( - ?int $id, - ?string $path, - bool $hasThumbnailPreview, - bool $hasSetTime, - bool $hasSetImage, - bool $hasImage, - int $imageId, - ?string $time, - ?string $origin, - array $queryAll, - ): GetVideoThumbnailResult { + public function __invoke(GetVideoThumbnailPayload $payload): GetVideoThumbnailResult + { + $id = $payload->id; + $path = $payload->path; + $hasThumbnailPreview = $payload->hasThumbnailPreview; + $hasSetTime = $payload->hasSetTime; + $hasSetImage = $payload->hasSetImage; + $hasImage = $payload->hasImage; + $imageId = $payload->imageId; + $time = $payload->time; + $origin = $payload->origin; + $queryAll = $payload->queryAll; $video = null; if ($id !== null) { diff --git a/src/Handler/Asset/UpdateAsset/UpdateAssetPayload.php b/src/Handler/Asset/UpdateAsset/UpdateAssetPayload.php new file mode 100644 index 00000000..acf8055a --- /dev/null +++ b/src/Handler/Asset/UpdateAsset/UpdateAssetPayload.php @@ -0,0 +1,37 @@ +request->getInt('id'), + updateData: [...$request->request->all(), ...$request->query->all()], + ); + } +} diff --git a/src/Handler/Asset/UpdateAssetHandler.php b/src/Handler/Asset/UpdateAssetHandler.php index e130d2b7..94bc3e73 100644 --- a/src/Handler/Asset/UpdateAssetHandler.php +++ b/src/Handler/Asset/UpdateAssetHandler.php @@ -17,10 +17,10 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\UpdateAsset\UpdateAssetPayload; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Logger; use OpenDxp\Model\Asset; -use OpenDxp\Model\User; use RuntimeException; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -31,8 +31,10 @@ final class UpdateAssetHandler public function __construct( private readonly AdminUserContextInterface $userContext,private readonly ElementServiceInterface $elementService) {} - public function __invoke(int $assetId, array $updateData): UpdateAssetResult + public function __invoke(UpdateAssetPayload $payload): UpdateAssetResult { + $assetId = $payload->assetId; + $updateData = $payload->updateData; $adminUser = $this->userContext->getAdminUser(); $asset = Asset::getById($assetId); $allowUpdate = true; diff --git a/src/Handler/Asset/Upload/CheckAssetExists/CheckAssetExistsPayload.php b/src/Handler/Asset/Upload/CheckAssetExists/CheckAssetExistsPayload.php new file mode 100644 index 00000000..57886439 --- /dev/null +++ b/src/Handler/Asset/Upload/CheckAssetExists/CheckAssetExistsPayload.php @@ -0,0 +1,39 @@ +query->getInt('parentId'), + filename: $request->query->getString('filename'), + dir: $request->query->getString('dir'), + ); + } +} diff --git a/src/Handler/Asset/Upload/CheckAssetExistsHandler.php b/src/Handler/Asset/Upload/CheckAssetExistsHandler.php index 7db44212..a511bd61 100644 --- a/src/Handler/Asset/Upload/CheckAssetExistsHandler.php +++ b/src/Handler/Asset/Upload/CheckAssetExistsHandler.php @@ -17,14 +17,18 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\CheckAssetExists\CheckAssetExistsPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class CheckAssetExistsHandler { - public function __invoke(int $parentId, string $filename, string $dir): bool + public function __invoke(CheckAssetExistsPayload $payload): bool { + $parentId = $payload->parentId; + $filename = $payload->filename; + $dir = $payload->dir; $parentAsset = Asset::getById($parentId); if (!$parentAsset) { throw new NotFoundHttpException('Parent asset not found'); diff --git a/src/Handler/Asset/Upload/ImportZip/ImportZipPayload.php b/src/Handler/Asset/Upload/ImportZip/ImportZipPayload.php new file mode 100644 index 00000000..6fe06ae1 --- /dev/null +++ b/src/Handler/Asset/Upload/ImportZip/ImportZipPayload.php @@ -0,0 +1,43 @@ +files->get('Filedata'); + + return new static( + parentId: $request->query->getInt('parentId'), + uploadedFilePath: $file->getPathname(), + allowOverwrite: $request->query->getString('allowOverwrite') ?: null, + ); + } +} diff --git a/src/Handler/Asset/Upload/ImportZipFiles/ImportZipFilesPayload.php b/src/Handler/Asset/Upload/ImportZipFiles/ImportZipFilesPayload.php new file mode 100644 index 00000000..b14eac1d --- /dev/null +++ b/src/Handler/Asset/Upload/ImportZipFiles/ImportZipFilesPayload.php @@ -0,0 +1,45 @@ +request->getInt('parentId'), + jobId: $request->request->getString('jobId'), + offset: $request->request->getInt('offset'), + limit: $request->request->getInt('limit'), + allowOverwrite: $request->request->getString('allowOverwrite') === 'true', + isLast: (bool) $request->request->get('last'), + ); + } +} diff --git a/src/Handler/Asset/Upload/ImportZipFilesHandler.php b/src/Handler/Asset/Upload/ImportZipFilesHandler.php index 00756a80..12b747eb 100644 --- a/src/Handler/Asset/Upload/ImportZipFilesHandler.php +++ b/src/Handler/Asset/Upload/ImportZipFilesHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZipFiles\ImportZipFilesPayload; use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetUploadService; use OpenDxp\File; use OpenDxp\Logger; @@ -34,14 +35,14 @@ public function __construct( private readonly Filesystem $filesystem, ) {} - public function __invoke( - int $parentId, - string $jobId, - int $offset, - int $limit, - bool $allowOverwrite, - bool $isLast, - ): void { + public function __invoke(ImportZipFilesPayload $payload): void + { + $parentId = $payload->parentId; + $jobId = $payload->jobId; + $offset = $payload->offset; + $limit = $payload->limit; + $allowOverwrite = $payload->allowOverwrite; + $isLast = $payload->isLast; $userId = $this->userContext->getAdminUser()?->getId() ?? 0; $importAsset = Asset::getById($parentId); $zipFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . $jobId . '.zip'; diff --git a/src/Handler/Asset/Upload/ImportZipHandler.php b/src/Handler/Asset/Upload/ImportZipHandler.php index d05e7818..b5392582 100644 --- a/src/Handler/Asset/Upload/ImportZipHandler.php +++ b/src/Handler/Asset/Upload/ImportZipHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZip\ImportZipPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -34,8 +35,11 @@ public function __construct( private readonly RouterInterface $router, ) {} - public function __invoke(int $parentId, string $uploadedFilePath, ?string $allowOverwrite): ImportZipResult + public function __invoke(ImportZipPayload $payload): ImportZipResult { + $parentId = $payload->parentId; + $uploadedFilePath = $payload->uploadedFilePath; + $allowOverwrite = $payload->allowOverwrite; $asset = Asset::getById($parentId) ?? throw new NotFoundHttpException('Parent asset not found'); if (!$asset->isAllowed('create')) { diff --git a/src/Handler/Asset/Upload/ReplaceAsset/ReplaceAssetPayload.php b/src/Handler/Asset/Upload/ReplaceAsset/ReplaceAssetPayload.php new file mode 100644 index 00000000..beefd3b5 --- /dev/null +++ b/src/Handler/Asset/Upload/ReplaceAsset/ReplaceAssetPayload.php @@ -0,0 +1,43 @@ +files->get('Filedata'); + + return new static( + id: $request->query->getInt('id'), + filePath: $file->getPathname(), + originalFilename: $file->getClientOriginalName(), + ); + } +} diff --git a/src/Handler/Asset/Upload/ReplaceAssetHandler.php b/src/Handler/Asset/Upload/ReplaceAssetHandler.php index d16a9ac7..47d9c89d 100644 --- a/src/Handler/Asset/Upload/ReplaceAssetHandler.php +++ b/src/Handler/Asset/Upload/ReplaceAssetHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ReplaceAsset\ReplaceAssetPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Element; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -31,8 +32,11 @@ final class ReplaceAssetHandler public function __construct( private readonly AdminUserContextInterface $userContext,private readonly TranslatorInterface $translator) {} - public function __invoke(int $id, string $filePath, string $originalFilename): Asset + public function __invoke(ReplaceAssetPayload $payload): Asset { + $id = $payload->id; + $filePath = $payload->filePath; + $originalFilename = $payload->originalFilename; $userId = $this->userContext->getAdminUser()?->getId() ?? 0; $asset = Asset::getById($id) ?? throw new AssetNotFoundException($id); diff --git a/src/Handler/Asset/Version/PublishVersion/PublishVersionPayload.php b/src/Handler/Asset/Version/PublishVersion/PublishVersionPayload.php new file mode 100644 index 00000000..3b59f259 --- /dev/null +++ b/src/Handler/Asset/Version/PublishVersion/PublishVersionPayload.php @@ -0,0 +1,35 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/Asset/Version/PublishVersionHandler.php b/src/Handler/Asset/Version/PublishVersionHandler.php index ae353c12..0c09c105 100644 --- a/src/Handler/Asset/Version/PublishVersionHandler.php +++ b/src/Handler/Asset/Version/PublishVersionHandler.php @@ -19,6 +19,7 @@ use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetVersionNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\AssetResult; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\PublishVersion\PublishVersionPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Version; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -30,8 +31,9 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $versionId): AssetResult + public function __invoke(PublishVersionPayload $payload): AssetResult { + $versionId = $payload->versionId; $userId = $this->userContext->getAdminUser()?->getId() ?? 0; $version = Version::getById($versionId) ?? throw new AssetVersionNotFoundException($versionId); diff --git a/src/Handler/Asset/Version/ShowVersion/ShowVersionPayload.php b/src/Handler/Asset/Version/ShowVersion/ShowVersionPayload.php new file mode 100644 index 00000000..f0ee2028 --- /dev/null +++ b/src/Handler/Asset/Version/ShowVersion/ShowVersionPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/Asset/Version/ShowVersionHandler.php b/src/Handler/Asset/Version/ShowVersionHandler.php index c3de9f2a..cf9002a3 100644 --- a/src/Handler/Asset/Version/ShowVersionHandler.php +++ b/src/Handler/Asset/Version/ShowVersionHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Version; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetVersionNotFoundException; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\ShowVersion\ShowVersionPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Version; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -26,8 +27,9 @@ final class ShowVersionHandler { private const string PDF_MIMETYPE = 'application/pdf'; - public function __invoke(int $versionId): ShowVersionResult + public function __invoke(ShowVersionPayload $payload): ShowVersionResult { + $versionId = $payload->versionId; $version = Version::getById($versionId) ?? throw new AssetVersionNotFoundException($versionId); diff --git a/src/Handler/DataObject/AddObjectFolderHandler.php b/src/Handler/DataObject/AddObjectFolderHandler.php index 92d3d0c0..881b375e 100644 --- a/src/Handler/DataObject/AddObjectFolderHandler.php +++ b/src/Handler/DataObject/AddObjectFolderHandler.php @@ -21,6 +21,7 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\AddObjectFolderPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class AddObjectFolderHandler @@ -29,9 +30,11 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $parentId, string $key): void + public function __invoke(AddObjectFolderPayload $payload): void { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; + $parentId = $payload->parentId; + $key = $payload->key; $parent = DataObject::getById($parentId); if ($parent === null) { throw new NotFoundHttpException("Parent object not found: $parentId"); diff --git a/src/Handler/DataObject/AddObjectHandler.php b/src/Handler/DataObject/AddObjectHandler.php index 8c1bbe52..d354bc90 100644 --- a/src/Handler/DataObject/AddObjectHandler.php +++ b/src/Handler/DataObject/AddObjectHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\AddObjectPayload; use OpenDxp\Model; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -31,15 +32,15 @@ public function __construct( private readonly Model\FactoryInterface $modelFactory, ) {} - public function __invoke( - string $className, - string $classId, - int $parentId, - string $key, - string $objectType, - bool $variantViaTree, - ): AddObjectResult { + public function __invoke(AddObjectPayload $payload): AddObjectResult + { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; + $className = $payload->className; + $classId = $payload->classId; + $parentId = $payload->parentId; + $key = $payload->key; + $objectType = $payload->objectType; + $variantViaTree = $payload->variantViaTree; $parent = DataObject::getById($parentId); if ($parent === null) { throw new NotFoundHttpException("Parent object not found: $parentId"); diff --git a/src/Handler/DataObject/ChangeChildrenSortByHandler.php b/src/Handler/DataObject/ChangeChildrenSortByHandler.php index f788dc58..b9e7dc1d 100644 --- a/src/Handler/DataObject/ChangeChildrenSortByHandler.php +++ b/src/Handler/DataObject/ChangeChildrenSortByHandler.php @@ -24,6 +24,7 @@ use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\ChangeChildrenSortByPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class ChangeChildrenSortByHandler @@ -32,12 +33,12 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $id, string $sortBy, string $sortOrder): void + public function __invoke(ChangeChildrenSortByPayload $payload): void { $adminUser = $this->userContext->getAdminUser(); - if (!in_array($sortOrder, ['ASC', 'DESC'])) { - $sortOrder = 'ASC'; - } + $id = $payload->id; + $sortBy = $payload->sortBy; + $sortOrder = in_array($payload->sortOrder, ['ASC', 'DESC']) ? $payload->sortOrder : 'ASC'; $object = DataObject::getById($id); diff --git a/src/Handler/DataObject/ClassDef/AddClassHandler.php b/src/Handler/DataObject/ClassDef/AddClassHandler.php index d5a674ad..b09c65af 100644 --- a/src/Handler/DataObject/ClassDef/AddClassHandler.php +++ b/src/Handler/DataObject/ClassDef/AddClassHandler.php @@ -27,13 +27,13 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(string $className, ?string $classId): AddClassResult + public function __invoke(AddClassPayload $payload): AddClassResult { - $className = preg_replace('/[^a-zA-Z0-9_]+/', '', $className); + $className = preg_replace('/[^a-zA-Z0-9_]+/', '', $payload->className); $className = preg_replace('/^\d+/', '', $className); $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $existingClass = DataObject\ClassDefinition::getById($classId); + $existingClass = DataObject\ClassDefinition::getById($payload->classId); if ($existingClass) { throw new Exception('Class identifier already exists'); } @@ -43,7 +43,7 @@ public function __invoke(string $className, ?string $classId): AddClassResult 'userOwner' => $userId, ]); - $class->setId($classId); + $class->setId($payload->classId); $class->save(true); return new AddClassResult(id: $class->getId()); diff --git a/src/Handler/DataObject/ClassDef/AddClassPayload.php b/src/Handler/DataObject/ClassDef/AddClassPayload.php new file mode 100644 index 00000000..f25713b0 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/AddClassPayload.php @@ -0,0 +1,24 @@ +request->getString('className'), + classId: $request->request->getString('classIdentifier') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/BulkCommitHandler.php b/src/Handler/DataObject/ClassDef/BulkCommitHandler.php index 1c4e53e4..96c95355 100644 --- a/src/Handler/DataObject/ClassDef/BulkCommitHandler.php +++ b/src/Handler/DataObject/ClassDef/BulkCommitHandler.php @@ -32,8 +32,9 @@ public function __construct( private readonly RequestStack $requestStack, ) {} - public function __invoke(array $data): void + public function __invoke(BulkCommitPayload $payload): void { + $data = $payload->data; $permissionMap = ['class' => 'classes', 'objectbrick' => 'objectbricks', 'fieldcollection' => 'fieldcollections', 'customlayout' => 'classes']; $type = $data['type']; diff --git a/src/Handler/DataObject/ClassDef/BulkCommitPayload.php b/src/Handler/DataObject/ClassDef/BulkCommitPayload.php new file mode 100644 index 00000000..b15462fc --- /dev/null +++ b/src/Handler/DataObject/ClassDef/BulkCommitPayload.php @@ -0,0 +1,22 @@ +request->getString('data'), true) ?? [], + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php b/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php index dc4d83b6..5a900ca8 100644 --- a/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php +++ b/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php @@ -25,12 +25,12 @@ final class BulkExportPrepareHandler { public function __construct(private readonly RequestStack $requestStack) {} - public function __invoke(string $data): void + public function __invoke(BulkExportPreparePayload $payload): void { Session::useBag( $this->requestStack->getCurrentRequest()->getSession(), - static function (AttributeBagInterface $session) use ($data): void { - $session->set('class_bulk_export_settings', $data); + static function (AttributeBagInterface $session) use ($payload): void { + $session->set('class_bulk_export_settings', $payload->data); }, 'opendxp_objects' ); diff --git a/src/Handler/DataObject/ClassDef/BulkExportPreparePayload.php b/src/Handler/DataObject/ClassDef/BulkExportPreparePayload.php new file mode 100644 index 00000000..bf5ff87d --- /dev/null +++ b/src/Handler/DataObject/ClassDef/BulkExportPreparePayload.php @@ -0,0 +1,22 @@ +request->getString('data'), + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/BulkImportHandler.php b/src/Handler/DataObject/ClassDef/BulkImportHandler.php index df308177..9d8da407 100644 --- a/src/Handler/DataObject/ClassDef/BulkImportHandler.php +++ b/src/Handler/DataObject/ClassDef/BulkImportHandler.php @@ -25,10 +25,10 @@ final class BulkImportHandler { public function __construct(private readonly RequestStack $requestStack) {} - public function __invoke(string $json): BulkImportResult + public function __invoke(BulkImportPayload $payload): BulkImportResult { $tmpName = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/bulk-import-' . uniqid('', false) . '.tmp'; - file_put_contents($tmpName, $json); + file_put_contents($tmpName, $payload->json); Session::useBag( $this->requestStack->getCurrentRequest()->getSession(), @@ -38,7 +38,7 @@ static function (AttributeBagInterface $session) use ($tmpName): void { 'opendxp_objects' ); - $parsed = json_decode($json, true); + $parsed = json_decode($payload->json, true); $result = []; foreach ($parsed as $groupName => $group) { diff --git a/src/Handler/DataObject/ClassDef/BulkImportPayload.php b/src/Handler/DataObject/ClassDef/BulkImportPayload.php new file mode 100644 index 00000000..ec627e14 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/BulkImportPayload.php @@ -0,0 +1,26 @@ +files->get('Filedata'); + + return new static( + json: $file !== null ? (file_get_contents($file->getPathname()) ?: '') : '', + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/DeleteClassHandler.php b/src/Handler/DataObject/ClassDef/DeleteClassHandler.php index acdf8edb..b7546d1a 100644 --- a/src/Handler/DataObject/ClassDef/DeleteClassHandler.php +++ b/src/Handler/DataObject/ClassDef/DeleteClassHandler.php @@ -21,9 +21,9 @@ final class DeleteClassHandler { - public function __invoke(?string $id): void + public function __invoke(DeleteClassPayload $payload): void { - $class = DataObject\ClassDefinition::getById($id); + $class = DataObject\ClassDefinition::getById($payload->id); if ($class) { $class->delete(); } diff --git a/src/Handler/DataObject/ClassDef/DeleteClassPayload.php b/src/Handler/DataObject/ClassDef/DeleteClassPayload.php new file mode 100644 index 00000000..b97cf04b --- /dev/null +++ b/src/Handler/DataObject/ClassDef/DeleteClassPayload.php @@ -0,0 +1,22 @@ +request->getString('id') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php b/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php index a3d1c976..ff79093f 100644 --- a/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php +++ b/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php @@ -22,9 +22,9 @@ final class DeleteSelectOptionsHandler { - public function __invoke(?string $id): void + public function __invoke(DeleteSelectOptionsPayload $payload): void { - $selectOptions = DataObject\SelectOptions\Config::getById($id); + $selectOptions = DataObject\SelectOptions\Config::getById($payload->id); if (!$selectOptions instanceof DataObject\SelectOptions\Config) { throw new NotFoundHttpException('Not Found', code: 1677133720896); } diff --git a/src/Handler/DataObject/ClassDef/DeleteSelectOptionsPayload.php b/src/Handler/DataObject/ClassDef/DeleteSelectOptionsPayload.php new file mode 100644 index 00000000..0947acaa --- /dev/null +++ b/src/Handler/DataObject/ClassDef/DeleteSelectOptionsPayload.php @@ -0,0 +1,23 @@ +request->getString(Config::PROPERTY_ID) ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/ExportClassHandler.php b/src/Handler/DataObject/ClassDef/ExportClassHandler.php index 72fb03e3..163d1779 100644 --- a/src/Handler/DataObject/ClassDef/ExportClassHandler.php +++ b/src/Handler/DataObject/ClassDef/ExportClassHandler.php @@ -23,11 +23,11 @@ final class ExportClassHandler { - public function __invoke(?string $id): ExportClassResult + public function __invoke(ExportClassPayload $payload): ExportClassResult { - $class = DataObject\ClassDefinition::getById($id); + $class = DataObject\ClassDefinition::getById($payload->id); if (!$class instanceof DataObject\ClassDefinition) { - $errorMessage = ': Class with id [ ' . $id . ' not found. ]'; + $errorMessage = ': Class with id [ ' . $payload->id . ' not found. ]'; Logger::error($errorMessage); throw new NotFoundHttpException($errorMessage); diff --git a/src/Handler/DataObject/ClassDef/ExportClassPayload.php b/src/Handler/DataObject/ClassDef/ExportClassPayload.php new file mode 100644 index 00000000..4fb87fd0 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/ExportClassPayload.php @@ -0,0 +1,22 @@ +query->getString('id') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigHandler.php b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigHandler.php index 5820335a..772c8ffc 100644 --- a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigHandler.php @@ -22,14 +22,14 @@ final class GetClassDefinitionForColumnConfigHandler { - public function __invoke(string $id, int $objectId): GetClassDefinitionForColumnConfigResult + public function __invoke(GetClassDefinitionForColumnConfigPayload $payload): GetClassDefinitionForColumnConfigResult { - $class = DataObject\ClassDefinition::getById($id); + $class = DataObject\ClassDefinition::getById($payload->id); if (!$class) { throw new NotFoundHttpException('Class not found'); } - $filteredDefinitions = DataObject\Service::getCustomLayoutDefinitionForGridColumnConfig($class, $objectId); + $filteredDefinitions = DataObject\Service::getCustomLayoutDefinitionForGridColumnConfig($class, $payload->objectId); /** @var DataObject\ClassDefinition\Layout $layoutDefinitions */ $layoutDefinitions = $filteredDefinitions['layoutDefinition'] ?? false; diff --git a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigPayload.php b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigPayload.php new file mode 100644 index 00000000..5b31352d --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigPayload.php @@ -0,0 +1,24 @@ +query->getString('id') ?: null, + objectId: $request->query->getInt('oid'), + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassHandler.php b/src/Handler/DataObject/ClassDef/GetClassHandler.php index ec17aeb1..d4490307 100644 --- a/src/Handler/DataObject/ClassDef/GetClassHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClassHandler.php @@ -22,9 +22,9 @@ final class GetClassHandler { - public function __invoke(?string $id): GetClassResult + public function __invoke(GetClassPayload $payload): GetClassResult { - $class = DataObject\ClassDefinition::getById($id); + $class = DataObject\ClassDefinition::getById($payload->id); if (!$class) { throw new NotFoundHttpException('Class not found'); } diff --git a/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php b/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php index 03146b97..7fa8c31e 100644 --- a/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php @@ -26,8 +26,11 @@ final class GetClassIconsHandler { public function __construct(private readonly EventDispatcherInterface $eventDispatcher) {} - public function __invoke(?string $type, ?string $classId): GetClassIconsResult + public function __invoke(GetClassIconsPayload $payload): GetClassIconsResult { + $type = $payload->type; + $classId = $payload->classId; + if ($type === '') { return new GetClassIconsResult(icons: []); } diff --git a/src/Handler/DataObject/ClassDef/GetClassIconsPayload.php b/src/Handler/DataObject/ClassDef/GetClassIconsPayload.php new file mode 100644 index 00000000..38ba6d97 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassIconsPayload.php @@ -0,0 +1,24 @@ +query->getString('type') ?: null, + classId: $request->query->getString('classId') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassPayload.php b/src/Handler/DataObject/ClassDef/GetClassPayload.php new file mode 100644 index 00000000..eee0a724 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassPayload.php @@ -0,0 +1,22 @@ +query->getString('id') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetClassTreeHandler.php b/src/Handler/DataObject/ClassDef/GetClassTreeHandler.php index 800d3709..34a6551f 100644 --- a/src/Handler/DataObject/ClassDef/GetClassTreeHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClassTreeHandler.php @@ -19,7 +19,6 @@ use OpenDxp\Model\DataObject; use OpenDxp\Model\Translation; -use OpenDxp\Model\User; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetClassTreeHandler @@ -30,12 +29,12 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke( - bool $createAllowed, - bool $withId, - bool $useTitle, - bool $grouped, - ): GetClassTreeResult { + public function __invoke(GetClassTreePayload $payload): GetClassTreeResult + { + $createAllowed = $payload->createAllowed; + $withId = $payload->withId; + $useTitle = $payload->useTitle; + $grouped = $payload->grouped; $adminUser = $this->userContext->getAdminUser(); $classesList = new DataObject\ClassDefinition\Listing(); $classesList->setOrderKey('name'); diff --git a/src/Handler/DataObject/ClassDef/GetClassTreePayload.php b/src/Handler/DataObject/ClassDef/GetClassTreePayload.php new file mode 100644 index 00000000..64e48b01 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetClassTreePayload.php @@ -0,0 +1,28 @@ +query->get('createAllowed'), + withId: (bool) $request->query->get('withId'), + useTitle: (bool) $request->query->get('useTitle'), + grouped: (bool) $request->query->get('grouped'), + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsHandler.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsHandler.php index 0d76bbf9..f9ebe19d 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsHandler.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsHandler.php @@ -22,9 +22,9 @@ final class GetSelectOptionsHandler { - public function __invoke(?string $id): GetSelectOptionsResult + public function __invoke(GetSelectOptionsPayload $payload): GetSelectOptionsResult { - $selectOptions = DataObject\SelectOptions\Config::getById($id); + $selectOptions = DataObject\SelectOptions\Config::getById($payload->id); if (!$selectOptions instanceof DataObject\SelectOptions\Config) { throw new NotFoundHttpException('Not Found', code: 1677133720896); } diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsPayload.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsPayload.php new file mode 100644 index 00000000..b252416c --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsPayload.php @@ -0,0 +1,23 @@ +query->getString(Config::PROPERTY_ID) ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeHandler.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeHandler.php index fa8f4420..e5668844 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeHandler.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeHandler.php @@ -26,8 +26,9 @@ final class GetSelectOptionsTreeHandler { public function __construct(private readonly EventDispatcherInterface $eventDispatcher) {} - public function __invoke(int $grouped): GetSelectOptionsTreeResult + public function __invoke(GetSelectOptionsTreePayload $payload): GetSelectOptionsTreeResult { + $grouped = $payload->grouped; $configurations = $groups = []; $selectOptionConfigs = new DataObject\SelectOptions\Config\Listing(); diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreePayload.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsTreePayload.php new file mode 100644 index 00000000..0d329f82 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsTreePayload.php @@ -0,0 +1,22 @@ +query->getInt('grouped'), + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesHandler.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesHandler.php index 14956eb3..bf6f179e 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesHandler.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesHandler.php @@ -22,9 +22,9 @@ final class GetSelectOptionsUsagesHandler { - public function __invoke(?string $id): GetSelectOptionsUsagesResult + public function __invoke(GetSelectOptionsUsagesPayload $payload): GetSelectOptionsUsagesResult { - $selectOptions = DataObject\SelectOptions\Config::getById($id); + $selectOptions = DataObject\SelectOptions\Config::getById($payload->id); if (!$selectOptions instanceof DataObject\SelectOptions\Config) { throw new NotFoundHttpException('Not Found', code: 1677133720896); } diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesPayload.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesPayload.php new file mode 100644 index 00000000..a2b101fb --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesPayload.php @@ -0,0 +1,23 @@ +query->getString(Config::PROPERTY_ID) ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewHandler.php b/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewHandler.php index fead0f78..274bdcdd 100644 --- a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewHandler.php +++ b/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewHandler.php @@ -21,30 +21,25 @@ final class GetTextLayoutPreviewHandler { - public function __invoke( - string $objPath, - ?string $className, - ?string $renderingData, - ?string $renderingClass, - ?string $html, - ): GetTextLayoutPreviewResult { - $fqClassName = '\\OpenDxp\\Model\\DataObject\\' . $className; - $obj = DataObject::getByPath($objPath) ?? new $fqClassName(); + public function __invoke(GetTextLayoutPreviewPayload $payload): GetTextLayoutPreviewResult + { + $fqClassName = '\\OpenDxp\\Model\\DataObject\\' . $payload->className; + $obj = DataObject::getByPath($payload->objPath) ?? new $fqClassName(); $textLayout = new DataObject\ClassDefinition\Layout\Text(); - $textLayout->setName('textLayoutPreview' . $className); + $textLayout->setName('textLayoutPreview' . $payload->className); $context = [ - 'data' => $renderingData, + 'data' => $payload->renderingData, ]; - if ($renderingClass) { - $textLayout->setRenderingClass($renderingClass); - $textLayout->setRenderingData($renderingData); + if ($payload->renderingClass) { + $textLayout->setRenderingClass($payload->renderingClass); + $textLayout->setRenderingData($payload->renderingData); } - if ($html) { - $textLayout->setHtml($html); + if ($payload->html) { + $textLayout->setHtml($payload->html); } $renderedHtml = $textLayout->enrichLayoutDefinition($obj, $context)->getHtml(); diff --git a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewPayload.php b/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewPayload.php new file mode 100644 index 00000000..c13cb8de --- /dev/null +++ b/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewPayload.php @@ -0,0 +1,30 @@ +query->getString('previewObject'), + className: $request->query->getString('className') ?: null, + renderingData: $request->query->getString('renderingData') ?: null, + renderingClass: $request->query->getString('renderingClass') ?: null, + html: $request->query->getString('html') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/ImportClassHandler.php b/src/Handler/DataObject/ClassDef/ImportClassHandler.php index 99710a3e..7e40deb8 100644 --- a/src/Handler/DataObject/ClassDef/ImportClassHandler.php +++ b/src/Handler/DataObject/ClassDef/ImportClassHandler.php @@ -23,14 +23,14 @@ final class ImportClassHandler { - public function __invoke(?string $id, string $json): void + public function __invoke(ImportClassPayload $payload): void { - $class = DataObject\ClassDefinition::getById($id); + $class = DataObject\ClassDefinition::getById($payload->id); if (!$class) { throw new NotFoundHttpException('Class not found'); } - $success = DataObject\ClassDefinition\Service::importClassDefinitionFromJson($class, $json, false, true); + $success = DataObject\ClassDefinition\Service::importClassDefinitionFromJson($class, $payload->json, false, true); if (!$success) { throw new RuntimeException('Failed to import class definition'); } diff --git a/src/Handler/DataObject/ClassDef/ImportClassPayload.php b/src/Handler/DataObject/ClassDef/ImportClassPayload.php new file mode 100644 index 00000000..5f68a532 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/ImportClassPayload.php @@ -0,0 +1,28 @@ +files->get('Filedata'); + + return new static( + id: $request->query->getString('id') ?: null, + json: $file !== null ? (file_get_contents($file->getPathname()) ?: '') : '', + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/SaveClassDefinitionHandler.php b/src/Handler/DataObject/ClassDef/SaveClassDefinitionHandler.php index 358eda7e..fdaf497b 100644 --- a/src/Handler/DataObject/ClassDef/SaveClassDefinitionHandler.php +++ b/src/Handler/DataObject/ClassDef/SaveClassDefinitionHandler.php @@ -28,17 +28,17 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke( - string $id, - array $configuration, - array $values, - ): SaveClassDefinitionResult { + public function __invoke(SaveClassDefinitionPayload $payload): SaveClassDefinitionResult + { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $class = DataObject\ClassDefinition::getById($id); + $class = DataObject\ClassDefinition::getById($payload->id); if (!$class) { throw new NotFoundHttpException('Class not found'); } + $values = $payload->values; + $configuration = $payload->configuration; + if ($class->getModificationDate() != $values['modificationDate']) { throw new BadRequestHttpException('The class was modified during editing, please reload the class and make your changes again'); } diff --git a/src/Handler/DataObject/ClassDef/SaveClassDefinitionPayload.php b/src/Handler/DataObject/ClassDef/SaveClassDefinitionPayload.php new file mode 100644 index 00000000..c81bdeb7 --- /dev/null +++ b/src/Handler/DataObject/ClassDef/SaveClassDefinitionPayload.php @@ -0,0 +1,26 @@ +request->getString('id'), + configuration: json_decode($request->request->getString('configuration'), true) ?? [], + values: json_decode($request->request->getString('values'), true) ?? [], + ); + } +} diff --git a/src/Handler/DataObject/ClassDef/SaveSelectOptionsHandler.php b/src/Handler/DataObject/ClassDef/SaveSelectOptionsHandler.php index 0887bd27..06454db9 100644 --- a/src/Handler/DataObject/ClassDef/SaveSelectOptionsHandler.php +++ b/src/Handler/DataObject/ClassDef/SaveSelectOptionsHandler.php @@ -27,24 +27,18 @@ final class SaveSelectOptionsHandler { public function __construct(private readonly EventDispatcherInterface $eventDispatcher) {} - public function __invoke( - string $id, - string $task, - string $group, - string $useTraits, - string $implementsInterfaces, - array $selectOptionsData, - ): SaveSelectOptionsResult { - if ($task === 'add' && (new DataObject\SelectOptions\Config\Listing())->hasConfig($id)) { + public function __invoke(SaveSelectOptionsPayload $payload): SaveSelectOptionsResult + { + if ($payload->task === 'add' && (new DataObject\SelectOptions\Config\Listing())->hasConfig($payload->id)) { throw new BadRequestHttpException('Select options with the same ID already exists (lower/upper cases may be different)'); } $selectOptionsConfiguration = DataObject\SelectOptions\Config::createFromData([ - DataObject\SelectOptions\Config::PROPERTY_ID => $id, - DataObject\SelectOptions\Config::PROPERTY_GROUP => $group, - DataObject\SelectOptions\Config::PROPERTY_USE_TRAITS => $useTraits, - DataObject\SelectOptions\Config::PROPERTY_IMPLEMENTS_INTERFACES => $implementsInterfaces, - DataObject\SelectOptions\Config::PROPERTY_SELECT_OPTIONS => $selectOptionsData, + DataObject\SelectOptions\Config::PROPERTY_ID => $payload->id, + DataObject\SelectOptions\Config::PROPERTY_GROUP => $payload->group, + DataObject\SelectOptions\Config::PROPERTY_USE_TRAITS => $payload->useTraits, + DataObject\SelectOptions\Config::PROPERTY_IMPLEMENTS_INTERFACES => $payload->implementsInterfaces, + DataObject\SelectOptions\Config::PROPERTY_SELECT_OPTIONS => $payload->selectOptionsData, ]); $event = new GenericEvent(null, ['selectOptionsConfiguration' => $selectOptionsConfiguration]); diff --git a/src/Handler/DataObject/ClassDef/SaveSelectOptionsPayload.php b/src/Handler/DataObject/ClassDef/SaveSelectOptionsPayload.php new file mode 100644 index 00000000..57a4d22a --- /dev/null +++ b/src/Handler/DataObject/ClassDef/SaveSelectOptionsPayload.php @@ -0,0 +1,35 @@ +request->getString(Config::PROPERTY_SELECT_OPTIONS); + + return new static( + id: $request->request->getString(Config::PROPERTY_ID), + task: $request->request->getString('task'), + group: $request->request->getString(Config::PROPERTY_GROUP) ?: null, + useTraits: $request->request->getString(Config::PROPERTY_USE_TRAITS), + implementsInterfaces: $request->request->getString(Config::PROPERTY_IMPLEMENTS_INTERFACES), + selectOptionsData: $rawSelectOptions !== '' ? json_decode($rawSelectOptions, true) : null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/AddCollectionsHandler.php b/src/Handler/DataObject/Classificationstore/AddCollectionsHandler.php index f84f3b5f..ba65aa39 100644 --- a/src/Handler/DataObject/Classificationstore/AddCollectionsHandler.php +++ b/src/Handler/DataObject/Classificationstore/AddCollectionsHandler.php @@ -25,8 +25,11 @@ final class AddCollectionsHandler { - public function __invoke(array $ids, int $oid, string $fieldname): AddCollectionsResult + public function __invoke(AddCollectionsPayload $payload): AddCollectionsResult { + $ids = $payload->ids; + $oid = $payload->oid; + $fieldname = $payload->fieldname; $data = []; if (!$ids) { diff --git a/src/Handler/DataObject/Classificationstore/AddCollectionsPayload.php b/src/Handler/DataObject/Classificationstore/AddCollectionsPayload.php new file mode 100644 index 00000000..2eda63b9 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/AddCollectionsPayload.php @@ -0,0 +1,26 @@ +request->getString('collectionIds'), true) ?: [], + oid: $request->request->getInt('oid'), + fieldname: $request->request->getString('fieldname'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/AddGroupsHandler.php b/src/Handler/DataObject/Classificationstore/AddGroupsHandler.php index 392f3e45..9051b973 100644 --- a/src/Handler/DataObject/Classificationstore/AddGroupsHandler.php +++ b/src/Handler/DataObject/Classificationstore/AddGroupsHandler.php @@ -23,8 +23,11 @@ final class AddGroupsHandler { - public function __invoke(array $ids, int $oid, ?string $fieldname): AddGroupsResult + public function __invoke(AddGroupsPayload $payload): AddGroupsResult { + $ids = $payload->ids; + $oid = $payload->oid; + $fieldname = $payload->fieldname; $object = $oid ? DataObject\Concrete::getById($oid) : null; $placeholders = implode(',', array_fill(0, count($ids), '?')); diff --git a/src/Handler/DataObject/Classificationstore/AddGroupsPayload.php b/src/Handler/DataObject/Classificationstore/AddGroupsPayload.php new file mode 100644 index 00000000..6d5ea320 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/AddGroupsPayload.php @@ -0,0 +1,26 @@ +request->getString('groupIds'), true) ?: [], + oid: $request->request->getInt('oid'), + fieldname: $request->request->getString('fieldname') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/AddPropertyHandler.php b/src/Handler/DataObject/Classificationstore/AddPropertyHandler.php index 11623f46..76e9912e 100644 --- a/src/Handler/DataObject/Classificationstore/AddPropertyHandler.php +++ b/src/Handler/DataObject/Classificationstore/AddPropertyHandler.php @@ -21,20 +21,20 @@ final class AddPropertyHandler { - public function __invoke(string $name, int $storeId): AddPropertyResult + public function __invoke(AddPropertyPayload $payload): AddPropertyResult { $definition = [ 'fieldtype' => 'input', - 'name' => $name, - 'title' => $name, + 'name' => $payload->name, + 'title' => $payload->name, 'datatype' => 'data', ]; $config = new Classificationstore\KeyConfig(); - $config->setName($name); - $config->setTitle($name); + $config->setName($payload->name); + $config->setTitle($payload->name); $config->setType('input'); - $config->setStoreId($storeId); + $config->setStoreId($payload->storeId); $config->setEnabled(true); $config->setDefinition(json_encode($definition)); $config->save(); diff --git a/src/Handler/DataObject/Classificationstore/AddPropertyPayload.php b/src/Handler/DataObject/Classificationstore/AddPropertyPayload.php new file mode 100644 index 00000000..af3ad439 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/AddPropertyPayload.php @@ -0,0 +1,24 @@ +request->getString('name'), + storeId: $request->request->getInt('storeId'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/CreateCollectionHandler.php b/src/Handler/DataObject/Classificationstore/CreateCollectionHandler.php index 1cd31ffb..7126a2b0 100644 --- a/src/Handler/DataObject/Classificationstore/CreateCollectionHandler.php +++ b/src/Handler/DataObject/Classificationstore/CreateCollectionHandler.php @@ -21,14 +21,14 @@ final class CreateCollectionHandler { - public function __invoke(string $name, int $storeId): CreateCollectionResult + public function __invoke(CreateCollectionPayload $payload): CreateCollectionResult { - $config = Classificationstore\CollectionConfig::getByName($name, $storeId); + $config = Classificationstore\CollectionConfig::getByName($payload->name, $payload->storeId); if (!$config) { $config = new Classificationstore\CollectionConfig(); - $config->setName($name); - $config->setStoreId($storeId); + $config->setName($payload->name); + $config->setStoreId($payload->storeId); $config->save(); } diff --git a/src/Handler/DataObject/Classificationstore/CreateCollectionPayload.php b/src/Handler/DataObject/Classificationstore/CreateCollectionPayload.php new file mode 100644 index 00000000..6f61e856 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/CreateCollectionPayload.php @@ -0,0 +1,25 @@ +request->getString('name')), + storeId: $request->request->getInt('storeId'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/CreateGroupHandler.php b/src/Handler/DataObject/Classificationstore/CreateGroupHandler.php index aa0f862d..12ebfa9b 100644 --- a/src/Handler/DataObject/Classificationstore/CreateGroupHandler.php +++ b/src/Handler/DataObject/Classificationstore/CreateGroupHandler.php @@ -21,14 +21,14 @@ final class CreateGroupHandler { - public function __invoke(string $name, int $storeId): CreateGroupResult + public function __invoke(CreateGroupPayload $payload): CreateGroupResult { - $config = Classificationstore\GroupConfig::getByName($name, $storeId); + $config = Classificationstore\GroupConfig::getByName($payload->name, $payload->storeId); if (!$config) { $config = new Classificationstore\GroupConfig(); - $config->setStoreId($storeId); - $config->setName($name); + $config->setStoreId($payload->storeId); + $config->setName($payload->name); $config->save(); return new CreateGroupResult(name: $config->getName(), alreadyExists: false); diff --git a/src/Handler/DataObject/Classificationstore/CreateGroupPayload.php b/src/Handler/DataObject/Classificationstore/CreateGroupPayload.php new file mode 100644 index 00000000..7bd60467 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/CreateGroupPayload.php @@ -0,0 +1,25 @@ +request->getString('name')), + storeId: $request->request->getInt('storeId'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/CreateStoreHandler.php b/src/Handler/DataObject/Classificationstore/CreateStoreHandler.php index 96d6e312..745fd9e9 100644 --- a/src/Handler/DataObject/Classificationstore/CreateStoreHandler.php +++ b/src/Handler/DataObject/Classificationstore/CreateStoreHandler.php @@ -25,13 +25,13 @@ final class CreateStoreHandler /** * @throws Exception */ - public function __invoke(string $name): CreateStoreResult + public function __invoke(CreateStorePayload $payload): CreateStoreResult { - $config = Classificationstore\StoreConfig::getByName($name); + $config = Classificationstore\StoreConfig::getByName($payload->name); if (!$config) { $config = new Classificationstore\StoreConfig(); - $config->setName($name); + $config->setName($payload->name); $config->save(); } else { throw new Exception('Store with the given name exists'); diff --git a/src/Handler/DataObject/Classificationstore/CreateStorePayload.php b/src/Handler/DataObject/Classificationstore/CreateStorePayload.php new file mode 100644 index 00000000..d1aff2ae --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/CreateStorePayload.php @@ -0,0 +1,23 @@ +request->getString('name')), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionHandler.php b/src/Handler/DataObject/Classificationstore/DeleteCollectionHandler.php index f4ea971e..7c433393 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteCollectionHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteCollectionHandler.php @@ -21,16 +21,16 @@ final class DeleteCollectionHandler { - public function __invoke(int $id): void + public function __invoke(DeleteCollectionPayload $payload): void { $configRelations = new Classificationstore\CollectionGroupRelation\Listing(); - $configRelations->setCondition('colId = ?', $id); + $configRelations->setCondition('colId = ?', $payload->id); $list = $configRelations->load(); foreach ($list as $item) { $item->delete(); } - $config = Classificationstore\CollectionConfig::getById($id); + $config = Classificationstore\CollectionConfig::getById($payload->id); $config->delete(); } } diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionPayload.php b/src/Handler/DataObject/Classificationstore/DeleteCollectionPayload.php new file mode 100644 index 00000000..7cd77b49 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeleteCollectionPayload.php @@ -0,0 +1,22 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php index c9a7cc3e..99c18017 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php @@ -21,11 +21,11 @@ final class DeleteCollectionRelationHandler { - public function __invoke(int $colId, int $groupId): void + public function __invoke(DeleteCollectionRelationPayload $payload): void { $config = new Classificationstore\CollectionGroupRelation(); - $config->setColId($colId); - $config->setGroupId($groupId); + $config->setColId($payload->colId); + $config->setGroupId($payload->groupId); $config->delete(); } } diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationPayload.php b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationPayload.php new file mode 100644 index 00000000..8f40892d --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationPayload.php @@ -0,0 +1,24 @@ +request->getInt('colId'), + groupId: $request->request->getInt('groupId'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php b/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php index 9db7464c..0575ab7e 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php @@ -21,9 +21,9 @@ final class DeleteGroupHandler { - public function __invoke(int $id): void + public function __invoke(DeleteGroupPayload $payload): void { - $config = Classificationstore\GroupConfig::getById($id); + $config = Classificationstore\GroupConfig::getById($payload->id); $config->delete(); } } diff --git a/src/Handler/DataObject/Classificationstore/DeleteGroupPayload.php b/src/Handler/DataObject/Classificationstore/DeleteGroupPayload.php new file mode 100644 index 00000000..18bb2ce0 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeleteGroupPayload.php @@ -0,0 +1,22 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php b/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php index 17383b15..83d8cd72 100644 --- a/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php @@ -21,9 +21,9 @@ final class DeletePropertyHandler { - public function __invoke(int $id): void + public function __invoke(DeletePropertyPayload $payload): void { - $config = Classificationstore\KeyConfig::getById($id); + $config = Classificationstore\KeyConfig::getById($payload->id); $config->setEnabled(false); $config->save(); } diff --git a/src/Handler/DataObject/Classificationstore/DeletePropertyPayload.php b/src/Handler/DataObject/Classificationstore/DeletePropertyPayload.php new file mode 100644 index 00000000..1d0a8fb7 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeletePropertyPayload.php @@ -0,0 +1,22 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php b/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php index 3af537f1..93a5c97c 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php @@ -21,11 +21,11 @@ final class DeleteRelationHandler { - public function __invoke(int $keyId, int $groupId): void + public function __invoke(DeleteRelationPayload $payload): void { $config = new Classificationstore\KeyGroupRelation(); - $config->setKeyId($keyId); - $config->setGroupId($groupId); + $config->setKeyId($payload->keyId); + $config->setGroupId($payload->groupId); $config->delete(); } } diff --git a/src/Handler/DataObject/Classificationstore/DeleteRelationPayload.php b/src/Handler/DataObject/Classificationstore/DeleteRelationPayload.php new file mode 100644 index 00000000..ca8ef231 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/DeleteRelationPayload.php @@ -0,0 +1,24 @@ +request->getInt('keyId'), + groupId: $request->request->getInt('groupId'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/EditStoreHandler.php b/src/Handler/DataObject/Classificationstore/EditStoreHandler.php index 4df70d02..d5a4dc72 100644 --- a/src/Handler/DataObject/Classificationstore/EditStoreHandler.php +++ b/src/Handler/DataObject/Classificationstore/EditStoreHandler.php @@ -25,8 +25,10 @@ final class EditStoreHandler /** * @throws Exception */ - public function __invoke(int $id, string $name, ?string $description): void + public function __invoke(EditStorePayload $payload): void { + $id = $payload->id; + $name = $payload->name; if (!$name) { throw new Exception('Name must not be empty'); } @@ -43,7 +45,7 @@ public function __invoke(int $id, string $name, ?string $description): void } $config->setName($name); - $config->setDescription($description); + $config->setDescription($payload->description); $config->save(); } } diff --git a/src/Handler/DataObject/Classificationstore/EditStorePayload.php b/src/Handler/DataObject/Classificationstore/EditStorePayload.php new file mode 100644 index 00000000..9be904f3 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/EditStorePayload.php @@ -0,0 +1,28 @@ +request->getString('data'), true) ?? []; + + return new static( + id: $request->request->getInt('id'), + name: $data['name'] ?? '', + description: $data['description'] ?? null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php b/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php index bf73ce7d..beaf7008 100644 --- a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php @@ -24,48 +24,41 @@ final class GetCollectionRelationsHandler { - public function __invoke( - array $queryAll, - int $limit, - int $start, - ?string $dir, - bool $overrideSort, - ?string $filter, - int $colId, - ): GetCollectionRelationsResult { + public function __invoke(GetCollectionRelationsPayload $payload): GetCollectionRelationsResult + { $mapping = ['groupName' => 'name', 'groupDescription' => 'description']; $orderKey = 'sorter'; $order = 'ASC'; - if ($dir !== null) { - $order = $dir; + if ($payload->dir !== null) { + $order = $payload->dir; } - $sortingSettings = QueryParams::extractSortingSettings($queryAll); + $sortingSettings = QueryParams::extractSortingSettings($payload->queryAll); if ($sortingSettings['orderKey'] && $sortingSettings['order']) { $orderKey = $sortingSettings['orderKey']; $order = $sortingSettings['order']; } - if ($overrideSort) { + if ($payload->overrideSort) { $orderKey = 'id'; $order = 'DESC'; } $list = new Classificationstore\CollectionGroupRelation\Listing(); - if ($limit > 0) { - $list->setLimit($limit); + if ($payload->limit > 0) { + $list->setLimit($payload->limit); } - $list->setOffset($start); + $list->setOffset($payload->start); $list->setOrder($order); $list->setOrderKey($mapping[$orderKey] ?? $orderKey); $condition = ''; - if ($filter !== null) { + if ($payload->filter !== null) { $db = Db::get(); - $filters = json_decode($filter); + $filters = json_decode($payload->filter); $count = 0; /** @var stdClass $f */ @@ -86,7 +79,7 @@ public function __invoke( if ($condition) { $condition = '( ' . $condition . ' ) AND'; } - $condition .= ' colId = ' . $list->quote($colId); + $condition .= ' colId = ' . $list->quote($payload->colId); $list->setCondition($condition); diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsPayload.php b/src/Handler/DataObject/Classificationstore/GetCollectionRelationsPayload.php new file mode 100644 index 00000000..8124adb7 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetCollectionRelationsPayload.php @@ -0,0 +1,36 @@ +query->getInt('limit'); + + return new static( + queryAll: $request->query->all(), + limit: $rawLimit ?: 15, + start: $request->query->getInt('start'), + dir: $request->query->getString('dir') ?: null, + overrideSort: (bool) $request->query->get('overrideSort'), + filter: $request->query->getString('filter') ?: null, + colId: $request->query->getInt('colId'), + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionsHandler.php b/src/Handler/DataObject/Classificationstore/GetCollectionsHandler.php index 60bc8651..bb817289 100644 --- a/src/Handler/DataObject/Classificationstore/GetCollectionsHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetCollectionsHandler.php @@ -29,43 +29,33 @@ final class GetCollectionsHandler { public function __construct(private readonly AdminSearchTermResolver $searchTermResolver) {} - public function __invoke( - array $queryAll, - int $limit, - int $start, - ?string $dir, - bool $overrideSort, - ?int $oid, - ?string $fieldname, - ?string $searchfilter, - ?int $storeId, - ?string $filter, - ): GetCollectionsResult { + public function __invoke(GetCollectionsPayload $payload): GetCollectionsResult + { $orderKey = 'name'; $order = 'ASC'; - if ($dir !== null) { - $order = $dir; + if ($payload->dir !== null) { + $order = $payload->dir; } - $sortingSettings = QueryParams::extractSortingSettings($queryAll); + $sortingSettings = QueryParams::extractSortingSettings($payload->queryAll); if ($sortingSettings['orderKey'] && $sortingSettings['order']) { $orderKey = $sortingSettings['orderKey']; $order = $sortingSettings['order']; } - if ($overrideSort) { + if ($payload->overrideSort) { $orderKey = 'id'; $order = 'DESC'; } $storeIdFromDefinition = 0; $allowedCollectionIds = []; - if ($oid) { - $object = DataObject\Concrete::getById($oid); + if ($payload->oid) { + $object = DataObject\Concrete::getById($payload->oid); $class = $object->getClass(); /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ - $fd = $class->getFieldDefinition($fieldname); + $fd = $class->getFieldDefinition($payload->fieldname); $allowedGroupIds = $fd->getAllowedGroupIds(); if ($allowedGroupIds) { @@ -86,18 +76,18 @@ public function __invoke( $list = new Classificationstore\CollectionConfig\Listing(); - $list->setLimit($limit); - $list->setOffset($start); + $list->setLimit($payload->limit); + $list->setOffset($payload->start); $list->setOrder($order); $list->setOrderKey($orderKey); $conditionParts = []; $db = Db::get(); - if ($searchfilter) { + if ($payload->searchfilter) { $searchFilterConditions = []; - $searchTerms = [$searchfilter, ...$this->searchTermResolver->resolve($searchfilter)]; + $searchTerms = [$payload->searchfilter, ...$this->searchTermResolver->resolve($payload->searchfilter)]; foreach ($searchTerms as $searchFilterTerm) { $searchFilterConditions[] = 'name LIKE ' . $db->quote('%' . $searchFilterTerm . '%') . ' OR description LIKE ' . $db->quote('%' . $searchFilterTerm . '%'); } @@ -105,12 +95,12 @@ public function __invoke( $conditionParts[] = '(' . implode(' OR ', $searchFilterConditions) . ')'; } - $storeId = $storeId ?: $storeIdFromDefinition; + $storeId = $payload->storeId ?: $storeIdFromDefinition; $conditionParts[] = ' (storeId = ' . $db->quote($storeId) . ')'; - if ($filter !== null) { - $filters = json_decode($filter); + if ($payload->filter !== null) { + $filters = json_decode($payload->filter); /** @var stdClass $f */ foreach ($filters as $f) { if (!isset($f->value)) { diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionsPayload.php b/src/Handler/DataObject/Classificationstore/GetCollectionsPayload.php new file mode 100644 index 00000000..263856a7 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetCollectionsPayload.php @@ -0,0 +1,42 @@ +query->get('limit'); + + return new static( + queryAll: $request->query->all(), + limit: $rawLimit !== null ? (int) $rawLimit : 15, + start: $request->query->getInt('start'), + dir: $request->query->getString('dir') ?: null, + overrideSort: (bool) $request->query->get('overrideSort'), + oid: ($v = $request->query->get('oid')) !== null && is_numeric($v) ? (int) $v : null, + fieldname: $request->query->getString('fieldname') ?: null, + searchfilter: $request->query->getString('searchfilter') ?: null, + storeId: ($v = $request->query->get('storeId')) !== null && is_numeric($v) ? (int) $v : null, + filter: $request->query->getString('filter') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetGroupsHandler.php b/src/Handler/DataObject/Classificationstore/GetGroupsHandler.php index a92b5871..c99b7be5 100644 --- a/src/Handler/DataObject/Classificationstore/GetGroupsHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetGroupsHandler.php @@ -28,55 +28,44 @@ final class GetGroupsHandler { public function __construct(private readonly AdminSearchTermResolver $searchTermResolver) {} - public function __invoke( - array $queryAll, - int $limit, - int $start, - ?string $dir, - ?string $sort, - bool $overrideSort, - ?string $searchfilter, - int $storeId, - ?string $filter, - ?int $oid, - ?string $fieldname, - ): GetGroupsResult { + public function __invoke(GetGroupsPayload $payload): GetGroupsResult + { $orderKey = 'name'; $order = 'ASC'; - if ($dir !== null) { - $order = $dir; + if ($payload->dir !== null) { + $order = $payload->dir; } - if ($sort !== null) { - $orderKey = $sort; + if ($payload->sort !== null) { + $orderKey = $payload->sort; } - $sortingSettings = QueryParams::extractSortingSettings($queryAll); + $sortingSettings = QueryParams::extractSortingSettings($payload->queryAll); if ($sortingSettings['orderKey'] && $sortingSettings['order']) { $orderKey = $sortingSettings['orderKey']; $order = $sortingSettings['order']; } - if ($overrideSort) { + if ($payload->overrideSort) { $orderKey = 'id'; $order = 'DESC'; } $list = new Classificationstore\GroupConfig\Listing(); - $list->setLimit($limit); - $list->setOffset($start); + $list->setLimit($payload->limit); + $list->setOffset($payload->start); $list->setOrder($order); $list->setOrderKey($orderKey); $conditionParts = []; $db = Db::get(); - if ($searchfilter !== null) { + if ($payload->searchfilter !== null) { $searchFilterConditions = []; - $searchTerms = [$searchfilter, ...$this->searchTermResolver->resolve($searchfilter)]; + $searchTerms = [$payload->searchfilter, ...$this->searchTermResolver->resolve($payload->searchfilter)]; foreach ($searchTerms as $searchFilterTerm) { $searchFilterConditions[] = 'name LIKE ' . $db->quote('%' . $searchFilterTerm . '%') . ' OR description LIKE ' . $db->quote('%' . $searchFilterTerm . '%'); } @@ -84,12 +73,12 @@ public function __invoke( $conditionParts[] = '(' . implode(' OR ', $searchFilterConditions) . ')'; } - if ($storeId) { - $conditionParts[] = '(storeId = ' . $db->quote($storeId) . ')'; + if ($payload->storeId) { + $conditionParts[] = '(storeId = ' . $db->quote($payload->storeId) . ')'; } - if ($filter !== null) { - $filters = json_decode($filter); + if ($payload->filter !== null) { + $filters = json_decode($payload->filter); /** @var stdClass $f */ foreach ($filters as $f) { if (!isset($f->value)) { @@ -100,11 +89,11 @@ public function __invoke( } } - if ($oid !== null) { - $object = DataObject\Concrete::getById($oid); + if ($payload->oid !== null) { + $object = DataObject\Concrete::getById($payload->oid); $class = $object->getClass(); /** @var DataObject\ClassDefinition\Data\Classificationstore $fd */ - $fd = $class->getFieldDefinition($fieldname); + $fd = $class->getFieldDefinition($payload->fieldname); $allowedGroupIds = $fd->getAllowedGroupIds(); if ($allowedGroupIds) { diff --git a/src/Handler/DataObject/Classificationstore/GetGroupsPayload.php b/src/Handler/DataObject/Classificationstore/GetGroupsPayload.php new file mode 100644 index 00000000..6acab087 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetGroupsPayload.php @@ -0,0 +1,44 @@ +query->getInt('limit'); + + return new static( + queryAll: $request->query->all(), + limit: $rawLimit ?: 15, + start: $request->query->getInt('start'), + dir: $request->query->getString('dir') ?: null, + sort: $request->query->getString('sort') ?: null, + overrideSort: (bool) $request->query->get('overrideSort'), + searchfilter: $request->query->getString('searchfilter') ?: null, + storeId: $request->query->getInt('storeId'), + filter: $request->query->getString('filter') ?: null, + oid: ($v = $request->query->get('oid')) !== null && is_numeric($v) ? (int) $v : null, + fieldname: $request->query->getString('fieldname') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetPageHandler.php b/src/Handler/DataObject/Classificationstore/GetPageHandler.php index aa294419..be91c317 100644 --- a/src/Handler/DataObject/Classificationstore/GetPageHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetPageHandler.php @@ -22,15 +22,9 @@ final class GetPageHandler { - public function __invoke( - ?string $table, - int $id, - int $storeId, - int $pageSize, - ?string $sortKey, - ?string $sortDir, - ): GetPageResult { - $tableSuffix = $table; + public function __invoke(GetPagePayload $payload): GetPageResult + { + $tableSuffix = $payload->table; if (!ArrayHelper::inArrayCaseInsensitive($tableSuffix, ['keys', 'groups'])) { $tableSuffix = 'keys'; } @@ -38,8 +32,10 @@ public function __invoke( $table = 'classificationstore_' . $tableSuffix; $db = Db::get(); - if ($sortKey) { - } else { + $sortKey = $payload->sortKey; + $sortDir = $payload->sortDir; + + if (!$sortKey) { $sortKey = 'name'; $sortDir = 'ASC'; } @@ -56,20 +52,20 @@ public function __invoke( if ($table === 'keys') { $query = ' - select *, (item.pos - 1)/ ' . $pageSize . ' + 1 as page from ( + select *, (item.pos - 1)/ ' . $payload->pageSize . ' + 1 as page from ( select * from ( select @rownum := @rownum + 1 as pos, id, name, `type` from `' . $table . '` - where enabled = 1 and storeId = ' . $storeId . $sorter . ' - ) all_rows) item where id = ' . $id . ';'; + where enabled = 1 and storeId = ' . $payload->storeId . $sorter . ' + ) all_rows) item where id = ' . $payload->id . ';'; } else { $query = ' - select *, (item.pos - 1)/ ' . $pageSize . ' + 1 as page from ( + select *, (item.pos - 1)/ ' . $payload->pageSize . ' + 1 as page from ( select * from ( select @rownum := @rownum + 1 as pos, id, name from `' . $table . '` - where storeId = ' . $storeId . $sorter . ' - ) all_rows) item where id = ' . $id . ';'; + where storeId = ' . $payload->storeId . $sorter . ' + ) all_rows) item where id = ' . $payload->id . ';'; } $db->executeStatement('SET @rownum = 0'); diff --git a/src/Handler/DataObject/Classificationstore/GetPagePayload.php b/src/Handler/DataObject/Classificationstore/GetPagePayload.php new file mode 100644 index 00000000..0a8896d8 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetPagePayload.php @@ -0,0 +1,32 @@ +query->getString('table') ?: null, + id: $request->query->getInt('id'), + storeId: $request->query->getInt('storeId'), + pageSize: $request->query->getInt('pageSize'), + sortKey: $request->query->getString('sortKey') ?: null, + sortDir: $request->query->getString('sortDir') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetPropertiesHandler.php b/src/Handler/DataObject/Classificationstore/GetPropertiesHandler.php index 6f6dcf37..9730e147 100644 --- a/src/Handler/DataObject/Classificationstore/GetPropertiesHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetPropertiesHandler.php @@ -24,26 +24,15 @@ final class GetPropertiesHandler { - public function __invoke( - array $queryAll, - int $storeId, - ?string $frameName, - int $limit, - int $start, - ?string $dir, - bool $overrideSort, - ?string $groupIds, - ?string $keyIds, - ?string $searchfilter, - ?string $filter, - ): GetPropertiesResult { + public function __invoke(GetPropertiesPayload $payload): GetPropertiesResult + { $db = Db::get(); $conditionParts = []; - if ($frameName) { + if ($payload->frameName) { $keyCriteria = ' FALSE '; - $frameConfig = Classificationstore\CollectionConfig::getByName($frameName, $storeId); + $frameConfig = Classificationstore\CollectionConfig::getByName($payload->frameName, $payload->storeId); if ($frameConfig) { // get all keys within that collection / frame $frameId = $frameConfig->getId(); @@ -76,40 +65,40 @@ public function __invoke( $orderKey = 'name'; $order = 'ASC'; - if ($dir !== null) { - $order = $dir; + if ($payload->dir !== null) { + $order = $payload->dir; } - $sortingSettings = QueryParams::extractSortingSettings($queryAll); + $sortingSettings = QueryParams::extractSortingSettings($payload->queryAll); if ($sortingSettings['orderKey'] && $sortingSettings['order']) { $orderKey = $sortingSettings['orderKey']; $order = $sortingSettings['order']; } - if ($overrideSort) { + if ($payload->overrideSort) { $orderKey = 'id'; $order = 'DESC'; } $list = new Classificationstore\KeyConfig\Listing(); - if ($limit > 0 && !$groupIds && !$keyIds) { - $list->setLimit($limit); + if ($payload->limit > 0 && !$payload->groupIds && !$payload->keyIds) { + $list->setLimit($payload->limit); } - $list->setOffset($start); + $list->setOffset($payload->start); $list->setOrder($order); $list->setOrderKey($orderKey); - if ($searchfilter) { - $conditionParts[] = '(name LIKE ' . $db->quote('%' . $searchfilter . '%') . ' OR description LIKE ' . $db->quote('%' . $searchfilter . '%') . ')'; + if ($payload->searchfilter) { + $conditionParts[] = '(name LIKE ' . $db->quote('%' . $payload->searchfilter . '%') . ' OR description LIKE ' . $db->quote('%' . $payload->searchfilter . '%') . ')'; } - if ($storeId) { - $conditionParts[] = '(storeId = ' . $db->quote($storeId) . ')'; + if ($payload->storeId) { + $conditionParts[] = '(storeId = ' . $db->quote($payload->storeId) . ')'; } - if ($filter !== null) { - $filters = json_decode($filter); + if ($payload->filter !== null) { + $filters = json_decode($payload->filter); /** @var stdClass $f */ foreach ($filters as $f) { if (!isset($f->value)) { @@ -122,12 +111,12 @@ public function __invoke( $condition = implode(' AND ', $conditionParts); $list->setCondition($condition); - if ($groupIds || $keyIds) { - if ($groupIds) { - $ids = json_decode($groupIds, true); + if ($payload->groupIds || $payload->keyIds) { + if ($payload->groupIds) { + $ids = json_decode($payload->groupIds, true); $col = 'group'; } else { - $ids = json_decode($keyIds, true); + $ids = json_decode($payload->keyIds, true); $col = 'id'; } diff --git a/src/Handler/DataObject/Classificationstore/GetPropertiesPayload.php b/src/Handler/DataObject/Classificationstore/GetPropertiesPayload.php new file mode 100644 index 00000000..37f4529e --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetPropertiesPayload.php @@ -0,0 +1,44 @@ +query->getInt('limit'); + + return new static( + queryAll: $request->query->all(), + storeId: $request->query->getInt('storeId'), + frameName: $request->query->getString('frameName') ?: null, + limit: $rawLimit ?: 15, + start: $request->query->getInt('start'), + dir: $request->query->getString('dir') ?: null, + overrideSort: (bool) $request->query->get('overrideSort'), + groupIds: $request->query->getString('groupIds') ?: null, + keyIds: $request->query->getString('keyIds') ?: null, + searchfilter: $request->query->getString('searchfilter') ?: null, + filter: $request->query->getString('filter') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/GetRelationsHandler.php b/src/Handler/DataObject/Classificationstore/GetRelationsHandler.php index 36b6b7a5..c4d899a0 100644 --- a/src/Handler/DataObject/Classificationstore/GetRelationsHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetRelationsHandler.php @@ -25,39 +25,32 @@ final class GetRelationsHandler { - public function __invoke( - array $queryAll, - ?string $relationIds, - int $limit, - int $start, - ?string $dir, - bool $overrideSort, - ?string $filter, - ?string $groupId, - ): GetRelationsResult { + public function __invoke(GetRelationsPayload $payload): GetRelationsResult + { $mapping = ['keyName' => 'name', 'keyDescription' => 'description']; $orderKey = 'name'; $order = 'ASC'; - $relationIdList = $relationIds ? json_decode($relationIds, true) : null; + $relationIdList = $payload->relationIds ? json_decode($payload->relationIds, true) : null; - if ($dir !== null) { - $order = $dir; + if ($payload->dir !== null) { + $order = $payload->dir; } - $sortingSettings = QueryParams::extractSortingSettings($queryAll); + $sortingSettings = QueryParams::extractSortingSettings($payload->queryAll); if ($sortingSettings['orderKey'] && $sortingSettings['order']) { $orderKey = $mapping[$sortingSettings['orderKey']] ?? $sortingSettings['orderKey']; $order = $sortingSettings['order']; } - if ($overrideSort) { + if ($payload->overrideSort) { $orderKey = 'id'; $order = 'DESC'; } + $limit = $payload->limit; if ($limit === 0 && is_array($relationIdList)) { $limit = count($relationIdList); } @@ -68,14 +61,14 @@ public function __invoke( $list->setLimit($limit); } - $list->setOffset($start); + $list->setOffset($payload->start); $list->setOrder($order); $list->setOrderKey($orderKey); $conditionParts = []; - if ($filter !== null) { + if ($payload->filter !== null) { $db = Db::get(); - $filters = json_decode($filter); + $filters = json_decode($payload->filter); /** @var stdClass $f */ foreach ($filters as $f) { if (!isset($f->value)) { @@ -88,7 +81,7 @@ public function __invoke( } if ($relationIdList === null) { - $conditionParts[] = ' groupId = ' . $list->quote($groupId); + $conditionParts[] = ' groupId = ' . $list->quote($payload->groupId); } if ($relationIdList) { diff --git a/src/Handler/DataObject/Classificationstore/GetRelationsPayload.php b/src/Handler/DataObject/Classificationstore/GetRelationsPayload.php new file mode 100644 index 00000000..d6755451 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/GetRelationsPayload.php @@ -0,0 +1,38 @@ +query->getInt('limit'); + + return new static( + queryAll: $request->query->all(), + relationIds: $request->query->getString('relationIds') ?: null, + limit: $rawLimit ?: 15, + start: $request->query->getInt('start'), + dir: $request->query->getString('dir') ?: null, + overrideSort: (bool) $request->query->get('overrideSort'), + filter: $request->query->getString('filter') ?: null, + groupId: $request->query->getString('groupId') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsHandler.php b/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsHandler.php index 569ee0b6..015f3153 100644 --- a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsHandler.php +++ b/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsHandler.php @@ -21,8 +21,9 @@ final class SaveCollectionRelationsHandler { - public function __invoke(array $data): SaveCollectionRelationsResult + public function __invoke(SaveCollectionRelationsPayload $payload): SaveCollectionRelationsResult { + $data = $payload->data; if (count($data) === count($data, 1)) { $data = [$data]; } diff --git a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsPayload.php b/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsPayload.php new file mode 100644 index 00000000..eed76819 --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsPayload.php @@ -0,0 +1,24 @@ +request->has('data'), + data: json_decode($request->request->getString('data'), true) ?? [], + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/SaveRelationHandler.php b/src/Handler/DataObject/Classificationstore/SaveRelationHandler.php index c34e513c..a7327d33 100644 --- a/src/Handler/DataObject/Classificationstore/SaveRelationHandler.php +++ b/src/Handler/DataObject/Classificationstore/SaveRelationHandler.php @@ -21,8 +21,9 @@ final class SaveRelationHandler { - public function __invoke(array $data): SaveRelationResult + public function __invoke(SaveRelationPayload $payload): SaveRelationResult { + $data = $payload->data; $keyId = $data['keyId']; $groupId = $data['groupId']; $sorter = $data['sorter']; diff --git a/src/Handler/DataObject/Classificationstore/SaveRelationPayload.php b/src/Handler/DataObject/Classificationstore/SaveRelationPayload.php new file mode 100644 index 00000000..c4565e5f --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/SaveRelationPayload.php @@ -0,0 +1,24 @@ +request->has('data'), + data: json_decode($request->request->getString('data'), true) ?? [], + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/SearchRelationsHandler.php b/src/Handler/DataObject/Classificationstore/SearchRelationsHandler.php index 1e854e8f..b648f62a 100644 --- a/src/Handler/DataObject/Classificationstore/SearchRelationsHandler.php +++ b/src/Handler/DataObject/Classificationstore/SearchRelationsHandler.php @@ -27,16 +27,8 @@ final class SearchRelationsHandler { public function __construct(private readonly AdminSearchTermResolver $searchTermResolver) {} - public function __invoke( - array $queryAll, - ?int $storeId, - int $limit, - int $start, - ?string $dir, - bool $overrideSort, - ?string $filter, - ?string $searchfilter, - ): SearchRelationsResult { + public function __invoke(SearchRelationsPayload $payload): SearchRelationsResult + { $db = Db::get(); $mapping = [ @@ -48,11 +40,11 @@ public function __invoke( $orderKey = 'name'; $order = 'ASC'; - if ($dir) { - $order = $dir; + if ($payload->dir) { + $order = $payload->dir; } - $sortingSettings = QueryParams::extractSortingSettings($queryAll); + $sortingSettings = QueryParams::extractSortingSettings($payload->queryAll); if ($sortingSettings['orderKey'] && $sortingSettings['order']) { $orderKey = $sortingSettings['orderKey']; if ($orderKey === 'keyName') { @@ -61,24 +53,24 @@ public function __invoke( $order = $sortingSettings['order']; } - if ($overrideSort) { + if ($payload->overrideSort) { $orderKey = 'id'; $order = 'DESC'; } $list = new Classificationstore\KeyGroupRelation\Listing(); - if ($limit > 0) { - $list->setLimit($limit); + if ($payload->limit > 0) { + $list->setLimit($payload->limit); } - $list->setOffset($start); + $list->setOffset($payload->start); $list->setOrder($order); $list->setOrderKey($orderKey); $conditionParts = []; - if ($filter !== null) { - $filters = json_decode($filter); + if ($payload->filter !== null) { + $filters = json_decode($payload->filter); /** @var stdClass $f */ foreach ($filters as $f) { if (!isset($f->value)) { @@ -90,12 +82,12 @@ public function __invoke( } } - $conditionParts[] = ' groupId IN (select id from classificationstore_groups where storeId = ' . $db->quote($storeId) . ')'; + $conditionParts[] = ' groupId IN (select id from classificationstore_groups where storeId = ' . $db->quote($payload->storeId) . ')'; - if ($searchfilter) { + if ($payload->searchfilter) { $searchFilterConditions = []; - $searchTerms = [$searchfilter, ...$this->searchTermResolver->resolve($searchfilter)]; + $searchTerms = [$payload->searchfilter, ...$this->searchTermResolver->resolve($payload->searchfilter)]; foreach ($searchTerms as $searchFilterTerm) { $searchFilterConditions[] = Classificationstore\KeyConfig\Dao::TABLE_NAME_KEYS . '.name LIKE ' . $db->quote('%' . $searchFilterTerm . '%') . ' OR ' . Classificationstore\GroupConfig\Dao::TABLE_NAME_GROUPS . '.name LIKE ' . $db->quote('%' . $searchFilterTerm . '%') diff --git a/src/Handler/DataObject/Classificationstore/SearchRelationsPayload.php b/src/Handler/DataObject/Classificationstore/SearchRelationsPayload.php new file mode 100644 index 00000000..5194e83c --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/SearchRelationsPayload.php @@ -0,0 +1,38 @@ +query->getInt('limit'); + + return new static( + queryAll: $request->query->all(), + storeId: ($v = $request->query->get('storeId')) !== null && is_numeric($v) ? (int) $v : null, + limit: $rawLimit ?: 15, + start: $request->query->getInt('start'), + dir: $request->query->getString('dir') ?: null, + overrideSort: (bool) $request->query->get('overrideSort'), + filter: $request->query->getString('filter') ?: null, + searchfilter: $request->query->getString('searchfilter') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/UpdateCollectionHandler.php b/src/Handler/DataObject/Classificationstore/UpdateCollectionHandler.php index f417b18e..a1f786b5 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateCollectionHandler.php +++ b/src/Handler/DataObject/Classificationstore/UpdateCollectionHandler.php @@ -21,8 +21,9 @@ final class UpdateCollectionHandler { - public function __invoke(array $data): UpdateCollectionResult + public function __invoke(UpdateCollectionPayload $payload): UpdateCollectionResult { + $data = $payload->data; $id = $data['id']; $config = Classificationstore\CollectionConfig::getById($id); diff --git a/src/Handler/DataObject/Classificationstore/UpdateCollectionPayload.php b/src/Handler/DataObject/Classificationstore/UpdateCollectionPayload.php new file mode 100644 index 00000000..fcb45c0a --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/UpdateCollectionPayload.php @@ -0,0 +1,24 @@ +request->has('data'), + data: json_decode($request->request->getString('data'), true) ?? [], + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/UpdateGroupHandler.php b/src/Handler/DataObject/Classificationstore/UpdateGroupHandler.php index 94209200..ad8e56ca 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateGroupHandler.php +++ b/src/Handler/DataObject/Classificationstore/UpdateGroupHandler.php @@ -21,8 +21,9 @@ final class UpdateGroupHandler { - public function __invoke(array $data): UpdateGroupResult + public function __invoke(UpdateGroupPayload $payload): UpdateGroupResult { + $data = $payload->data; $id = $data['id']; $config = Classificationstore\GroupConfig::getById($id); diff --git a/src/Handler/DataObject/Classificationstore/UpdateGroupPayload.php b/src/Handler/DataObject/Classificationstore/UpdateGroupPayload.php new file mode 100644 index 00000000..74e8008c --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/UpdateGroupPayload.php @@ -0,0 +1,24 @@ +request->has('data'), + data: json_decode($request->request->getString('data'), true) ?? [], + ); + } +} diff --git a/src/Handler/DataObject/Classificationstore/UpdatePropertyHandler.php b/src/Handler/DataObject/Classificationstore/UpdatePropertyHandler.php index 7750c6e7..454ab6ba 100644 --- a/src/Handler/DataObject/Classificationstore/UpdatePropertyHandler.php +++ b/src/Handler/DataObject/Classificationstore/UpdatePropertyHandler.php @@ -21,8 +21,9 @@ final class UpdatePropertyHandler { - public function __invoke(array $data): UpdatePropertyResult + public function __invoke(UpdatePropertyPayload $payload): UpdatePropertyResult { + $data = $payload->data; $id = $data['id']; $config = Classificationstore\KeyConfig::getById($id); diff --git a/src/Handler/DataObject/Classificationstore/UpdatePropertyPayload.php b/src/Handler/DataObject/Classificationstore/UpdatePropertyPayload.php new file mode 100644 index 00000000..097aa30d --- /dev/null +++ b/src/Handler/DataObject/Classificationstore/UpdatePropertyPayload.php @@ -0,0 +1,24 @@ +request->has('data'), + data: json_decode($request->request->getString('data'), true) ?? [], + ); + } +} diff --git a/src/Handler/DataObject/Copy/CopyDataObjectHandler.php b/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectHandler.php similarity index 70% rename from src/Handler/DataObject/Copy/CopyDataObjectHandler.php rename to src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectHandler.php index 3de320cb..9e879d0b 100644 --- a/src/Handler/DataObject/Copy/CopyDataObjectHandler.php +++ b/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectHandler.php @@ -15,40 +15,34 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyDataObject; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; -use OpenDxp\Logger; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; -use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class CopyDataObjectHandler { public function __construct( - private readonly AdminUserContextInterface $userContext,private readonly ElementServiceFactory $serviceFactory) {} - - public function __invoke( - int $sourceId, - int $targetId, - string $type, - ?int $sourceParentId, - ?int $targetParentId, - ?int $sessionParentId, - ): CopyDataObjectResult { + private readonly AdminUserContextInterface $userContext, + private readonly ElementServiceFactory $serviceFactory, + ) {} + + public function __invoke(CopyDataObjectPayload $payload): CopyDataObjectResult + { $adminUser = $this->userContext->getAdminUser(); - $source = DataObject::getById($sourceId); + $source = DataObject::getById($payload->sourceId); - if ($sourceParentId !== null && $targetParentId !== null) { - $sourceParent = DataObject::getById($sourceParentId) ?? throw new NotFoundHttpException('Source parent not found'); - $resolvedTargetParentId = $sessionParentId ?? $targetParentId; + if ($payload->sourceParentId !== null && $payload->targetParentId !== null) { + $sourceParent = DataObject::getById($payload->sourceParentId) ?? throw new NotFoundHttpException('Source parent not found'); + $resolvedTargetParentId = $payload->sessionParentId ?? $payload->targetParentId; $targetParent = DataObject::getById($resolvedTargetParentId) ?? throw new NotFoundHttpException('Target parent not found'); $targetPath = preg_replace('@^' . preg_quote($sourceParent->getRealFullPath(), '@') . '@', $targetParent . '/', $source->getRealPath()); $target = DataObject::getByPath($targetPath); } else { - $target = DataObject::getById($targetId); + $target = DataObject::getById($payload->targetId); } if (!$target instanceof DataObject) { @@ -60,9 +54,9 @@ public function __invoke( throw new AccessDeniedHttpException(); } - $source = DataObject::getById($sourceId); + $source = DataObject::getById($payload->sourceId); if (!$source instanceof DataObject) { - throw new NotFoundHttpException("Source object not found: $sourceId"); + throw new NotFoundHttpException("Source object not found: {$payload->sourceId}"); } if ($source instanceof DataObject\Concrete && $latestVersion = $source->getLatestVersion()) { @@ -72,18 +66,18 @@ public function __invoke( $objectService = $this->serviceFactory->createDataObjectService(); - if ($type === 'child') { + if ($payload->type === 'child') { $newObject = $objectService->copyAsChild($target, $source); - return new CopyDataObjectResult($sourceId, $newObject); + return new CopyDataObjectResult($payload->sourceId, $newObject); } - if ($type === 'replace') { + if ($payload->type === 'replace') { $concreteTarget = DataObject\Concrete::getById($target->getId()); $concreteSource = DataObject\Concrete::getById($source->getId()); $objectService->copyContents($concreteTarget, $concreteSource); } - return new CopyDataObjectResult($sourceId); + return new CopyDataObjectResult($payload->sourceId); } } diff --git a/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectPayload.php b/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectPayload.php new file mode 100644 index 00000000..576444dd --- /dev/null +++ b/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectPayload.php @@ -0,0 +1,55 @@ +request->getString('transactionId'); + $sessionBag = Session::getSessionBag($request->getSession(), 'opendxp_copy')->get($transactionId) ?? []; + $hasTargetParentId = (bool) $request->request->getString('targetParentId'); + + return new static( + sourceId: $request->request->getInt('sourceId'), + targetId: $request->request->getInt('targetId'), + type: $request->request->getString('type'), + sourceParentId: $hasTargetParentId ? $request->request->getInt('sourceParentId') : null, + targetParentId: $hasTargetParentId ? $request->request->getInt('targetParentId') : null, + sessionParentId: !empty($sessionBag['parentId']) ? (int) $sessionBag['parentId'] : null, + transactionId: $transactionId, + saveParentId: (bool) $request->request->getString('saveParentId'), + sessionBag: $sessionBag, + ); + } +} diff --git a/src/Handler/DataObject/Copy/CopyDataObjectResult.php b/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectResult.php similarity index 90% rename from src/Handler/DataObject/Copy/CopyDataObjectResult.php rename to src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectResult.php index 5e5dda88..56d912a2 100644 --- a/src/Handler/DataObject/Copy/CopyDataObjectResult.php +++ b/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyDataObject; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/Copy/GetDataObjectChildIdsHandler.php b/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsHandler.php similarity index 70% rename from src/Handler/DataObject/Copy/GetDataObjectChildIdsHandler.php rename to src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsHandler.php index 405416ca..6e8c6588 100644 --- a/src/Handler/DataObject/Copy/GetDataObjectChildIdsHandler.php +++ b/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsHandler.php @@ -15,19 +15,19 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIds; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; use OpenDxp\Model\DataObject; final class GetDataObjectChildIdsHandler { - public function __invoke(int $sourceId): ChildIdsResult + public function __invoke(GetDataObjectChildIdsPayload $payload): GetDataObjectChildIdsResult { - $object = DataObject::getById($sourceId) ?? throw new DataObjectNotFoundException($sourceId); + $object = DataObject::getById($payload->sourceId) ?? throw new DataObjectNotFoundException($payload->sourceId); if (!$object->hasChildren(DataObject::$types)) { - return new ChildIdsResult([]); + return new GetDataObjectChildIdsResult([]); } $list = new DataObject\Listing(); @@ -36,6 +36,6 @@ public function __invoke(int $sourceId): ChildIdsResult $list->setOrder('ASC'); $list->setObjectTypes(DataObject::$types); - return new ChildIdsResult($list->loadIdList()); + return new GetDataObjectChildIdsResult($list->loadIdList()); } } diff --git a/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsPayload.php b/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsPayload.php new file mode 100644 index 00000000..b97bc2d5 --- /dev/null +++ b/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsPayload.php @@ -0,0 +1,30 @@ +query->getInt('sourceId')); + } +} diff --git a/src/Handler/DataObject/Copy/ChildIdsResult.php b/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsResult.php similarity index 82% rename from src/Handler/DataObject/Copy/ChildIdsResult.php rename to src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsResult.php index e6c16df6..2503d303 100644 --- a/src/Handler/DataObject/Copy/ChildIdsResult.php +++ b/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsResult.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIds; -final readonly class ChildIdsResult +final readonly class GetDataObjectChildIdsResult { /** @param int[] $ids */ public function __construct( diff --git a/src/Handler/DataObject/Copy/RewriteDataObjectIdsHandler.php b/src/Handler/DataObject/Copy/RewriteDataObjectIds/RewriteDataObjectIdsHandler.php similarity index 77% rename from src/Handler/DataObject/Copy/RewriteDataObjectIdsHandler.php rename to src/Handler/DataObject/Copy/RewriteDataObjectIds/RewriteDataObjectIdsHandler.php index 77058752..96867737 100644 --- a/src/Handler/DataObject/Copy/RewriteDataObjectIdsHandler.php +++ b/src/Handler/DataObject/Copy/RewriteDataObjectIds/RewriteDataObjectIdsHandler.php @@ -15,11 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\RewriteDataObjectIds; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; -use OpenDxp\Model\DataObject; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; +use OpenDxp\Model\DataObject; final class RewriteDataObjectIdsHandler { @@ -27,12 +27,12 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $objectId, array $idMapping): void + public function __invoke(RewriteDataObjectIdsPayload $payload): void { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $object = DataObject::getById($objectId) ?? throw new DataObjectNotFoundException($objectId); + $object = DataObject::getById($payload->objectId) ?? throw new DataObjectNotFoundException($payload->objectId); - $object = DataObject\Service::rewriteIds($object, ['object' => $idMapping]); + $object = DataObject\Service::rewriteIds($object, ['object' => $payload->idMapping]); $object->setUserModification($userId); $object->save(); } diff --git a/src/Handler/DataObject/Copy/RewriteDataObjectIds/RewriteDataObjectIdsPayload.php b/src/Handler/DataObject/Copy/RewriteDataObjectIds/RewriteDataObjectIdsPayload.php new file mode 100644 index 00000000..999ec067 --- /dev/null +++ b/src/Handler/DataObject/Copy/RewriteDataObjectIds/RewriteDataObjectIdsPayload.php @@ -0,0 +1,50 @@ +request->getString('transactionId'); + $idStore = Session::getSessionBag($request->getSession(), 'opendxp_copy')->get($transactionId) ?? []; + + if (!array_key_exists('rewrite-stack', $idStore)) { + $idStore['rewrite-stack'] = array_values($idStore['idMapping'] ?? []); + } + + $objectId = (int) array_shift($idStore['rewrite-stack']); + + return new static( + objectId: $objectId, + idMapping: $idStore['idMapping'] ?? [], + transactionId: $transactionId, + updatedIdStore: $idStore, + ); + } +} diff --git a/src/Handler/DataObject/CustomLayout/AddCustomLayout/AddCustomLayoutPayload.php b/src/Handler/DataObject/CustomLayout/AddCustomLayout/AddCustomLayoutPayload.php new file mode 100644 index 00000000..260549b9 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/AddCustomLayout/AddCustomLayoutPayload.php @@ -0,0 +1,38 @@ +request->getString('layoutIdentifier'), + layoutName: $request->request->getString('layoutName'), + classId: $request->request->getString('classId'), + ); + } +} diff --git a/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php index ef9f4130..ec15ff7f 100644 --- a/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php @@ -17,10 +17,11 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\AddCustomLayout\AddCustomLayoutPayload; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; use OpenDxp\Model\Exception\ConfigWriteException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class AddCustomLayoutHandler { @@ -28,8 +29,12 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(string $layoutId, string $layoutName, string $classId): DataObject\ClassDefinition\CustomLayout + public function __invoke(AddCustomLayoutPayload $payload): DataObject\ClassDefinition\CustomLayout { + $layoutId = $payload->layoutIdentifier; + $layoutName = $payload->layoutName; + $classId = $payload->classId; + $userId = $this->userContext->getAdminUser()?->getId() ?? 0; if (DataObject\ClassDefinition\CustomLayout::getById($layoutId)) { throw new BadRequestHttpException('Custom Layout identifier already exists'); diff --git a/src/Handler/DataObject/CustomLayout/DeleteCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/DeleteCustomLayoutHandler.php index 8790201f..9815710d 100644 --- a/src/Handler/DataObject/CustomLayout/DeleteCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/DeleteCustomLayoutHandler.php @@ -17,12 +17,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Model\DataObject; final class DeleteCustomLayoutHandler { - public function __invoke(string $id): void + public function __invoke(StringIdBodyPayload $payload): void { + $id = $payload->id; $customLayouts = new DataObject\ClassDefinition\CustomLayout\Listing(); $customLayouts->setFilter(function (DataObject\ClassDefinition\CustomLayout $layout) use ($id) { $currentLayoutId = $layout->getId(); diff --git a/src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutPayload.php b/src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutPayload.php new file mode 100644 index 00000000..b1f94de9 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutPayload.php @@ -0,0 +1,30 @@ +query->getString('id') ?: null); + } +} diff --git a/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php index 35ecbf4e..6351b0ee 100644 --- a/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php @@ -17,16 +17,17 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayout\ExportCustomLayoutPayload; use OpenDxp\Logger; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class ExportCustomLayoutHandler { - public function __invoke(?string $id): ExportCustomLayoutResult + public function __invoke(ExportCustomLayoutPayload $payload): ExportCustomLayoutResult { - if ($id) { - $customLayout = DataObject\ClassDefinition\CustomLayout::getById($id); + if ($payload->id) { + $customLayout = DataObject\ClassDefinition\CustomLayout::getById($payload->id); if ($customLayout) { return new ExportCustomLayoutResult( $customLayout->getName(), @@ -35,7 +36,7 @@ public function __invoke(?string $id): ExportCustomLayoutResult } } - $errorMessage = ': Custom Layout with id [ ' . $id . ' not found. ]'; + $errorMessage = ': Custom Layout with id [ ' . $payload->id . ' not found. ]'; Logger::error($errorMessage); throw new NotFoundHttpException($errorMessage); diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutPayload.php b/src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutPayload.php new file mode 100644 index 00000000..a1f00b31 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutPayload.php @@ -0,0 +1,30 @@ +query->getString('id')); + } +} diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/GetCustomLayoutDefinitionsPayload.php b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/GetCustomLayoutDefinitionsPayload.php new file mode 100644 index 00000000..0d9f81da --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/GetCustomLayoutDefinitionsPayload.php @@ -0,0 +1,30 @@ +query->getString('classId')); + } +} diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php index 7dae3745..66ab56ca 100644 --- a/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php @@ -17,17 +17,17 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitions\GetCustomLayoutDefinitionsPayload; use OpenDxp\Model\DataObject; final class GetCustomLayoutDefinitionsHandler { - public function __invoke(string $classId): CustomLayoutDefinitionsResult + public function __invoke(GetCustomLayoutDefinitionsPayload $payload): CustomLayoutDefinitionsResult { - $classIds = explode(',', $classId); + $classIds = explode(',', $payload->classId); $list = new DataObject\ClassDefinition\CustomLayout\Listing(); $list->setFilter( - fn (DataObject\ClassDefinition\CustomLayout $layout) => - in_array($layout->getClassId(), $classIds) && !str_contains($layout->getId(), '.brick.') + fn (DataObject\ClassDefinition\CustomLayout $layout) => in_array($layout->getClassId(), $classIds) && !str_contains($layout->getId(), '.brick.') ); $definitions = []; diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php index 87c8fbfc..80469bea 100644 --- a/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php @@ -17,10 +17,11 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayout\GetCustomLayoutPayload; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; use OpenDxp\Model\Exception\ConfigWriteException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetCustomLayoutHandler { @@ -28,22 +29,22 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(string $id): GetCustomLayoutResult + public function __invoke(GetCustomLayoutPayload $payload): GetCustomLayoutResult { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $customLayout = DataObject\ClassDefinition\CustomLayout::getById($id); + $customLayout = DataObject\ClassDefinition\CustomLayout::getById($payload->id); if (!$customLayout) { - $brickLayoutSeparator = strpos($id, '.brick.'); + $brickLayoutSeparator = strpos($payload->id, '.brick.'); if ($brickLayoutSeparator !== false) { - $parentLayout = DataObject\ClassDefinition\CustomLayout::getById(substr($id, 0, $brickLayoutSeparator)); + $parentLayout = DataObject\ClassDefinition\CustomLayout::getById(substr($payload->id, 0, $brickLayoutSeparator)); if ($parentLayout instanceof DataObject\ClassDefinition\CustomLayout) { $customLayout = DataObject\ClassDefinition\CustomLayout::create([ - 'name' => $parentLayout->getName() . ' ' . substr($id, $brickLayoutSeparator + strlen('.brick.')), + 'name' => $parentLayout->getName() . ' ' . substr($payload->id, $brickLayoutSeparator + strlen('.brick.')), 'userOwner' => $userId, 'classId' => $parentLayout->getClassId(), ]); - $customLayout->setId($id); + $customLayout->setId($payload->id); if (!$customLayout->isWriteable()) { throw new ConfigWriteException(); } diff --git a/src/Handler/DataObject/CustomLayout/ImportCustomLayout/ImportCustomLayoutPayload.php b/src/Handler/DataObject/CustomLayout/ImportCustomLayout/ImportCustomLayoutPayload.php new file mode 100644 index 00000000..bbefe81f --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/ImportCustomLayout/ImportCustomLayoutPayload.php @@ -0,0 +1,44 @@ +files->get('Filedata'); + $importData = json_decode(file_get_contents($file->getPathname()), true) ?? []; + + return new static( + id: $request->query->getString('id') ?: null, + importData: $importData, + nameAlreadyInUse: isset($importData['name']) && DataObject\ClassDefinition\CustomLayout::getByName($importData['name']) instanceof DataObject\ClassDefinition\CustomLayout, + ); + } +} diff --git a/src/Handler/DataObject/CustomLayout/ImportCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/ImportCustomLayoutHandler.php index 58f04a79..bc8b6506 100644 --- a/src/Handler/DataObject/CustomLayout/ImportCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/ImportCustomLayoutHandler.php @@ -17,18 +17,20 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ImportCustomLayout\ImportCustomLayoutPayload; use OpenDxp\Model\DataObject; use OpenDxp\Model\Exception\ConfigWriteException; final class ImportCustomLayoutHandler { - public function __invoke(?string $id, array $importData): void + public function __invoke(ImportCustomLayoutPayload $payload): void { - $customLayout = DataObject\ClassDefinition\CustomLayout::getById($id); + $customLayout = DataObject\ClassDefinition\CustomLayout::getById($payload->id); if (!$customLayout) { return; } + $importData = $payload->importData; $layout = DataObject\ClassDefinition\Service::generateLayoutTreeFromArray($importData['layoutDefinitions'], true); $customLayout->setLayoutDefinitions($layout); if (isset($importData['name'])) { diff --git a/src/Handler/DataObject/CustomLayout/SaveCustomLayout/SaveCustomLayoutPayload.php b/src/Handler/DataObject/CustomLayout/SaveCustomLayout/SaveCustomLayoutPayload.php new file mode 100644 index 00000000..afa092dd --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/SaveCustomLayout/SaveCustomLayoutPayload.php @@ -0,0 +1,38 @@ +request->getString('id'), + configuration: json_decode($request->request->getString('configuration'), true) ?? [], + values: json_decode($request->request->getString('values'), true) ?? [], + ); + } +} diff --git a/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php index fdbd4f2e..30eb77f5 100644 --- a/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SaveCustomLayout\SaveCustomLayoutPayload; use OpenDxp\Model\DataObject; use OpenDxp\Model\Exception\ConfigWriteException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -24,8 +25,12 @@ final class SaveCustomLayoutHandler { - public function __invoke(string $id, array $configuration, array $values): DataObject\ClassDefinition\CustomLayout + public function __invoke(SaveCustomLayoutPayload $payload): DataObject\ClassDefinition\CustomLayout { + $id = $payload->id; + $configuration = $payload->configuration; + $values = $payload->values; + $customLayout = DataObject\ClassDefinition\CustomLayout::getById($id) ?? throw new NotFoundHttpException(); diff --git a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierPayload.php b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierPayload.php new file mode 100644 index 00000000..4be04c02 --- /dev/null +++ b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierPayload.php @@ -0,0 +1,30 @@ +query->getString('classId')); + } +} diff --git a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php index a0a9ebb2..ecfb6392 100644 --- a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php +++ b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php @@ -17,20 +17,21 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier\SuggestCustomLayoutIdentifierPayload; use OpenDxp\Model\DataObject; final class SuggestCustomLayoutIdentifierHandler { - public function __invoke(string $classId): SuggestCustomLayoutIdentifierResult + public function __invoke(SuggestCustomLayoutIdentifierPayload $payload): SuggestCustomLayoutIdentifierResult { - $identifier = DataObject\ClassDefinition\CustomLayout::getIdentifier($classId); + $identifier = DataObject\ClassDefinition\CustomLayout::getIdentifier($payload->classId); $existingIds = []; $existingNames = []; foreach ((new DataObject\ClassDefinition\CustomLayout\Listing())->load() as $item) { $existingIds[] = $item->getId(); - if ($item->getClassId() == $classId) { + if ($item->getClassId() == $payload->classId) { $existingNames[] = $item->getName(); } } diff --git a/src/Handler/DataObject/DataObjectGridProxyHandler.php b/src/Handler/DataObject/DataObjectGridProxyHandler.php index 5e91f6f9..58407236 100644 --- a/src/Handler/DataObject/DataObjectGridProxyHandler.php +++ b/src/Handler/DataObject/DataObjectGridProxyHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DataObjectGridProxyPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService; use OpenDxp\Model\DataObject; use Symfony\Component\EventDispatcher\GenericEvent; @@ -30,15 +31,16 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke(array $allParams, string $fallbackLocale): DataObjectGridProxyResult + public function __invoke(DataObjectGridProxyPayload $payload): DataObjectGridProxyResult { + $allParams = $payload->allParams; $filterPrepareEvent = new GenericEvent($this, ['requestParams' => $allParams]); $this->eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::OBJECT_LIST_BEFORE_FILTER_PREPARE); $allParams = $filterPrepareEvent->getArgument('requestParams'); $requestedLanguage = $allParams['language'] ?? null; if (!$requestedLanguage) { - $requestedLanguage = $fallbackLocale; + $requestedLanguage = $payload->locale; } return new DataObjectGridProxyResult( diff --git a/src/Handler/DataObject/DeleteDataObjectHandler.php b/src/Handler/DataObject/DeleteDataObjectHandler.php index 59d6b1fd..4a50d2b0 100644 --- a/src/Handler/DataObject/DeleteDataObjectHandler.php +++ b/src/Handler/DataObject/DeleteDataObjectHandler.php @@ -17,14 +17,18 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DeleteDataObjectPayload; use OpenDxp\Model\DataObject; use RuntimeException; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; final class DeleteDataObjectHandler { - public function __invoke(string $type, int $id, int $amount = 0): DeleteDataObjectResult + public function __invoke(DeleteDataObjectPayload $payload): DeleteDataObjectResult { + $type = $payload->type; + $id = $payload->id; + $amount = $payload->amount; if ($type === 'children') { $parentObject = DataObject::getById($id); diff --git a/src/Handler/DataObject/FieldCollection/DeleteFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/DeleteFieldCollectionHandler.php index 9abefadc..b2fbf4ff 100644 --- a/src/Handler/DataObject/FieldCollection/DeleteFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/DeleteFieldCollectionHandler.php @@ -17,13 +17,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Model\DataObject; final class DeleteFieldCollectionHandler { - public function __invoke(string $id): void + public function __invoke(StringIdBodyPayload $payload): void { - $fc = DataObject\Fieldcollection\Definition::getByKey($id); + $fc = DataObject\Fieldcollection\Definition::getByKey($payload->id); $fc->delete(); } } diff --git a/src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionPayload.php b/src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionPayload.php new file mode 100644 index 00000000..57cbe0df --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionPayload.php @@ -0,0 +1,30 @@ +query->getString('id')); + } +} diff --git a/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php index 979f80f0..bf7c31f0 100644 --- a/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php @@ -17,18 +17,19 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollection\ExportFieldCollectionPayload; use OpenDxp\Logger; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class ExportFieldCollectionHandler { - public function __invoke(string $id): ExportFieldCollectionResult + public function __invoke(ExportFieldCollectionPayload $payload): ExportFieldCollectionResult { - $fieldCollection = DataObject\Fieldcollection\Definition::getByKey($id); + $fieldCollection = DataObject\Fieldcollection\Definition::getByKey($payload->id); if (!$fieldCollection instanceof DataObject\Fieldcollection\Definition) { - $errorMessage = ': Field-Collection with id [ ' . $id . ' not found. ]'; + $errorMessage = ': Field-Collection with id [ ' . $payload->id . ' not found. ]'; Logger::error($errorMessage); throw new NotFoundHttpException($errorMessage); diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionPayload.php b/src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionPayload.php new file mode 100644 index 00000000..23c6d305 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionPayload.php @@ -0,0 +1,30 @@ +query->getString('id')); + } +} diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionHandler.php index a142b817..72914105 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionHandler.php @@ -17,13 +17,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollection\GetFieldCollectionPayload; use OpenDxp\Model\DataObject; final class GetFieldCollectionHandler { - public function __invoke(string $id): GetFieldCollectionResult + public function __invoke(GetFieldCollectionPayload $payload): GetFieldCollectionResult { - $fc = DataObject\Fieldcollection\Definition::getByKey($id); + $fc = DataObject\Fieldcollection\Definition::getByKey($payload->id); return new GetFieldCollectionResult($fc->getObjectVars(), $fc->isWritable()); } diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionList/GetFieldCollectionListPayload.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionList/GetFieldCollectionListPayload.php new file mode 100644 index 00000000..94ec1ab7 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionList/GetFieldCollectionListPayload.php @@ -0,0 +1,42 @@ +query->getString('allowedTypes'); + + return new static( + layoutId: $request->query->getString('layoutId') ?: null, + allowedTypes: $allowedTypes !== '' ? explode(',', $allowedTypes) : null, + fieldName: $request->query->getString('field_name') ?: null, + objectId: $request->query->getInt('object_id'), + ); + } +} diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php index d157db5c..ee6a8372 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php @@ -18,23 +18,25 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionList\GetFieldCollectionListPayload; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; -use OpenDxp\Model\User; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetFieldCollectionListHandler { public function __construct( - private readonly AdminUserContextInterface $userContext,private readonly EventDispatcherInterface $eventDispatcher) {} + private readonly AdminUserContextInterface $userContext, + private readonly EventDispatcherInterface $eventDispatcher, + ) {} - public function __invoke( - ?array $allowedTypes, - ?string $fieldName, - int $objectId, - ?string $layoutId, - ): FieldCollectionListResult { + public function __invoke(GetFieldCollectionListPayload $payload): FieldCollectionListResult + { + $allowedTypes = $payload->allowedTypes; + $fieldName = $payload->fieldName; + $objectId = $payload->objectId; + $layoutId = $payload->layoutId; $adminUser = $this->userContext->getAdminUser(); $list = (new DataObject\Fieldcollection\Definition\Listing())->load(); $currentLayoutId = $layoutId; diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/GetFieldCollectionTreePayload.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/GetFieldCollectionTreePayload.php new file mode 100644 index 00000000..945432c2 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/GetFieldCollectionTreePayload.php @@ -0,0 +1,42 @@ +query->getString('allowedTypes'); + + return new static( + forObjectEditor: (bool) $request->query->getString('forObjectEditor'), + allowedTypes: $allowedTypes !== '' ? explode(',', $allowedTypes) : null, + objectId: $request->query->getInt('object_id'), + layoutId: $request->query->getString('layoutId') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionTreeHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionTreeHandler.php index 3eaf901d..3f84e76c 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionTreeHandler.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionTreeHandler.php @@ -18,23 +18,25 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTree\GetFieldCollectionTreePayload; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; -use OpenDxp\Model\User; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetFieldCollectionTreeHandler { public function __construct( - private readonly AdminUserContextInterface $userContext,private readonly EventDispatcherInterface $eventDispatcher) {} + private readonly AdminUserContextInterface $userContext, + private readonly EventDispatcherInterface $eventDispatcher, + ) {} - public function __invoke( - bool $forObjectEditor, - ?array $allowedTypes, - int $objectId, - ?string $layoutId, - ): FieldCollectionTreeResult { + public function __invoke(GetFieldCollectionTreePayload $payload): FieldCollectionTreeResult + { + $forObjectEditor = $payload->forObjectEditor; + $allowedTypes = $payload->allowedTypes; + $objectId = $payload->objectId; + $layoutId = $payload->layoutId; $adminUser = $this->userContext->getAdminUser(); $list = (new DataObject\Fieldcollection\Definition\Listing())->load(); diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsages/GetFieldCollectionUsagesPayload.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsages/GetFieldCollectionUsagesPayload.php new file mode 100644 index 00000000..4d148dd8 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsages/GetFieldCollectionUsagesPayload.php @@ -0,0 +1,30 @@ +query->getString('key')); + } +} diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php index 81166f3c..1f4eea9b 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php @@ -17,18 +17,19 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionUsages\GetFieldCollectionUsagesPayload; use OpenDxp\Model\DataObject\ClassDefinition\Data\Fieldcollections; use OpenDxp\Model\DataObject\ClassDefinition\Listing; final class GetFieldCollectionUsagesHandler { - public function __invoke(string $key): array + public function __invoke(GetFieldCollectionUsagesPayload $payload): array { $result = []; foreach ((new Listing())->load() as $class) { foreach ($class->getFieldDefinitions() as $fieldDef) { - if ($fieldDef instanceof Fieldcollections && in_array($key, $fieldDef->getAllowedTypes())) { + if ($fieldDef instanceof Fieldcollections && in_array($payload->key, $fieldDef->getAllowedTypes())) { $result[] = [ 'class' => $class->getName(), 'field' => $fieldDef->getName(), diff --git a/src/Handler/DataObject/FieldCollection/ImportFieldCollection/ImportFieldCollectionPayload.php b/src/Handler/DataObject/FieldCollection/ImportFieldCollection/ImportFieldCollectionPayload.php new file mode 100644 index 00000000..48557885 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/ImportFieldCollection/ImportFieldCollectionPayload.php @@ -0,0 +1,40 @@ +files->get('Filedata'); + + return new static( + id: $request->query->getString('id'), + json: file_get_contents($file->getPathname()), + ); + } +} diff --git a/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php index 8539037c..f727ef81 100644 --- a/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php @@ -17,17 +17,18 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ImportFieldCollection\ImportFieldCollectionPayload; use OpenDxp\Model\DataObject; use RuntimeException; final class ImportFieldCollectionHandler { - public function __invoke(string $id, string $jsonData): void + public function __invoke(ImportFieldCollectionPayload $payload): void { - $fieldCollection = DataObject\Fieldcollection\Definition::getByKey($id); + $fieldCollection = DataObject\Fieldcollection\Definition::getByKey($payload->id); - if (!DataObject\ClassDefinition\Service::importFieldCollectionFromJson($fieldCollection, $jsonData)) { - throw new RuntimeException('Failed to import field collection: ' . $id); + if (!DataObject\ClassDefinition\Service::importFieldCollectionFromJson($fieldCollection, $payload->json)) { + throw new RuntimeException('Failed to import field collection: ' . $payload->id); } } } diff --git a/src/Handler/DataObject/FieldCollection/UpdateFieldCollection/UpdateFieldCollectionPayload.php b/src/Handler/DataObject/FieldCollection/UpdateFieldCollection/UpdateFieldCollectionPayload.php new file mode 100644 index 00000000..d6add299 --- /dev/null +++ b/src/Handler/DataObject/FieldCollection/UpdateFieldCollection/UpdateFieldCollectionPayload.php @@ -0,0 +1,44 @@ +request->getString('key'), + title: $request->request->getString('title'), + group: $request->request->getString('group'), + isAdd: $request->request->getString('task') === 'add', + values: $request->request->has('values') ? (json_decode($request->request->getString('values'), true) ?? null) : null, + configuration: $request->request->has('configuration') ? (json_decode($request->request->getString('configuration'), true) ?? null) : null, + ); + } +} diff --git a/src/Handler/DataObject/FieldCollection/UpdateFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/UpdateFieldCollectionHandler.php index 426eadec..93120f40 100644 --- a/src/Handler/DataObject/FieldCollection/UpdateFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/UpdateFieldCollectionHandler.php @@ -17,19 +17,21 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\UpdateFieldCollection\UpdateFieldCollectionPayload; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; final class UpdateFieldCollectionHandler { - public function __invoke( - string $key, - string $title, - string $group, - bool $isAdd, - ?array $values, - ?array $configuration, - ): DataObject\Fieldcollection\Definition { + public function __invoke(UpdateFieldCollectionPayload $payload): DataObject\Fieldcollection\Definition + { + $key = $payload->key; + $title = $payload->title; + $group = $payload->group; + $isAdd = $payload->isAdd; + $values = $payload->values; + $configuration = $payload->configuration; + if ($isAdd) { $list = new DataObject\Fieldcollection\Definition\Listing(); foreach ($list->loadNames() as $fcName) { diff --git a/src/Handler/DataObject/GetDataObjectFolderHandler.php b/src/Handler/DataObject/GetDataObjectFolderHandler.php index 0809ec8d..9ca5af90 100644 --- a/src/Handler/DataObject/GetDataObjectFolderHandler.php +++ b/src/Handler/DataObject/GetDataObjectFolderHandler.php @@ -19,6 +19,7 @@ use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; use OpenDxp\Model\Element; @@ -35,13 +36,13 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke(int $objectId): GetDataObjectFolderResult + public function __invoke(IdQueryPayload $payload): GetDataObjectFolderResult { $adminUser = $this->userContext->getAdminUser(); - $object = DataObject::getById($objectId); + $object = DataObject::getById($payload->id); if (!$object) { - throw new NotFoundHttpException(sprintf('DataObject with id %d not found', $objectId)); + throw new NotFoundHttpException(sprintf('DataObject with id %d not found', $payload->id)); } if (!$object->isAllowed('view')) { diff --git a/src/Handler/DataObject/GetDataObjectHandler.php b/src/Handler/DataObject/GetDataObjectHandler.php index d073ea76..a446a2e1 100644 --- a/src/Handler/DataObject/GetDataObjectHandler.php +++ b/src/Handler/DataObject/GetDataObjectHandler.php @@ -36,6 +36,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Throwable; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\GetDataObjectPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetDataObjectHandler @@ -48,8 +49,10 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke(int $id, ?int $layoutId): GetDataObjectResult + public function __invoke(GetDataObjectPayload $payload): GetDataObjectResult { + $id = $payload->id; + $layoutId = $payload->layoutId; $objectFromDatabase = DataObject\Concrete::getById($id); if (!$objectFromDatabase instanceof DataObject\Concrete) { throw new NotFoundHttpException('element_not_found'); diff --git a/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlPayload.php b/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlPayload.php new file mode 100644 index 00000000..4af7197c --- /dev/null +++ b/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlPayload.php @@ -0,0 +1,28 @@ +object; + $queryParams = $payload->queryParams; $url = null; if ($previewService = $object->getClass()->getPreviewGenerator()) { $url = $previewService->generatePreviewUrl($object, ['preview' => true, ...$queryParams]); diff --git a/src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoPayload.php b/src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoPayload.php new file mode 100644 index 00000000..d79f45ec --- /dev/null +++ b/src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoPayload.php @@ -0,0 +1,37 @@ +query->getString('path') ?: null, + limit: $request->query->getInt('limit', 30), + ); + } +} diff --git a/src/Handler/DataObject/GetIdPathPagingInfoHandler.php b/src/Handler/DataObject/GetIdPathPagingInfoHandler.php index 836db3f1..f5b24dbe 100644 --- a/src/Handler/DataObject/GetIdPathPagingInfoHandler.php +++ b/src/Handler/DataObject/GetIdPathPagingInfoHandler.php @@ -17,12 +17,15 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfo\GetIdPathPagingInfoPayload; use OpenDxp\Model\DataObject; final class GetIdPathPagingInfoHandler { - public function __invoke(string $path, int $limit): GetIdPathPagingInfoResult + public function __invoke(GetIdPathPagingInfoPayload $payload): GetIdPathPagingInfoResult { + $path = $payload->path; + $limit = $payload->limit; $pathParts = explode('/', $path); $id = (int) array_pop($pathParts); diff --git a/src/Handler/DataObject/GetSelectOptionsHandler.php b/src/Handler/DataObject/GetSelectOptionsHandler.php index b83e1749..c4a655cd 100644 --- a/src/Handler/DataObject/GetSelectOptionsHandler.php +++ b/src/Handler/DataObject/GetSelectOptionsHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\GetSelectOptionsPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPayloadMapper; use OpenDxp\Model\DataObject; use OpenDxp\Model\DataObject\ClassDefinition\Helper\OptionsProviderResolver; @@ -26,21 +27,21 @@ final class GetSelectOptionsHandler { public function __construct(private readonly DataObjectPayloadMapper $mapper) {} - public function __invoke(int $objectId, ?array $changedData, array $fieldDefinitionConfig, array $context): array + public function __invoke(GetSelectOptionsPayload $payload): array { - $object = DataObject\Concrete::getById($objectId); + $object = DataObject\Concrete::getById($payload->objectId); if (!$object instanceof DataObject\Concrete) { throw new NotFoundHttpException('Object not found.'); } - if ($changedData !== null) { - $this->mapper->applyChanges($object, $changedData); + if ($payload->changedData !== null) { + $this->mapper->applyChanges($object, $payload->changedData); } /** @var DataObject\ClassDefinition\Data\Select|DataObject\ClassDefinition\Data\Multiselect $fieldDefinition */ $fieldDefinition = DataObject\Classificationstore\Service::getFieldDefinitionFromJson( - $fieldDefinitionConfig, - $fieldDefinitionConfig['fieldtype'] + $payload->fieldDefinitionConfig, + $payload->fieldDefinitionConfig['fieldtype'] ); $optionsProvider = OptionsProviderResolver::resolveProvider( @@ -55,7 +56,7 @@ public function __invoke(int $objectId, ?array $changedData, array $fieldDefinit 'object' => $object, 'fieldname' => $fieldDefinition->getName(), 'class' => $object->getClass(), - 'context' => $context, + 'context' => $payload->context, ], $fieldDefinition ); diff --git a/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php b/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php index 19bcf7ba..ed0b0c3a 100644 --- a/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php +++ b/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php @@ -19,7 +19,6 @@ use OpenDxp\Db; use OpenDxp\Model\DataObject; -use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -30,10 +29,10 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $objectId, string $classId, string $searchType): void + public function __invoke(ApplyGridConfigToAllPayload $payload): void { $adminUser = $this->userContext->getAdminUser(); - $object = DataObject::getById($objectId); + $object = DataObject::getById($payload->objectId); if (!$object) { throw new NotFoundHttpException(); } @@ -44,7 +43,7 @@ public function __invoke(int $objectId, string $classId, string $searchType): vo Db::get()->executeStatement( 'DELETE FROM gridconfig_favourites WHERE ownerId = ? AND classId = ? AND searchType = ? AND objectId != ? AND objectId != 0', - [$adminUser->getId(), $classId, $searchType, $objectId] + [$adminUser->getId(), $payload->classId, $payload->searchType, $payload->objectId] ); } } diff --git a/src/Handler/DataObject/Helper/ApplyGridConfigToAllPayload.php b/src/Handler/DataObject/Helper/ApplyGridConfigToAllPayload.php new file mode 100644 index 00000000..af0263d1 --- /dev/null +++ b/src/Handler/DataObject/Helper/ApplyGridConfigToAllPayload.php @@ -0,0 +1,39 @@ +request->getInt('objectId'), + classId: $request->request->getString('classId'), + searchType: $request->request->getString('searchType'), + ); + } +} diff --git a/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php index 36f349ee..dbc24879 100644 --- a/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php +++ b/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php @@ -29,10 +29,10 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $gridConfigId): void + public function __invoke(DeleteDataObjectGridColumnConfigPayload $payload): void { $adminUser = $this->userContext->getAdminUser(); - $gridConfig = GridConfig::getById($gridConfigId); + $gridConfig = GridConfig::getById($payload->gridConfigId); if (!$gridConfig) { throw new NotFoundHttpException('Grid config not found: ' . $gridConfigId); } diff --git a/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigPayload.php b/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigPayload.php new file mode 100644 index 00000000..1970bf14 --- /dev/null +++ b/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigPayload.php @@ -0,0 +1,35 @@ +request->getInt('gridConfigId'), + ); + } +} diff --git a/src/Handler/DataObject/Helper/DeleteGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigHandler.php similarity index 56% rename from src/Handler/DataObject/Helper/DeleteGridColumnConfigHandler.php rename to src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigHandler.php index 3cd50d57..f0632a63 100644 --- a/src/Handler/DataObject/Helper/DeleteGridColumnConfigHandler.php +++ b/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigHandler.php @@ -15,13 +15,13 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteGridColumnConfig; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Service\Grid\DataObjectGridColumnConfigResolver; use OpenDxp\Config; use Symfony\Component\EventDispatcher\GenericEvent; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; final class DeleteGridColumnConfigHandler @@ -30,17 +30,31 @@ public function __construct( private readonly DataObjectGridColumnConfigResolver $gridConfigResolver, private readonly EventDispatcherInterface $eventDispatcher, private readonly Config $config, + private readonly RequestStack $requestStack, ) {} - public function __invoke(Request $request, array $params): array + public function __invoke(DeleteGridColumnConfigPayload $payload): array { - $resolverResult = $this->gridConfigResolver->resolve($request, $params, true); + $params = [ + 'id' => $payload->id, + 'objectId' => $payload->objectId, + 'name' => $payload->name, + 'type' => $payload->type, + 'types' => $payload->types, + 'gridtype' => $payload->gridtype, + 'gridConfigId' => $payload->gridConfigId, + 'searchType' => $payload->searchType, + 'noSystemColumns' => $payload->noSystemColumns, + 'noBrickColumns' => $payload->noBrickColumns, + ]; + + $resolverResult = $this->gridConfigResolver->resolve($payload->locale, $params, null, true); $data = [...$resolverResult->jsonSerialize(), 'deleteSuccess' => true]; $event = new GenericEvent($this, [ - 'data' => $data, - 'request' => $request, - 'config' => $this->config, + 'data' => $data, + 'request' => $this->requestStack->getCurrentRequest(), + 'config' => $this->config, 'context' => 'delete', ]); $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_GRID_GET_COLUMN_CONFIG_PRE_SEND_DATA); diff --git a/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigPayload.php b/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigPayload.php new file mode 100644 index 00000000..7a202d5e --- /dev/null +++ b/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigPayload.php @@ -0,0 +1,55 @@ +request->getString('id') ?: null, + objectId: is_numeric($request->request->get('objectId')) ? $request->request->getInt('objectId') : null, + name: $request->request->getString('name') ?: null, + type: $request->request->getString('type') ?: null, + types: $request->request->getString('types') ?: null, + gridtype: $request->request->getString('gridtype') ?: null, + gridConfigId: is_numeric($request->request->get('gridConfigId')) ? $request->request->getInt('gridConfigId') : null, + searchType: $request->request->getString('searchType') ?: null, + noSystemColumns: $request->query->getBoolean('no_system_columns'), + noBrickColumns: $request->query->getBoolean('no_brick_columns'), + locale: $request->getLocale(), + ); + } +} diff --git a/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php b/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php index 3a786e7d..1a192489 100644 --- a/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php +++ b/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php @@ -41,24 +41,12 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke( - string $fileHandle, - array $ids, - string $classId, - string $delimiter, - string $header, - ?string $userTimezone, - array $allParams, - string $requestedLanguage, - array $fields, - bool $addTitles, - bool $enableInheritance, - array $context, - ): void { - UserTimezone::setUserTimezone($userTimezone); - DataObject\Concrete::setGetInheritedValues($enableInheritance); - - $class = DataObject\ClassDefinition::getById($classId); + public function __invoke(DoDataObjectExportPayload $payload): void + { + UserTimezone::setUserTimezone($payload->userTimezone); + DataObject\Concrete::setGetInheritedValues($payload->enableInheritance); + + $class = DataObject\ClassDefinition::getById($payload->classId); if (!$class) { throw new InvalidArgumentException('No class definition found'); } @@ -69,7 +57,7 @@ public function __invoke( $list = new $listClass(); $quotedIds = []; - foreach ($ids as $id) { + foreach ($payload->ids as $id) { $quotedIds[] = $list->quote($id); } @@ -79,50 +67,50 @@ public function __invoke( $beforeListExportEvent = new GenericEvent(null, [ 'list' => $list, - 'context' => $allParams, + 'context' => $payload->allParams, ]); $this->eventDispatcher->dispatch($beforeListExportEvent, AdminEvents::OBJECT_LIST_BEFORE_EXPORT); $list = $beforeListExportEvent->getArgument('list'); $csv = DataObject\Service::getCsvData( - $requestedLanguage, + $payload->requestedLanguage, $this->localeService, $list, - $fields, - $header, - $addTitles, - $context + $payload->fields, + $payload->header, + $payload->addTitles, + $payload->context ); $temp = tmpfile(); try { $storage = Storage::get('temp'); - $csvFile = $this->gridExportService->getCsvFile($fileHandle); + $csvFile = $this->gridExportService->getCsvFile($payload->fileHandle); $fileStream = $storage->readStream($csvFile); stream_copy_to_stream($fileStream, $temp, null, 0); $firstLine = true; - if ($addTitles && $header === 'no_header') { + if ($payload->addTitles && $payload->header === 'no_header') { array_shift($csv); $firstLine = false; } $lineCount = count($csv); - if (!$addTitles && $lineCount > 0) { + if (!$payload->addTitles && $lineCount > 0) { fwrite($temp, "\r\n"); } for ($i = 0; $i < $lineCount; $i++) { $line = $csv[$i]; - if ($addTitles && $firstLine) { + if ($payload->addTitles && $firstLine) { $firstLine = false; - fwrite($temp, implode($delimiter, $line)); + fwrite($temp, implode($payload->delimiter, $line)); } else { - fwrite($temp, implode($delimiter, array_map($this->gridColumnConfigService->encode(...), $line))); + fwrite($temp, implode($payload->delimiter, array_map($this->gridColumnConfigService->encode(...), $line))); } if ($i < $lineCount - 1) { fwrite($temp, "\r\n"); @@ -132,7 +120,7 @@ public function __invoke( $storage->writeStream($csvFile, $temp); } catch (UnableToReadFile $exception) { Logger::err($exception->getMessage()); - throw new BadRequestHttpException(sprintf('export file not found: %s', $fileHandle)); + throw new BadRequestHttpException(sprintf('export file not found: %s', $payload->fileHandle)); } finally { if (is_resource($temp)) { fclose($temp); diff --git a/src/Handler/DataObject/Helper/DoDataObjectExportPayload.php b/src/Handler/DataObject/Helper/DoDataObjectExportPayload.php new file mode 100644 index 00000000..77a753aa --- /dev/null +++ b/src/Handler/DataObject/Helper/DoDataObjectExportPayload.php @@ -0,0 +1,66 @@ +request->getString('settings'), true) ?? []; + $fieldsRaw = $request->request->all('fields'); + $contextFromRequest = $request->request->getString('context') ?: null; + $context = ['source' => 'opendxp-export']; + if ($contextFromRequest) { + $context = [...$context, ...json_decode($contextFromRequest, true)]; + } + + return new static( + fileHandle: File::getValidFilename($request->request->getString('fileHandle')), + ids: $request->request->all('ids'), + classId: $request->request->getString('classId'), + delimiter: $settings['delimiter'] ?? ';', + header: $settings['header'] ?? 'title', + userTimezone: $request->request->getString('userTimezone') ?: null, + allParams: [...$request->request->all(), ...$request->query->all()], + requestedLanguage: $request->request->getString('language') ?: $request->getLocale(), + fields: !empty($fieldsRaw) ? (json_decode($fieldsRaw[0], true) ?? []) : [], + addTitles: (bool) $request->request->get('initial'), + enableInheritance: (bool) ($settings['enableInheritance'] ?? false), + context: $context, + ); + } +} diff --git a/src/Handler/DataObject/Helper/ExecuteBatchHandler.php b/src/Handler/DataObject/Helper/ExecuteBatchHandler.php index b4b45590..411675bd 100644 --- a/src/Handler/DataObject/Helper/ExecuteBatchHandler.php +++ b/src/Handler/DataObject/Helper/ExecuteBatchHandler.php @@ -27,10 +27,10 @@ public function __construct( private readonly GridBatchService $gridBatchService, ) {} - public function __invoke(array $params, string $locale): bool + public function __invoke(ExecuteBatchPayload $payload): bool { $adminUser = $this->userContext->getAdminUser(); - return $this->gridBatchService->executeObjectBatch($params, $locale, $adminUser); + return $this->gridBatchService->executeObjectBatch($payload->params, $payload->locale, $adminUser); } } diff --git a/src/Handler/DataObject/Helper/ExecuteBatchPayload.php b/src/Handler/DataObject/Helper/ExecuteBatchPayload.php new file mode 100644 index 00000000..18312ac5 --- /dev/null +++ b/src/Handler/DataObject/Helper/ExecuteBatchPayload.php @@ -0,0 +1,41 @@ +request->has('data') + ? (json_decode($request->request->getString('data'), true) ?? []) + : [], + locale: $request->getLocale(), + hasData: $request->request->has('data'), + ); + } +} diff --git a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php b/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php index 1cadadd8..42041473 100644 --- a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php +++ b/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php @@ -22,8 +22,9 @@ final class GetAvailableVisibleFieldsHandler { - public function __invoke(?string $classes): GetAvailableVisibleFieldsResult + public function __invoke(GetAvailableVisibleFieldsPayload $payload): GetAvailableVisibleFieldsResult { + $classes = $payload->classes; if ($classes === null) { return new GetAvailableVisibleFieldsResult([]); } diff --git a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsPayload.php b/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsPayload.php new file mode 100644 index 00000000..e7b0532d --- /dev/null +++ b/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsPayload.php @@ -0,0 +1,35 @@ +query->getString('classes') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Helper/GetBatchJobsHandler.php b/src/Handler/DataObject/Helper/GetBatchJobsHandler.php index 69a7b941..0d8a6838 100644 --- a/src/Handler/DataObject/Helper/GetBatchJobsHandler.php +++ b/src/Handler/DataObject/Helper/GetBatchJobsHandler.php @@ -27,12 +27,12 @@ public function __construct( private readonly GridBatchService $gridBatchService, ) {} - public function __invoke(array $allParams, string $locale): GetBatchJobsResult + public function __invoke(GetBatchJobsPayload $payload): GetBatchJobsResult { $adminUser = $this->userContext->getAdminUser(); return new GetBatchJobsResult( - jobs: $this->gridBatchService->getObjectBatchJobIds($allParams, $locale, $adminUser), + jobs: $this->gridBatchService->getObjectBatchJobIds($payload->allParams, $payload->locale, $adminUser), ); } } diff --git a/src/Handler/DataObject/Helper/GetBatchJobsPayload.php b/src/Handler/DataObject/Helper/GetBatchJobsPayload.php new file mode 100644 index 00000000..235ce33b --- /dev/null +++ b/src/Handler/DataObject/Helper/GetBatchJobsPayload.php @@ -0,0 +1,37 @@ +request->all(), ...$request->query->all()], + locale: $request->request->getString('language') ?: $request->getLocale(), + ); + } +} diff --git a/src/Handler/DataObject/Helper/GetExportConfigsHandler.php b/src/Handler/DataObject/Helper/GetExportConfigsHandler.php index cd9da735..478d1e77 100644 --- a/src/Handler/DataObject/Helper/GetExportConfigsHandler.php +++ b/src/Handler/DataObject/Helper/GetExportConfigsHandler.php @@ -27,13 +27,13 @@ public function __construct( private readonly GridColumnConfigService $gridColumnConfigService, ) {} - public function __invoke(?string $classId): array + public function __invoke(GetExportConfigsPayload $payload): array { $adminUser = $this->userContext->getAdminUser(); $userId = $adminUser?->getId() ?? 0; - $list = $this->gridColumnConfigService->getMyOwnColumnConfigs($userId, $classId); - $list = [...$list, ...$this->gridColumnConfigService->getSharedColumnConfigs($adminUser, $classId)]; + $list = $this->gridColumnConfigService->getMyOwnColumnConfigs($userId, $payload->classId); + $list = [...$list, ...$this->gridColumnConfigService->getSharedColumnConfigs($adminUser, $payload->classId)]; $result = [['id' => -1, 'name' => '--default--']]; diff --git a/src/Handler/DataObject/Helper/GetExportConfigsPayload.php b/src/Handler/DataObject/Helper/GetExportConfigsPayload.php new file mode 100644 index 00000000..fcc0f06c --- /dev/null +++ b/src/Handler/DataObject/Helper/GetExportConfigsPayload.php @@ -0,0 +1,35 @@ +query->getString('classId') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Helper/GetExportJobsHandler.php b/src/Handler/DataObject/Helper/GetExportJobsHandler.php index 14aed124..7a279946 100644 --- a/src/Handler/DataObject/Helper/GetExportJobsHandler.php +++ b/src/Handler/DataObject/Helper/GetExportJobsHandler.php @@ -32,8 +32,9 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke(array $allParams, string $requestedLanguage): GetExportJobsResult + public function __invoke(GetExportJobsPayload $payload): GetExportJobsResult { + $allParams = $payload->allParams; $fieldnames = []; $fields = json_decode($allParams['fields'][0], true); foreach ($fields as $field) { @@ -41,7 +42,7 @@ public function __invoke(array $allParams, string $requestedLanguage): GetExport } $allParams['fields'] = $fieldnames; - $list = $this->gridHelperService->prepareListingForGrid($allParams, $requestedLanguage); + $list = $this->gridHelperService->prepareListingForGrid($allParams, $payload->requestedLanguage); $beforeListPrepareEvent = new GenericEvent($this, [ 'list' => $list, diff --git a/src/Handler/DataObject/Helper/GetExportJobsPayload.php b/src/Handler/DataObject/Helper/GetExportJobsPayload.php new file mode 100644 index 00000000..cd75a725 --- /dev/null +++ b/src/Handler/DataObject/Helper/GetExportJobsPayload.php @@ -0,0 +1,37 @@ +request->all(), ...$request->query->all()], + requestedLanguage: $request->request->getString('language') ?: $request->getLocale(), + ); + } +} diff --git a/src/Handler/DataObject/Helper/GetGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/GetGridColumnConfig/GetGridColumnConfigHandler.php similarity index 54% rename from src/Handler/DataObject/Helper/GetGridColumnConfigHandler.php rename to src/Handler/DataObject/Helper/GetGridColumnConfig/GetGridColumnConfigHandler.php index 25800de8..df813a63 100644 --- a/src/Handler/DataObject/Helper/GetGridColumnConfigHandler.php +++ b/src/Handler/DataObject/Helper/GetGridColumnConfig/GetGridColumnConfigHandler.php @@ -15,13 +15,13 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetGridColumnConfig; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Service\Grid\DataObjectGridColumnConfigResolver; use OpenDxp\Config; use Symfony\Component\EventDispatcher\GenericEvent; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; final class GetGridColumnConfigHandler @@ -30,16 +30,30 @@ public function __construct( private readonly DataObjectGridColumnConfigResolver $gridConfigResolver, private readonly EventDispatcherInterface $eventDispatcher, private readonly Config $config, + private readonly RequestStack $requestStack, ) {} - public function __invoke(Request $request, array $params): array + public function __invoke(GetGridColumnConfigPayload $payload): array { - $result = $this->gridConfigResolver->resolve($request, $params); + $params = [ + 'id' => $payload->id, + 'objectId' => $payload->objectId, + 'name' => $payload->name, + 'type' => $payload->type, + 'types' => $payload->types, + 'gridtype' => $payload->gridtype, + 'gridConfigId' => $payload->gridConfigId, + 'searchType' => $payload->searchType, + 'noSystemColumns' => $payload->noSystemColumns, + 'noBrickColumns' => $payload->noBrickColumns, + ]; + + $result = $this->gridConfigResolver->resolve($payload->locale, $params, $payload->helperColumnsBag); $event = new GenericEvent($this, [ - 'data' => $result->jsonSerialize(), - 'request' => $request, - 'config' => $this->config, + 'data' => $result->jsonSerialize(), + 'request' => $this->requestStack->getCurrentRequest(), + 'config' => $this->config, 'context' => 'get', ]); $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_GRID_GET_COLUMN_CONFIG_PRE_SEND_DATA); diff --git a/src/Handler/DataObject/Helper/GetGridColumnConfig/GetGridColumnConfigPayload.php b/src/Handler/DataObject/Helper/GetGridColumnConfig/GetGridColumnConfigPayload.php new file mode 100644 index 00000000..7831c5aa --- /dev/null +++ b/src/Handler/DataObject/Helper/GetGridColumnConfig/GetGridColumnConfigPayload.php @@ -0,0 +1,59 @@ +query->getString('id') ?: null, + objectId: is_numeric($request->query->get('objectId')) ? $request->query->getInt('objectId') : null, + name: $request->query->getString('name') ?: null, + type: $request->query->getString('type') ?: null, + types: $request->query->getString('types') ?: null, + gridtype: $request->query->getString('gridtype') ?: null, + gridConfigId: is_numeric($request->query->get('gridConfigId')) ? $request->query->getInt('gridConfigId') : null, + searchType: $request->query->getString('searchType') ?: null, + noSystemColumns: $request->query->getBoolean('no_system_columns'), + noBrickColumns: $request->query->getBoolean('no_brick_columns'), + locale: $request->getLocale(), + helperColumnsBag: Session::getSessionBag($request->getSession(), 'opendxp_gridconfig'), + ); + } +} diff --git a/src/Handler/DataObject/Helper/ImportUploadHandler.php b/src/Handler/DataObject/Helper/ImportUploadHandler.php index 0cc665ce..bd667045 100644 --- a/src/Handler/DataObject/Helper/ImportUploadHandler.php +++ b/src/Handler/DataObject/Helper/ImportUploadHandler.php @@ -24,10 +24,10 @@ final class ImportUploadHandler { public function __construct(private readonly Filesystem $filesystem) {} - public function __invoke(string $fileContents, string $importId): void + public function __invoke(ImportUploadPayload $payload): void { - $data = Text::convertToUTF8($fileContents); - $importId = str_replace('..', '', $importId); + $data = Text::convertToUTF8($payload->fileContents); + $importId = str_replace('..', '', $payload->importId); $importFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/import_' . $importId; $this->filesystem->dumpFile($importFile, $data); diff --git a/src/Handler/DataObject/Helper/ImportUploadPayload.php b/src/Handler/DataObject/Helper/ImportUploadPayload.php new file mode 100644 index 00000000..6a515d2c --- /dev/null +++ b/src/Handler/DataObject/Helper/ImportUploadPayload.php @@ -0,0 +1,41 @@ +files->get('Filedata'); + + return new static( + fileContents: $file !== null ? (file_get_contents($file->getPathname()) ?: '') : '', + importId: $request->request->getString('importId'), + ); + } +} diff --git a/src/Handler/DataObject/Helper/LoadObjectDataHandler.php b/src/Handler/DataObject/Helper/LoadObjectDataHandler.php index cb703fc0..ef681364 100644 --- a/src/Handler/DataObject/Helper/LoadObjectDataHandler.php +++ b/src/Handler/DataObject/Helper/LoadObjectDataHandler.php @@ -22,13 +22,13 @@ final class LoadObjectDataHandler { - public function __invoke(int $id, array $fields): array + public function __invoke(LoadObjectDataPayload $payload): array { - $object = DataObject::getById($id); + $object = DataObject::getById($payload->id); if (!$object instanceof DataObject) { - throw new DataObjectNotFoundException($id); + throw new DataObjectNotFoundException($payload->id); } - return GridData\DataObject::getData($object, $fields); + return GridData\DataObject::getData($object, $payload->fields); } } diff --git a/src/Handler/DataObject/Helper/LoadObjectDataPayload.php b/src/Handler/DataObject/Helper/LoadObjectDataPayload.php new file mode 100644 index 00000000..c40982cf --- /dev/null +++ b/src/Handler/DataObject/Helper/LoadObjectDataPayload.php @@ -0,0 +1,37 @@ +query->getInt('id'), + fields: $request->query->all('fields'), + ); + } +} diff --git a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteHandler.php b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteHandler.php index 40281831..a4b97ed1 100644 --- a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteHandler.php +++ b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteHandler.php @@ -22,7 +22,6 @@ use OpenDxp\Bundle\AdminBundle\Model\GridConfigFavourite; use OpenDxp\Db; use OpenDxp\Model\DataObject; -use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -34,16 +33,10 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke( - int $objectId, - ?string $classId, - int $gridConfigId, - ?string $searchType, - bool $global, - ?string $type, - ): MarkDataObjectGridConfigFavouriteResult { + public function __invoke(MarkDataObjectGridConfigFavouritePayload $payload): MarkDataObjectGridConfigFavouriteResult + { $adminUser = $this->userContext->getAdminUser(); - $object = DataObject::getById($objectId); + $object = DataObject::getById($payload->objectId); if (!$object) { throw new NotFoundHttpException(); } @@ -52,36 +45,36 @@ public function __invoke( throw new AccessDeniedHttpException(); } - $class = DataObject\ClassDefinition::getById($classId); + $class = DataObject\ClassDefinition::getById($payload->classId); if (!$class) { - throw new BadRequestHttpException('class ' . $classId . ' does not exist anymore'); + throw new BadRequestHttpException('class ' . $payload->classId . ' does not exist anymore'); } $favourite = new GridConfigFavourite(); $favourite->setOwnerId($adminUser->getId()); - $favourite->setClassId($classId); - $favourite->setSearchType($searchType); - $favourite->setType($type); + $favourite->setClassId($payload->classId); + $favourite->setSearchType($payload->searchType); + $favourite->setType($payload->type); $specializedConfigs = false; try { - if ($gridConfigId !== 0) { - $gridConfig = GridConfig::getById($gridConfigId); + if ($payload->gridConfigId !== 0) { + $gridConfig = GridConfig::getById($payload->gridConfigId); $favourite->setGridConfigId($gridConfig->getId()); } - $favourite->setObjectId($objectId); + $favourite->setObjectId($payload->objectId); $favourite->save(); - if ($global) { + if ($payload->global) { $favourite->setObjectId(0); $favourite->save(); } $count = Db::get()->fetchOne( 'SELECT * FROM gridconfig_favourites WHERE ownerId = ? AND classId = ? AND searchType = ? AND objectId != ? AND objectId != 0 AND `type` != ?', - [$adminUser->getId(), $classId, $searchType, $objectId, $type] + [$adminUser->getId(), $payload->classId, $payload->searchType, $payload->objectId, $payload->type] ); $specializedConfigs = $count > 0; } catch (Exception) { diff --git a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouritePayload.php b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouritePayload.php new file mode 100644 index 00000000..25d2e19b --- /dev/null +++ b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouritePayload.php @@ -0,0 +1,45 @@ +request->getInt('objectId'), + classId: $request->request->getString('classId') ?: null, + gridConfigId: $request->request->getInt('gridConfigId'), + searchType: $request->request->getString('searchType') ?: null, + global: (bool) $request->request->get('global'), + type: $request->request->getString('type') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsHandler.php b/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsHandler.php index 7c926baa..ccbaebff 100644 --- a/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsHandler.php +++ b/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsHandler.php @@ -20,15 +20,15 @@ final class PrepareHelperColumnConfigsHandler { /** - * @param \stdClass[] $columns * @return array{newData: \stdClass[], helperColumns: array} */ - public function __invoke(array $columns, array $existingHelperColumns): array + public function __invoke(PrepareHelperColumnConfigsPayload $payload): array { $helperColumns = []; $newData = []; - foreach ($columns as $item) { + foreach ($payload->columns as $item) { + $item = (object) $item; if (!empty($item->isOperator)) { $itemKey = '#' . uniqid('', false); $item->key = $itemKey; @@ -39,7 +39,7 @@ public function __invoke(array $columns, array $existingHelperColumns): array return [ 'newData' => $newData, - 'helperColumns' => [...$helperColumns, ...$existingHelperColumns], + 'helperColumns' => [...$helperColumns, ...$payload->existingHelperColumns], ]; } } diff --git a/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsPayload.php b/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsPayload.php new file mode 100644 index 00000000..ecc539b7 --- /dev/null +++ b/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsPayload.php @@ -0,0 +1,43 @@ +getSession(), 'opendxp_gridconfig'); + + return new static( + columns: json_decode($request->request->getString('columns'), true) ?? [], + existingHelperColumns: $bag->get('helpercolumns', []), + helperColumnsBag: $bag, + ); + } +} diff --git a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php index 9945c129..7677eb44 100644 --- a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php +++ b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php @@ -20,7 +20,6 @@ use OpenDxp\Bundle\AdminBundle\Model\GridConfig; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridColumnConfigService; use OpenDxp\Model\DataObject; -use OpenDxp\Model\User; use OpenDxp\Security\SecurityHelper; use OpenDxp\Version; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -35,16 +34,10 @@ public function __construct( private readonly GridColumnConfigService $gridColumnConfigService, ) {} - public function __invoke( - int $objectId, - ?string $classId, - ?string $context, - ?string $searchType, - array $gridConfigData, - ?array $metadata, - ): SaveDataObjectGridColumnConfigResult { + public function __invoke(SaveDataObjectGridColumnConfigPayload $payload): SaveDataObjectGridColumnConfigResult + { $adminUser = $this->userContext->getAdminUser(); - $object = DataObject::getById($objectId); + $object = DataObject::getById($payload->objectId); if (!$object) { throw new NotFoundHttpException(); } @@ -53,9 +46,12 @@ public function __invoke( throw new AccessDeniedHttpException(); } + $gridConfigData = $payload->gridConfigData; + $metadata = $payload->metadata; + $gridConfigData['opendxp_version'] = Version::getVersion(); $gridConfigData['opendxp_revision'] = Version::getRevision(); - $gridConfigData['context'] = $context; + $gridConfigData['context'] = $payload->context; unset($gridConfigData['settings']['isShared']); $gridConfigId = $metadata['gridConfigId'] ?? null; @@ -71,14 +67,14 @@ public function __invoke( $this->gridColumnConfigService->updateGridConfigShares($gridConfig, $metadata ?? [], $adminUser, adminCanEditAll: true); if (!empty($metadata['setAsFavourite']) && $adminUser->isAdmin()) { - $this->gridColumnConfigService->updateGridConfigFavourites($gridConfig, $metadata, $adminUser, $objectId); + $this->gridColumnConfigService->updateGridConfigFavourites($gridConfig, $metadata, $adminUser, $payload->objectId); } if (!$gridConfig) { $gridConfig = new GridConfig(); $gridConfig->setName(date('c')); - $gridConfig->setClassId($classId); - $gridConfig->setSearchType($searchType); + $gridConfig->setClassId($payload->classId); + $gridConfig->setSearchType($payload->searchType); $gridConfig->setOwnerId($adminUser->getId()); } @@ -93,8 +89,8 @@ public function __invoke( $gridConfig->setConfig(json_encode($gridConfigData)); $gridConfig->save(); - $availableConfigs = $this->gridColumnConfigService->getMyOwnColumnConfigs($adminUser->getId(), $classId ?? '', $searchType); - $sharedConfigs = $this->gridColumnConfigService->getSharedColumnConfigs($adminUser, $classId ?? '', $searchType); + $availableConfigs = $this->gridColumnConfigService->getMyOwnColumnConfigs($adminUser->getId(), $payload->classId ?? '', $payload->searchType); + $sharedConfigs = $this->gridColumnConfigService->getSharedColumnConfigs($adminUser, $payload->classId ?? '', $payload->searchType); $settings = $this->gridColumnConfigService->getShareSettings($gridConfig->getId()); $settings['gridConfigId'] = (int) $gridConfig->getId(); diff --git a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigPayload.php b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigPayload.php new file mode 100644 index 00000000..8f675b07 --- /dev/null +++ b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigPayload.php @@ -0,0 +1,47 @@ +request->getString('settings'); + + return new static( + objectId: $request->request->getInt('id'), + classId: $request->request->getString('class_id') ?: null, + context: $request->request->getString('context') ?: null, + searchType: $request->request->getString('searchType') ?: null, + gridConfigData: json_decode($request->request->getString('gridconfig'), true) ?? [], + metadata: $meta ? json_decode($meta, true) : null, + ); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/DeleteObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/DeleteObjectBrickHandler.php index 36692066..9117b3ed 100644 --- a/src/Handler/DataObject/ObjectBrick/DeleteObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/DeleteObjectBrickHandler.php @@ -17,13 +17,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Model\DataObject; final class DeleteObjectBrickHandler { - public function __invoke(string $id): void + public function __invoke(StringIdBodyPayload $payload): void { - $brick = DataObject\Objectbrick\Definition::getByKey($id); + $brick = DataObject\Objectbrick\Definition::getByKey($payload->id); $brick->delete(); } } diff --git a/src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickPayload.php b/src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickPayload.php new file mode 100644 index 00000000..a0d97065 --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickPayload.php @@ -0,0 +1,30 @@ +query->getString('id')); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php index f992858f..07385295 100644 --- a/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php @@ -17,18 +17,19 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrick\ExportObjectBrickPayload; use OpenDxp\Logger; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class ExportObjectBrickHandler { - public function __invoke(string $id): ExportObjectBrickResult + public function __invoke(ExportObjectBrickPayload $payload): ExportObjectBrickResult { - $objectBrick = DataObject\Objectbrick\Definition::getByKey($id); + $objectBrick = DataObject\Objectbrick\Definition::getByKey($payload->id); if (!$objectBrick instanceof DataObject\Objectbrick\Definition) { - $errorMessage = ': Object-Brick with id [ ' . $id . ' not found. ]'; + $errorMessage = ': Object-Brick with id [ ' . $payload->id . ' not found. ]'; Logger::error($errorMessage); throw new NotFoundHttpException($errorMessage); diff --git a/src/Handler/DataObject/ObjectBrick/GetBrickUsages/GetBrickUsagesPayload.php b/src/Handler/DataObject/ObjectBrick/GetBrickUsages/GetBrickUsagesPayload.php new file mode 100644 index 00000000..9d84db6c --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/GetBrickUsages/GetBrickUsagesPayload.php @@ -0,0 +1,30 @@ +query->getString('classId')); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/GetBrickUsagesHandler.php b/src/Handler/DataObject/ObjectBrick/GetBrickUsagesHandler.php index f488bcdd..a42ce6ad 100644 --- a/src/Handler/DataObject/ObjectBrick/GetBrickUsagesHandler.php +++ b/src/Handler/DataObject/ObjectBrick/GetBrickUsagesHandler.php @@ -17,13 +17,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsages\GetBrickUsagesPayload; use OpenDxp\Model\DataObject; final class GetBrickUsagesHandler { - public function __invoke(string $classId): BrickUsagesResult + public function __invoke(GetBrickUsagesPayload $payload): BrickUsagesResult { - $myClass = DataObject\ClassDefinition::getById($classId); + $myClass = DataObject\ClassDefinition::getById($payload->classId); $usages = []; foreach ((new DataObject\Objectbrick\Definition\Listing())->load() as $brickDefinition) { diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickPayload.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickPayload.php new file mode 100644 index 00000000..630d68c2 --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickPayload.php @@ -0,0 +1,30 @@ +query->getString('id')); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php index 2fcaaab1..c30948bd 100644 --- a/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php @@ -17,13 +17,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrick\GetObjectBrickPayload; use OpenDxp\Model\DataObject; final class GetObjectBrickHandler { - public function __invoke(string $id): GetObjectBrickResult + public function __invoke(GetObjectBrickPayload $payload): GetObjectBrickResult { - $brick = DataObject\Objectbrick\Definition::getByKey($id); + $brick = DataObject\Objectbrick\Definition::getByKey($payload->id); return new GetObjectBrickResult($brick->getObjectVars(), $brick->isWritable()); } diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickList/GetObjectBrickListPayload.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickList/GetObjectBrickListPayload.php new file mode 100644 index 00000000..0849790b --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickList/GetObjectBrickListPayload.php @@ -0,0 +1,40 @@ +query->getString('class_id') ?: null, + fieldName: $request->query->getString('field_name') ?: null, + layoutId: $request->query->getString('layoutId') ?: null, + objectId: $request->query->getInt('object_id'), + ); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php index 643d58dd..03409912 100644 --- a/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php @@ -18,23 +18,25 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickList\GetObjectBrickListPayload; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; -use OpenDxp\Model\User; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetObjectBrickListHandler { public function __construct( - private readonly AdminUserContextInterface $userContext,private readonly EventDispatcherInterface $eventDispatcher) {} + private readonly AdminUserContextInterface $userContext, + private readonly EventDispatcherInterface $eventDispatcher, + ) {} - public function __invoke( - ?string $classId, - ?string $fieldName, - ?string $layoutId, - int $objectId, - ): ObjectBrickListResult { + public function __invoke(GetObjectBrickListPayload $payload): ObjectBrickListResult + { + $classId = $payload->classId; + $fieldName = $payload->fieldName; + $layoutId = $payload->layoutId; + $objectId = $payload->objectId; $adminUser = $this->userContext->getAdminUser(); $list = (new DataObject\Objectbrick\Definition\Listing())->load(); diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/GetObjectBrickTreePayload.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/GetObjectBrickTreePayload.php new file mode 100644 index 00000000..20d40c72 --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/GetObjectBrickTreePayload.php @@ -0,0 +1,42 @@ +query->getString('forObjectEditor'), + objectId: $request->query->getInt('object_id'), + classId: $request->query->getString('class_id') ?: null, + fieldName: $request->query->getString('field_name') ?: null, + layoutId: $request->query->getString('layoutId') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickTreeHandler.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickTreeHandler.php index ccb1a438..a4d24f6a 100644 --- a/src/Handler/DataObject/ObjectBrick/GetObjectBrickTreeHandler.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickTreeHandler.php @@ -18,24 +18,26 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTree\GetObjectBrickTreePayload; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; -use OpenDxp\Model\User; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetObjectBrickTreeHandler { public function __construct( - private readonly AdminUserContextInterface $userContext,private readonly EventDispatcherInterface $eventDispatcher) {} - - public function __invoke( - bool $forObjectEditor, - int $objectId, - ?string $classId, - ?string $fieldName, - ?string $layoutId, - ): ObjectBrickTreeResult { + private readonly AdminUserContextInterface $userContext, + private readonly EventDispatcherInterface $eventDispatcher, + ) {} + + public function __invoke(GetObjectBrickTreePayload $payload): ObjectBrickTreeResult + { + $forObjectEditor = $payload->forObjectEditor; + $objectId = $payload->objectId; + $classId = $payload->classId; + $fieldName = $payload->fieldName; + $layoutId = $payload->layoutId; $adminUser = $this->userContext->getAdminUser(); $list = (new DataObject\Objectbrick\Definition\Listing())->load(); diff --git a/src/Handler/DataObject/ObjectBrick/ImportObjectBrick/ImportObjectBrickPayload.php b/src/Handler/DataObject/ObjectBrick/ImportObjectBrick/ImportObjectBrickPayload.php new file mode 100644 index 00000000..1856d5cc --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/ImportObjectBrick/ImportObjectBrickPayload.php @@ -0,0 +1,40 @@ +files->get('Filedata'); + + return new static( + id: $request->query->getString('id'), + json: file_get_contents($file->getPathname()), + ); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php index 1827ad00..aae47451 100644 --- a/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php @@ -17,17 +17,18 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ImportObjectBrick\ImportObjectBrickPayload; use OpenDxp\Model\DataObject; use RuntimeException; final class ImportObjectBrickHandler { - public function __invoke(string $id, string $jsonData): void + public function __invoke(ImportObjectBrickPayload $payload): void { - $objectBrick = DataObject\Objectbrick\Definition::getByKey($id); + $objectBrick = DataObject\Objectbrick\Definition::getByKey($payload->id); - if (!DataObject\ClassDefinition\Service::importObjectBrickFromJson($objectBrick, $jsonData)) { - throw new RuntimeException('Failed to import objectbrick: ' . $id); + if (!DataObject\ClassDefinition\Service::importObjectBrickFromJson($objectBrick, $payload->json)) { + throw new RuntimeException('Failed to import objectbrick: ' . $payload->id); } } } diff --git a/src/Handler/DataObject/ObjectBrick/UpdateObjectBrick/UpdateObjectBrickPayload.php b/src/Handler/DataObject/ObjectBrick/UpdateObjectBrick/UpdateObjectBrickPayload.php new file mode 100644 index 00000000..fd61f445 --- /dev/null +++ b/src/Handler/DataObject/ObjectBrick/UpdateObjectBrick/UpdateObjectBrickPayload.php @@ -0,0 +1,44 @@ +request->getString('key'), + title: $request->request->getString('title'), + group: $request->request->getString('group'), + isAdd: $request->request->getString('task') === 'add', + values: $request->request->has('values') ? (json_decode($request->request->getString('values'), true) ?? null) : null, + configuration: $request->request->has('configuration') ? (json_decode($request->request->getString('configuration'), true) ?? null) : null, + ); + } +} diff --git a/src/Handler/DataObject/ObjectBrick/UpdateObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/UpdateObjectBrickHandler.php index 7addab01..450271ce 100644 --- a/src/Handler/DataObject/ObjectBrick/UpdateObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/UpdateObjectBrickHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\UpdateObjectBrick\UpdateObjectBrickPayload; use OpenDxp\Model\DataObject; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -27,14 +28,15 @@ final class UpdateObjectBrickHandler { public function __construct(private readonly EventDispatcherInterface $eventDispatcher) {} - public function __invoke( - string $key, - string $title, - string $group, - bool $isAdd, - ?array $values, - ?array $configuration, - ): DataObject\Objectbrick\Definition { + public function __invoke(UpdateObjectBrickPayload $payload): DataObject\Objectbrick\Definition + { + $key = $payload->key; + $title = $payload->title; + $group = $payload->group; + $isAdd = $payload->isAdd; + $values = $payload->values; + $configuration = $payload->configuration; + if ($isAdd) { $list = new DataObject\Objectbrick\Definition\Listing(); foreach ($list->loadNames() as $brickName) { diff --git a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesPayload.php b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesPayload.php new file mode 100644 index 00000000..f4be0a51 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesPayload.php @@ -0,0 +1,36 @@ +query->getString('unit') ?: null, + value: $request->query->getString('value') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php index bc03446c..444329c6 100644 --- a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php +++ b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValues\ConvertAllQuantityValuesPayload; use OpenDxp\Model\DataObject\Data\QuantityValue; use OpenDxp\Model\DataObject\QuantityValue\Unit; use OpenDxp\Model\DataObject\QuantityValue\UnitConversionService; @@ -28,9 +29,9 @@ public function __construct( private readonly UnitConversionService $conversionService, ) {} - public function __invoke(?string $unitId, ?string $value): ConvertAllQuantityValuesResult + public function __invoke(ConvertAllQuantityValuesPayload $payload): ConvertAllQuantityValuesResult { - $fromUnit = Unit::getById($unitId); + $fromUnit = Unit::getById($payload->unitId); if (!$fromUnit instanceof Unit) { throw new BadRequestHttpException('Invalid unit ID provided'); } @@ -42,7 +43,7 @@ public function __invoke(?string $unitId, ?string $value): ConvertAllQuantityVal $convertedValues = []; foreach ($units->getUnits() as $targetUnit) { - $convertedValue = $this->conversionService->convert(new QuantityValue($value, $fromUnit), $targetUnit); + $convertedValue = $this->conversionService->convert(new QuantityValue($payload->value, $fromUnit), $targetUnit); $convertedValues[] = [ 'unit' => $targetUnit->getAbbreviation(), 'unitName' => $targetUnit->getLongname(), @@ -50,6 +51,6 @@ public function __invoke(?string $unitId, ?string $value): ConvertAllQuantityVal ]; } - return new ConvertAllQuantityValuesResult($value, $fromUnit->getAbbreviation(), $convertedValues); + return new ConvertAllQuantityValuesResult($payload->value, $fromUnit->getAbbreviation(), $convertedValues); } } diff --git a/src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValuePayload.php b/src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValuePayload.php new file mode 100644 index 00000000..64e56b6d --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValuePayload.php @@ -0,0 +1,38 @@ +query->getString('fromUnit') ?: null, + toUnitId: $request->query->getString('toUnit') ?: null, + value: $request->query->getString('value') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/QuantityValue/ConvertQuantityValueHandler.php b/src/Handler/DataObject/QuantityValue/ConvertQuantityValueHandler.php index 565ccc79..62712a49 100644 --- a/src/Handler/DataObject/QuantityValue/ConvertQuantityValueHandler.php +++ b/src/Handler/DataObject/QuantityValue/ConvertQuantityValueHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValue\ConvertQuantityValuePayload; use OpenDxp\Model\DataObject\Data\QuantityValue; use OpenDxp\Model\DataObject\QuantityValue\Unit; use OpenDxp\Model\DataObject\QuantityValue\UnitConversionService; @@ -28,16 +29,16 @@ public function __construct( private readonly UnitConversionService $conversionService, ) {} - public function __invoke(?string $fromUnitId, ?string $toUnitId, ?string $value): ConvertQuantityValueResult + public function __invoke(ConvertQuantityValuePayload $payload): ConvertQuantityValueResult { - $fromUnit = Unit::getById($fromUnitId); - $toUnit = Unit::getById($toUnitId); + $fromUnit = Unit::getById($payload->fromUnitId); + $toUnit = Unit::getById($payload->toUnitId); if (!$fromUnit instanceof Unit || !$toUnit instanceof Unit) { throw new BadRequestHttpException('Invalid unit IDs provided'); } - $convertedValue = $this->conversionService->convert(new QuantityValue($value, $fromUnit), $toUnit); + $convertedValue = $this->conversionService->convert(new QuantityValue($payload->value, $fromUnit), $toUnit); return new ConvertQuantityValueResult($convertedValue->getValue()); } diff --git a/src/Handler/DataObject/QuantityValue/CreateQuantityValueUnit/CreateQuantityValueUnitHandler.php b/src/Handler/DataObject/QuantityValue/CreateQuantityValueUnit/CreateQuantityValueUnitHandler.php new file mode 100644 index 00000000..bc181b44 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/CreateQuantityValueUnit/CreateQuantityValueUnitHandler.php @@ -0,0 +1,51 @@ +data; + + if (isset($data['baseunit']) && $data['baseunit'] === -1) { + $data['baseunit'] = null; + } + + $id = $data['id']; + + if (Unit::getById($id)) { + throw new BadRequestHttpException('unit with ID [' . $id . '] already exists'); + } + + if (mb_strlen($id) > 50) { + throw new BadRequestHttpException('The maximal character length for the unit ID is 50 characters, the provided ID has ' . mb_strlen($id) . ' characters.'); + } + + $unit = new Unit(); + $unit->setValues($data); + $unit->save(); + + return new ManageQuantityValueUnitResult($unit->getObjectVars()); + } +} diff --git a/src/Handler/DataObject/QuantityValue/DeleteQuantityValueUnit/DeleteQuantityValueUnitHandler.php b/src/Handler/DataObject/QuantityValue/DeleteQuantityValueUnit/DeleteQuantityValueUnitHandler.php new file mode 100644 index 00000000..7ddea0f9 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/DeleteQuantityValueUnit/DeleteQuantityValueUnitHandler.php @@ -0,0 +1,38 @@ +data['id']); + if (!$unit) { + throw new NotFoundHttpException('Unit with id ' . $payload->data['id'] . ' not found.'); + } + + $unit->delete(); + + return new ManageQuantityValueUnitResult([]); + } +} diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListPayload.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListPayload.php new file mode 100644 index 00000000..36c005ce --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListPayload.php @@ -0,0 +1,30 @@ +query->getString('filter') ?: null); + } +} diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php index 41f8c525..c34d5a89 100644 --- a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php @@ -18,14 +18,16 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; use Exception; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitList\GetQuantityValueUnitListPayload; use OpenDxp\Db; use OpenDxp\Model\DataObject\QuantityValue\Unit; use OpenDxp\Model\Translation; final class GetQuantityValueUnitListHandler { - public function __invoke(?string $filter): GetQuantityValueUnitListResult + public function __invoke(GetQuantityValueUnitListPayload $payload): GetQuantityValueUnitListResult { + $filter = $payload->filter; $list = new Unit\Listing(); $list->setOrderKey(['baseunit', 'factor', 'abbreviation']); $list->setOrder(['ASC', 'ASC', 'ASC']); diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsPayload.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsPayload.php new file mode 100644 index 00000000..91268205 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsPayload.php @@ -0,0 +1,40 @@ +query->all(), + limit: $request->query->getInt('limit', 25), + start: $request->query->getInt('start', 0), + filter: $request->query->getString('filter') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsHandler.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsHandler.php index 92f7b926..8aff26a6 100644 --- a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsHandler.php +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsHandler.php @@ -17,18 +17,19 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnits\GetQuantityValueUnitsPayload; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Db; use OpenDxp\Model\DataObject\QuantityValue\Unit; final class GetQuantityValueUnitsHandler { - public function __invoke( - array $queryAll, - int $limit, - int $start, - ?string $filter, - ): GetQuantityValueUnitsResult { + public function __invoke(GetQuantityValueUnitsPayload $payload): GetQuantityValueUnitsResult + { + $queryAll = $payload->queryAll; + $limit = $payload->limit; + $start = $payload->start; + $filter = $payload->filter; $list = new Unit\Listing(); $order = ['ASC', 'ASC', 'ASC']; diff --git a/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnits/ImportQuantityValueUnitsPayload.php b/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnits/ImportQuantityValueUnitsPayload.php new file mode 100644 index 00000000..7e436380 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnits/ImportQuantityValueUnitsPayload.php @@ -0,0 +1,34 @@ +files->get('Filedata'); + + return new static(json: file_get_contents($file->getPathname())); + } +} diff --git a/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnitsHandler.php b/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnitsHandler.php index c0a0cb03..d8b28288 100644 --- a/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnitsHandler.php +++ b/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnitsHandler.php @@ -17,14 +17,15 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnits\ImportQuantityValueUnitsPayload; use OpenDxp\Model\DataObject\QuantityValue\Service as QuantityValueService; final class ImportQuantityValueUnitsHandler { public function __construct(private readonly QuantityValueService $service) {} - public function __invoke(string $json): bool + public function __invoke(ImportQuantityValueUnitsPayload $payload): bool { - return $this->service->importDefinitionFromJson($json); + return $this->service->importDefinitionFromJson($payload->json); } } diff --git a/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitHandler.php b/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitHandler.php deleted file mode 100644 index b4fee9e8..00000000 --- a/src/Handler/DataObject/QuantityValue/ManageQuantityValueUnitHandler.php +++ /dev/null @@ -1,72 +0,0 @@ -delete(); - - return new ManageQuantityValueUnitResult([]); - } - - if ($xaction === 'update') { - $unit = Unit::getById($data['id']); - if (!$unit) { - throw new NotFoundHttpException('Unit with id ' . $data['id'] . ' not found.'); - } - if (($data['baseunit'] ?? null) == -1) { - $data['baseunit'] = null; - } - $unit->setValues($data); - $unit->save(); - - return new ManageQuantityValueUnitResult($unit->getObjectVars()); - } - - if ($xaction === 'create') { - if (isset($data['baseunit']) && $data['baseunit'] === -1) { - $data['baseunit'] = null; - } - $id = $data['id']; - if (Unit::getById($id)) { - throw new BadRequestHttpException('unit with ID [' . $id . '] already exists'); - } - if (mb_strlen($id) > 50) { - throw new BadRequestHttpException('The maximal character length for the unit ID is 50 characters, the provided ID has ' . mb_strlen($id) . ' characters.'); - } - $unit = new Unit(); - $unit->setValues($data); - $unit->save(); - - return new ManageQuantityValueUnitResult($unit->getObjectVars()); - } - - throw new BadRequestHttpException('Unknown xaction: ' . $xaction); - } -} diff --git a/src/Handler/DataObject/QuantityValue/QuantityValueUnitPayload.php b/src/Handler/DataObject/QuantityValue/QuantityValueUnitPayload.php new file mode 100644 index 00000000..4bf7c605 --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/QuantityValueUnitPayload.php @@ -0,0 +1,30 @@ +request->getString('data'), true) ?? []); + } +} diff --git a/src/Handler/DataObject/QuantityValue/UpdateQuantityValueUnit/UpdateQuantityValueUnitHandler.php b/src/Handler/DataObject/QuantityValue/UpdateQuantityValueUnit/UpdateQuantityValueUnitHandler.php new file mode 100644 index 00000000..8afb337d --- /dev/null +++ b/src/Handler/DataObject/QuantityValue/UpdateQuantityValueUnit/UpdateQuantityValueUnitHandler.php @@ -0,0 +1,45 @@ +data; + + $unit = Unit::getById($data['id']); + if (!$unit) { + throw new NotFoundHttpException('Unit with id ' . $data['id'] . ' not found.'); + } + + if (($data['baseunit'] ?? null) == -1) { + $data['baseunit'] = null; + } + + $unit->setValues($data); + $unit->save(); + + return new ManageQuantityValueUnitResult($unit->getObjectVars()); + } +} diff --git a/src/Handler/DataObject/SaveDataObjectHandler.php b/src/Handler/DataObject/SaveDataObject/SaveDataObjectHandler.php similarity index 92% rename from src/Handler/DataObject/SaveDataObjectHandler.php rename to src/Handler/DataObject/SaveDataObject/SaveDataObjectHandler.php index 4bdad46b..2d7992e6 100644 --- a/src/Handler/DataObject/SaveDataObjectHandler.php +++ b/src/Handler/DataObject/SaveDataObject/SaveDataObjectHandler.php @@ -15,10 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject; use OpenDxp\Bundle\AdminBundle\Helper\DataObjectVersionHelper; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DataObjectPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPayloadMapper; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPersistenceCoordinator; @@ -37,9 +36,9 @@ public function __construct( private readonly DataObjectPersistenceCoordinator $coordinator, ) {} - public function __invoke(int $id, DataObjectPayload $payload): SaveDataObjectResult + public function __invoke(SaveDataObjectPayload $payload): SaveDataObjectResult { - $objectFromDatabase = DataObject\Concrete::getById($id); + $objectFromDatabase = DataObject\Concrete::getById($payload->id); if (!$objectFromDatabase instanceof DataObject\Concrete) { throw new NotFoundHttpException('Could not find object'); } diff --git a/src/Payload/DataObject/DataObjectPayload.php b/src/Handler/DataObject/SaveDataObject/SaveDataObjectPayload.php similarity index 71% rename from src/Payload/DataObject/DataObjectPayload.php rename to src/Handler/DataObject/SaveDataObject/SaveDataObjectPayload.php index 9c3675a6..0c3eee6c 100644 --- a/src/Payload/DataObject/DataObjectPayload.php +++ b/src/Handler/DataObject/SaveDataObject/SaveDataObjectPayload.php @@ -15,20 +15,22 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -final class DataObjectPayload +final readonly class SaveDataObjectPayload implements ExtJsPayloadInterface { public function __construct( + public readonly int $id, public readonly string $task, public readonly array $data, public readonly array $properties, public readonly array $scheduler, ) {} - public static function fromRequest(Request $request, ?string $taskOverride = null): self + public static function fromRequest(Request $request): static { $data = $request->request->has('data') ? (json_decode($request->request->getString('data'), true) ?? []) @@ -44,11 +46,12 @@ public static function fromRequest(Request $request, ?string $taskOverride = nul $scheduler = !empty($raw) ? $raw : []; } - return new self( - task: $taskOverride ?? $request->query->getString('task'), - data: is_array($data) ? $data : [], + return new static( + id: $request->request->getInt('id'), + task: $request->query->getString('task'), + data: is_array($data) ? $data : [], properties: is_array($properties) ? $properties : [], - scheduler: $scheduler, + scheduler: $scheduler, ); } } diff --git a/src/Handler/DataObject/SaveDataObjectResult.php b/src/Handler/DataObject/SaveDataObject/SaveDataObjectResult.php similarity index 91% rename from src/Handler/DataObject/SaveDataObjectResult.php rename to src/Handler/DataObject/SaveDataObject/SaveDataObjectResult.php index 95eaa332..c6208f64 100644 --- a/src/Handler/DataObject/SaveDataObjectResult.php +++ b/src/Handler/DataObject/SaveDataObject/SaveDataObjectResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject; final readonly class SaveDataObjectResult { diff --git a/src/Handler/DataObject/SaveDataObjectFolderHandler.php b/src/Handler/DataObject/SaveDataObjectFolderHandler.php index e423a8dc..9333fb37 100644 --- a/src/Handler/DataObject/SaveDataObjectFolderHandler.php +++ b/src/Handler/DataObject/SaveDataObjectFolderHandler.php @@ -24,6 +24,7 @@ use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\SaveDataObjectFolderPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class SaveDataObjectFolderHandler @@ -32,9 +33,12 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(int $id, array $general, ?array $propertiesData): void + public function __invoke(SaveDataObjectFolderPayload $payload): void { $adminUser = $this->userContext->getAdminUser(); + $id = $payload->id; + $general = $payload->general; + $propertiesData = $payload->propertiesData; $object = DataObject::getById($id); if (!$object) { diff --git a/src/Handler/DataObject/TreeGetChildrenByIdHandler.php b/src/Handler/DataObject/TreeGetChildrenByIdHandler.php index 135e9bf4..775506e2 100644 --- a/src/Handler/DataObject/TreeGetChildrenByIdHandler.php +++ b/src/Handler/DataObject/TreeGetChildrenByIdHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\TreeGetChildrenByIdPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Db; @@ -35,15 +36,16 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke( - int $node, - ?string $filter, - int $start, - int $limit, - string $view, - int $fromPaging, - array $requestQueryAll, - ): GetDataObjectChildrenResult { + public function __invoke(TreeGetChildrenByIdPayload $payload): GetDataObjectChildrenResult + { + $node = $payload->node; + $filter = $payload->filter; + $start = $payload->start; + $limit = $payload->limit; + $view = $payload->view; + $fromPaging = $payload->fromPaging; + $requestQueryAll = $payload->allParams; + $object = DataObject::getById($node); $objectTypes = [DataObject::OBJECT_TYPE_OBJECT, DataObject::OBJECT_TYPE_FOLDER]; diff --git a/src/Handler/DataObject/UpdateDataObjectHandler.php b/src/Handler/DataObject/UpdateDataObjectHandler.php index bfd2eab3..9d8a2ea6 100644 --- a/src/Handler/DataObject/UpdateDataObjectHandler.php +++ b/src/Handler/DataObject/UpdateDataObjectHandler.php @@ -19,13 +19,13 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; +use OpenDxp\Bundle\AdminBundle\Payload\DataObject\UpdateDataObjectPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Db; use OpenDxp\Logger; use OpenDxp\Model\DataObject; use OpenDxp\Model\Element\Service; -use OpenDxp\Model\User; use RuntimeException; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -38,7 +38,17 @@ public function __construct( private readonly ElementServiceInterface $elementService, ) {} - public function __invoke(int $id, array $values): UpdateDataObjectResult + public function __invoke(UpdateDataObjectPayload $payload): UpdateDataObjectResult + { + $result = null; + foreach ($payload->ids as $id) { + $result = $this->processOne($id, $payload->values); + } + + return $result ?? new UpdateDataObjectResult(treeData: []); + } + + private function processOne(int $id, array $values): UpdateDataObjectResult { $object = DataObject::getById($id); if (!$object instanceof DataObject) { diff --git a/src/Handler/DataObject/Variants/GetVariants/GetVariantsPayload.php b/src/Handler/DataObject/Variants/GetVariants/GetVariantsPayload.php new file mode 100644 index 00000000..f4c1ea73 --- /dev/null +++ b/src/Handler/DataObject/Variants/GetVariants/GetVariantsPayload.php @@ -0,0 +1,44 @@ +request->all(), ...$request->query->all()]; + $languageFromParams = $request->request->getString('language'); + $requestedLanguage = ($languageFromParams !== '' && $languageFromParams !== 'default') + ? $languageFromParams + : $request->getLocale(); + + return new static( + objectId: $request->request->getInt('objectId'), + allParams: $allParams, + requestedLanguage: $requestedLanguage, + ); + } +} diff --git a/src/Handler/DataObject/Variants/GetVariantsHandler.php b/src/Handler/DataObject/Variants/GetVariantsHandler.php index 498effe0..d1a7afe0 100644 --- a/src/Handler/DataObject/Variants/GetVariantsHandler.php +++ b/src/Handler/DataObject/Variants/GetVariantsHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariants\GetVariantsPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -28,8 +29,12 @@ public function __construct( private readonly DataObjectGridService $dataObjectGridService, ) {} - public function __invoke(int $parentObjectId, array $allParams, string $requestedLanguage): GetVariantsResult + public function __invoke(GetVariantsPayload $payload): GetVariantsResult { + $parentObjectId = $payload->objectId; + $allParams = $payload->allParams; + $requestedLanguage = $payload->requestedLanguage; + $parentObject = DataObject\Concrete::getById($parentObjectId); if (!$parentObject) { throw new NotFoundHttpException('No Object found with id ' . $parentObjectId); diff --git a/src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyPayload.php b/src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyPayload.php new file mode 100644 index 00000000..6dec5bb5 --- /dev/null +++ b/src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyPayload.php @@ -0,0 +1,36 @@ +request->getInt('id'), + key: $request->request->getString('key'), + ); + } +} diff --git a/src/Handler/DataObject/Variants/UpdateObjectKeyHandler.php b/src/Handler/DataObject/Variants/UpdateObjectKeyHandler.php index 177dc51a..576b221c 100644 --- a/src/Handler/DataObject/Variants/UpdateObjectKeyHandler.php +++ b/src/Handler/DataObject/Variants/UpdateObjectKeyHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKey\UpdateObjectKeyPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -27,8 +28,11 @@ public function __construct( private readonly DataObjectGridService $dataObjectGridService, ) {} - public function __invoke(int $id, string $key): UpdateObjectKeyResult + public function __invoke(UpdateObjectKeyPayload $payload): UpdateObjectKeyResult { + $id = $payload->id; + $key = $payload->key; + $object = DataObject\Concrete::getById($id); if (!$object) { diff --git a/src/Handler/DataObject/Version/DiffVersionsHandler.php b/src/Handler/DataObject/Version/DiffVersions/DiffVersionsHandler.php similarity index 81% rename from src/Handler/DataObject/Version/DiffVersionsHandler.php rename to src/Handler/DataObject/Version/DiffVersions/DiffVersionsHandler.php index f0e634e0..ed9c0683 100644 --- a/src/Handler/DataObject/Version/DiffVersionsHandler.php +++ b/src/Handler/DataObject/Version/DiffVersions/DiffVersionsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\DiffVersions; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; use OpenDxp\Model\DataObject; @@ -24,16 +24,16 @@ final class DiffVersionsHandler { - public function __invoke(int $fromId, int $toId): DiffVersionsResult + public function __invoke(DiffVersionsPayload $payload): DiffVersionsResult { DataObject::setDoNotRestoreKeyAndPath(true); - $version1 = Version::getById($fromId); + $version1 = Version::getById($payload->from); $object1 = $version1?->loadData(); if (!$object1 instanceof DataObject\AbstractObject) { DataObject::setDoNotRestoreKeyAndPath(false); - throw new DataObjectNotFoundException($fromId); + throw new DataObjectNotFoundException($payload->from); } if (method_exists($object1, 'getLocalizedFields')) { @@ -42,12 +42,12 @@ public function __invoke(int $fromId, int $toId): DiffVersionsResult $localizedFields1->setLoadedAllLazyData(); } - $version2 = Version::getById($toId); + $version2 = Version::getById($payload->to); $object2 = $version2?->loadData(); if (!$object2 instanceof DataObject\AbstractObject) { DataObject::setDoNotRestoreKeyAndPath(false); - throw new DataObjectNotFoundException($toId); + throw new DataObjectNotFoundException($payload->to); } if (method_exists($object2, 'getLocalizedFields')) { @@ -59,7 +59,7 @@ public function __invoke(int $fromId, int $toId): DiffVersionsResult DataObject::setDoNotRestoreKeyAndPath(false); if (!$object1->isAllowed('versions') || !$object2->isAllowed('versions')) { - throw new AccessDeniedHttpException('Permission denied for version ids [' . $fromId . ', ' . $toId . ']'); + throw new AccessDeniedHttpException('Permission denied for version ids [' . $payload->from . ', ' . $payload->to . ']'); } return new DiffVersionsResult($object1, $version1, $object2, $version2); diff --git a/src/Handler/DataObject/Version/DiffVersions/DiffVersionsPayload.php b/src/Handler/DataObject/Version/DiffVersions/DiffVersionsPayload.php new file mode 100644 index 00000000..8509330d --- /dev/null +++ b/src/Handler/DataObject/Version/DiffVersions/DiffVersionsPayload.php @@ -0,0 +1,36 @@ +attributes->getInt('from'), + to: $request->attributes->getInt('to'), + ); + } +} diff --git a/src/Handler/DataObject/Version/DiffVersionsResult.php b/src/Handler/DataObject/Version/DiffVersions/DiffVersionsResult.php similarity index 91% rename from src/Handler/DataObject/Version/DiffVersionsResult.php rename to src/Handler/DataObject/Version/DiffVersions/DiffVersionsResult.php index ae24ef18..d12277b2 100644 --- a/src/Handler/DataObject/Version/DiffVersionsResult.php +++ b/src/Handler/DataObject/Version/DiffVersions/DiffVersionsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\DiffVersions; use OpenDxp\Model\DataObject\AbstractObject; use OpenDxp\Model\Version; diff --git a/src/Handler/DataObject/Version/PreviewVersionHandler.php b/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionHandler.php similarity index 79% rename from src/Handler/DataObject/Version/PreviewVersionHandler.php rename to src/Handler/DataObject/Version/PreviewVersion/PreviewVersionHandler.php index 598b3b02..63b6e55f 100644 --- a/src/Handler/DataObject/Version/PreviewVersionHandler.php +++ b/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionHandler.php @@ -15,25 +15,26 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PreviewVersion; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Model\DataObject; use OpenDxp\Model\Version; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; final class PreviewVersionHandler { - public function __invoke(int $versionId): PreviewVersionResult + public function __invoke(IdQueryPayload $payload): PreviewVersionResult { DataObject::setDoNotRestoreKeyAndPath(true); - $version = Version::getById($versionId); + $version = Version::getById($payload->id); $object = $version?->loadData(); if (!$object instanceof DataObject\AbstractObject) { DataObject::setDoNotRestoreKeyAndPath(false); - throw new DataObjectNotFoundException($versionId); + throw new DataObjectNotFoundException($payload->id); } if (method_exists($object, 'getLocalizedFields')) { @@ -45,7 +46,7 @@ public function __invoke(int $versionId): PreviewVersionResult DataObject::setDoNotRestoreKeyAndPath(false); if (!$object->isAllowed('versions')) { - throw new AccessDeniedHttpException('Permission denied for version id [' . $versionId . ']'); + throw new AccessDeniedHttpException('Permission denied for version id [' . $payload->id . ']'); } return new PreviewVersionResult($object, $version); diff --git a/src/Handler/DataObject/Version/PreviewVersionResult.php b/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionResult.php similarity index 90% rename from src/Handler/DataObject/Version/PreviewVersionResult.php rename to src/Handler/DataObject/Version/PreviewVersion/PreviewVersionResult.php index 924f8825..8e61903c 100644 --- a/src/Handler/DataObject/Version/PreviewVersionResult.php +++ b/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PreviewVersion; use OpenDxp\Model\DataObject\AbstractObject; use OpenDxp\Model\Version; diff --git a/src/Handler/DataObject/Version/PublishVersionHandler.php b/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php similarity index 84% rename from src/Handler/DataObject/Version/PublishVersionHandler.php rename to src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php index 4acb82ca..e478462f 100644 --- a/src/Handler/DataObject/Version/PublishVersionHandler.php +++ b/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PublishVersion; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; use OpenDxp\Model\Version; @@ -31,14 +32,14 @@ public function __construct( private readonly ElementResponseNormalizer $normalizer, ) {} - public function __invoke(int $versionId): PublishVersionResult + public function __invoke(IdBodyPayload $payload): PublishVersionResult { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $version = Version::getById($versionId); + $version = Version::getById($payload->id); $object = $version?->loadData(); if (!$object instanceof DataObject\AbstractObject) { - throw new DataObjectNotFoundException($versionId); + throw new DataObjectNotFoundException($payload->id); } $currentObject = DataObject::getById($object->getId()); diff --git a/src/Handler/DataObject/Version/PublishVersionResult.php b/src/Handler/DataObject/Version/PublishVersion/PublishVersionResult.php similarity index 88% rename from src/Handler/DataObject/Version/PublishVersionResult.php rename to src/Handler/DataObject/Version/PublishVersion/PublishVersionResult.php index 61f7c16c..0df78dcd 100644 --- a/src/Handler/DataObject/Version/PublishVersionResult.php +++ b/src/Handler/DataObject/Version/PublishVersion/PublishVersionResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PublishVersion; final readonly class PublishVersionResult { diff --git a/src/Handler/Document/AddDocumentHandler.php b/src/Handler/Document/AddDocument/AddDocumentHandler.php similarity index 81% rename from src/Handler/Document/AddDocumentHandler.php rename to src/Handler/Document/AddDocument/AddDocumentHandler.php index 61f40eed..1c096663 100644 --- a/src/Handler/Document/AddDocumentHandler.php +++ b/src/Handler/Document/AddDocument/AddDocumentHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocument; use Exception; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; @@ -37,25 +37,15 @@ public function __construct( private readonly string $defaultDocumentController, ) {} - public function __invoke( - int $parentId, - string $type, - string $key, - ?string $docTypeId, - ?string $translationsBaseDocumentId, - ?string $language, - ?string $inheritanceSource, - ?string $title, - ?string $name, - ): AddDocumentResult { + public function __invoke(AddDocumentPayload $payload): AddDocumentResult { $adminUser = $this->userContext->getAdminUser(); - $parentDocument = Document::getById($parentId); + $parentDocument = Document::getById($payload->parentId); if (!$parentDocument || !$parentDocument->isAllowed('create')) { throw new AccessDeniedHttpException('Prevented adding a document because of missing permissions'); } - $intendedPath = $parentDocument->getRealFullPath() . '/' . $key; + $intendedPath = $parentDocument->getRealFullPath() . '/' . $payload->key; if (Document\Service::pathExists($intendedPath)) { throw new BadRequestHttpException( @@ -69,47 +59,47 @@ public function __invoke( 'published' => false, ]; - $createValues['key'] = Service::getValidKey($key, 'document'); + $createValues['key'] = Service::getValidKey($payload->key, 'document'); // determine template / controller from docType or translationsBaseDocument - $docType = Document\DocType::getById($docTypeId ?? ''); + $docType = Document\DocType::getById($payload->docTypeId ?? ''); if ($docType) { $createValues['template'] = $docType->getTemplate(); $createValues['controller'] = $docType->getController(); $createValues['staticGeneratorEnabled'] = $docType->getStaticGeneratorEnabled(); - } elseif ($translationsBaseDocumentId !== null) { - $translationsBaseDocument = Document::getById((int) $translationsBaseDocumentId); + } elseif ($payload->translationsBaseDocumentId !== null) { + $translationsBaseDocument = Document::getById((int) $payload->translationsBaseDocumentId); if ($translationsBaseDocument instanceof Document\PageSnippet) { $createValues['template'] = $translationsBaseDocument->getTemplate(); $createValues['controller'] = $translationsBaseDocument->getController(); } - } elseif (in_array($type, ['page', 'snippet', 'email'])) { + } elseif (in_array($payload->type, ['page', 'snippet', 'email'])) { $createValues['controller'] = $this->defaultDocumentController; } - if ($inheritanceSource !== null) { - $createValues['contentMainDocumentId'] = $inheritanceSource; + if ($payload->inheritanceSource !== null) { + $createValues['contentMainDocumentId'] = $payload->inheritanceSource; } - $document = match ($type) { - 'page' => $this->createPage($parentDocument, $createValues, $title, $name), + $document = match ($payload->type) { + 'page' => $this->createPage($parentDocument, $createValues, $payload->title, $payload->name), 'snippet' => Document\Snippet::create($parentDocument->getId(), $createValues), 'email' => Document\Email::create($parentDocument->getId(), $createValues), 'link' => Document\Link::create($parentDocument->getId(), $createValues), 'hardlink' => Document\Hardlink::create($parentDocument->getId(), $createValues), 'folder' => $this->createFolder($parentDocument, $createValues), - default => $this->createCustomType($type, $parentDocument, $createValues), + default => $this->createCustomType($payload->type, $parentDocument, $createValues), }; // link translation if translationsBaseDocument given - if ($translationsBaseDocumentId !== null) { - $translationsBaseDocument = Document::getById((int) $translationsBaseDocumentId); + if ($payload->translationsBaseDocumentId !== null) { + $translationsBaseDocument = Document::getById((int) $payload->translationsBaseDocumentId); if ($translationsBaseDocument) { $properties = $translationsBaseDocument->getProperties(); $properties = [...$properties, ...$document->getProperties()]; $document->setProperties($properties); - $document->setProperty('language', 'text', $language, false, true); + $document->setProperty('language', 'text', $payload->language, false, true); $document->save(); $service = $this->serviceFactory->createDocumentService(); diff --git a/src/Handler/Document/AddDocument/AddDocumentPayload.php b/src/Handler/Document/AddDocument/AddDocumentPayload.php new file mode 100644 index 00000000..6e499103 --- /dev/null +++ b/src/Handler/Document/AddDocument/AddDocumentPayload.php @@ -0,0 +1,38 @@ +request->getInt('parentId'), + type: $request->request->getString('type'), + key: $request->request->getString('key'), + docTypeId: $request->request->get('docTypeId'), + translationsBaseDocumentId: $request->request->get('translationsBaseDocument'), + language: $request->request->get('language'), + inheritanceSource: $request->request->has('inheritanceSource') ? $request->request->get('inheritanceSource') : null, + title: $request->request->get('title'), + name: $request->request->get('name'), + ); + } +} diff --git a/src/Handler/Document/AddDocumentResult.php b/src/Handler/Document/AddDocument/AddDocumentResult.php similarity index 89% rename from src/Handler/Document/AddDocumentResult.php rename to src/Handler/Document/AddDocument/AddDocumentResult.php index 66772bd7..28056d40 100644 --- a/src/Handler/Document/AddDocumentResult.php +++ b/src/Handler/Document/AddDocument/AddDocumentResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocument; use OpenDxp\Model\Document; diff --git a/src/Handler/Document/ChangeMainDocumentHandler.php b/src/Handler/Document/ChangeMainDocument/ChangeMainDocumentHandler.php similarity index 72% rename from src/Handler/Document/ChangeMainDocumentHandler.php rename to src/Handler/Document/ChangeMainDocument/ChangeMainDocumentHandler.php index ba8d5ad6..82b61b25 100644 --- a/src/Handler/Document/ChangeMainDocumentHandler.php +++ b/src/Handler/Document/ChangeMainDocument/ChangeMainDocumentHandler.php @@ -15,21 +15,21 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\ChangeMainDocument; use OpenDxp\Model\Document\PageSnippet; final class ChangeMainDocumentHandler { - public function __invoke(int $id, string $contentMainDocumentPath): void + public function __invoke(ChangeMainDocumentPayload $payload): void { - $doc = PageSnippet::getById($id); + $doc = PageSnippet::getById($payload->id); if (!$doc instanceof PageSnippet) { return; } $doc->setEditables([]); - $doc->setContentMainDocumentId($contentMainDocumentPath, true); + $doc->setContentMainDocumentId($payload->contentMainDocumentPath, true); $doc->saveVersion(); } -} \ No newline at end of file +} diff --git a/src/Handler/Document/ChangeMainDocument/ChangeMainDocumentPayload.php b/src/Handler/Document/ChangeMainDocument/ChangeMainDocumentPayload.php new file mode 100644 index 00000000..605dc1b3 --- /dev/null +++ b/src/Handler/Document/ChangeMainDocument/ChangeMainDocumentPayload.php @@ -0,0 +1,36 @@ +request->getInt('id'), + contentMainDocumentPath: $request->request->getString('contentMainDocumentPath'), + ); + } +} diff --git a/src/Handler/Document/ConvertDocumentHandler.php b/src/Handler/Document/ConvertDocument/ConvertDocumentHandler.php similarity index 80% rename from src/Handler/Document/ConvertDocumentHandler.php rename to src/Handler/Document/ConvertDocument/ConvertDocumentHandler.php index bb696b55..60434bd1 100644 --- a/src/Handler/Document/ConvertDocumentHandler.php +++ b/src/Handler/Document/ConvertDocument/ConvertDocumentHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\ConvertDocument; use OpenDxp\Cache\RuntimeCache; use OpenDxp\Model\Document; @@ -24,14 +24,14 @@ final class ConvertDocumentHandler { - public function __invoke(int $id, string $type): void + public function __invoke(ConvertDocumentPayload $payload): void { - $document = Document::getById($id); + $document = Document::getById($payload->id); if (!$document) { throw new NotFoundHttpException('Document not found'); } - $class = '\\OpenDxp\\Model\\Document\\' . ucfirst($type); + $class = '\\OpenDxp\\Model\\Document\\' . ucfirst($payload->type); if (!Tool::classExists($class)) { return; } @@ -49,13 +49,13 @@ public function __invoke(int $id, string $type): void $new->setValue($name, $value); } - if ($type === 'hardlink' || $type === 'folder') { + if ($payload->type === 'hardlink' || $payload->type === 'folder') { foreach (['name', 'title', 'target', 'exclude', 'class', 'anchor', 'parameters', 'relation', 'accesskey', 'tabindex'] as $propertyName) { $new->removeProperty('navigation_' . $propertyName); } } - $new->setType($type); + $new->setType($payload->type); $new->save(); } } diff --git a/src/Handler/Document/ConvertDocument/ConvertDocumentPayload.php b/src/Handler/Document/ConvertDocument/ConvertDocumentPayload.php new file mode 100644 index 00000000..d8ca4256 --- /dev/null +++ b/src/Handler/Document/ConvertDocument/ConvertDocumentPayload.php @@ -0,0 +1,36 @@ +request->getInt('id'), + type: $request->request->getString('type'), + ); + } +} diff --git a/src/Handler/Document/Copy/CopyDocumentHandler.php b/src/Handler/Document/Copy/CopyDocument/CopyDocumentHandler.php similarity index 66% rename from src/Handler/Document/Copy/CopyDocumentHandler.php rename to src/Handler/Document/Copy/CopyDocument/CopyDocumentHandler.php index e826a27b..e2e7d08d 100644 --- a/src/Handler/Document/Copy/CopyDocumentHandler.php +++ b/src/Handler/Document/Copy/CopyDocument/CopyDocumentHandler.php @@ -15,44 +15,37 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyDocument; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Logger; use OpenDxp\Model\Document; use OpenDxp\Tool; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class CopyDocumentHandler { public function __construct( - private readonly AdminUserContextInterface $userContext,private readonly ElementServiceFactory $serviceFactory) {} - - public function __invoke( - int $sourceId, - int $targetId, - string $type, - ?int $sourceParentId, - ?int $targetParentId, - ?int $sessionParentId, - bool $enableInheritance, - bool $resetIndex, - ?string $language, - ): CopyDocumentResult { + private readonly AdminUserContextInterface $userContext, + private readonly ElementServiceFactory $serviceFactory, + ) {} + + public function __invoke(CopyDocumentPayload $payload): CopyDocumentResult + { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $source = Document::getById($sourceId); + $source = Document::getById($payload->sourceId); - if ($sourceParentId !== null && $targetParentId !== null) { - $sourceParent = Document::getById($sourceParentId) ?? throw new NotFoundHttpException('Source parent not found'); - $resolvedTargetParentId = $sessionParentId ?? $targetParentId; + if ($payload->sourceParentId !== null && $payload->targetParentId !== null) { + $sourceParent = Document::getById($payload->sourceParentId) ?? throw new NotFoundHttpException('Source parent not found'); + $resolvedTargetParentId = $payload->sessionParentId ?? $payload->targetParentId; $targetParent = Document::getById($resolvedTargetParentId) ?? throw new NotFoundHttpException('Target parent not found'); $targetPath = preg_replace('@^' . $sourceParent->getRealFullPath() . '@', $targetParent . '/', $source->getRealPath()); $target = Document::getByPath($targetPath); } else { - $target = Document::getById($targetId); + $target = Document::getById($payload->targetId); } if (!$target instanceof Document) { @@ -60,7 +53,7 @@ public function __invoke( } if (!$target->isAllowed('create')) { - Logger::error('could not execute copy/paste because of missing permissions on target [ ' . $targetId . ' ]'); + Logger::error('could not execute copy/paste because of missing permissions on target [ ' . $payload->targetId . ' ]'); throw new AccessDeniedHttpException(); } @@ -75,20 +68,20 @@ public function __invoke( $documentService = $this->serviceFactory->createDocumentService(); - if ($type === 'child') { - if ($language !== null && !Tool::isValidLanguage($language)) { - throw new BadRequestHttpException('Invalid language: ' . $language); + if ($payload->type === 'child') { + if ($payload->language !== null && !Tool::isValidLanguage($payload->language)) { + throw new BadRequestHttpException('Invalid language: ' . $payload->language); } - $newDocument = $documentService->copyAsChild($target, $source, $enableInheritance, $resetIndex, $language); + $newDocument = $documentService->copyAsChild($target, $source, $payload->enableInheritance, $payload->resetIndex, $payload->language); - return new CopyDocumentResult($sourceId, $newDocument); + return new CopyDocumentResult($payload->sourceId, $newDocument); } - if ($type === 'replace') { + if ($payload->type === 'replace') { $documentService->copyContents($target, $source); } - return new CopyDocumentResult($sourceId); + return new CopyDocumentResult($payload->sourceId); } } diff --git a/src/Handler/Document/Copy/CopyDocument/CopyDocumentPayload.php b/src/Handler/Document/Copy/CopyDocument/CopyDocumentPayload.php new file mode 100644 index 00000000..c2d90e99 --- /dev/null +++ b/src/Handler/Document/Copy/CopyDocument/CopyDocumentPayload.php @@ -0,0 +1,61 @@ +request->getString('transactionId'); + $sessionBag = Session::getSessionBag($request->getSession(), 'opendxp_copy')->get($transactionId) ?? []; + $hasTargetParentId = (bool) $request->request->getString('targetParentId'); + + return new static( + sourceId: $request->request->getInt('sourceId'), + targetId: $request->request->getInt('targetId'), + type: $request->request->getString('type'), + sourceParentId: $hasTargetParentId ? $request->request->getInt('sourceParentId') : null, + targetParentId: $hasTargetParentId ? $request->request->getInt('targetParentId') : null, + sessionParentId: !empty($sessionBag['parentId']) ? (int) $sessionBag['parentId'] : null, + enableInheritance: $request->request->getString('enableInheritance') === 'true', + resetIndex: $request->request->getString('resetIndex') === 'true', + language: $request->request->getString('language') ?: null, + transactionId: $transactionId, + saveParentId: (bool) $request->request->getString('saveParentId'), + sessionBag: $sessionBag, + ); + } +} diff --git a/src/Handler/Document/Copy/CopyDocumentResult.php b/src/Handler/Document/Copy/CopyDocument/CopyDocumentResult.php similarity index 86% rename from src/Handler/Document/Copy/CopyDocumentResult.php rename to src/Handler/Document/Copy/CopyDocument/CopyDocumentResult.php index bf3f158b..2c512ab8 100644 --- a/src/Handler/Document/Copy/CopyDocumentResult.php +++ b/src/Handler/Document/Copy/CopyDocument/CopyDocumentResult.php @@ -15,14 +15,14 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyDocument; use OpenDxp\Model\Document; final readonly class CopyDocumentResult { public function __construct( - public int $sourceId, + public int $sourceId, public ?Document $newDocument = null, ) {} } diff --git a/src/Handler/Document/Copy/GetDocumentChildIdsHandler.php b/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsHandler.php similarity index 69% rename from src/Handler/Document/Copy/GetDocumentChildIdsHandler.php rename to src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsHandler.php index d58284d3..e0fc7014 100644 --- a/src/Handler/Document/Copy/GetDocumentChildIdsHandler.php +++ b/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsHandler.php @@ -15,19 +15,19 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\GetDocumentChildIds; use OpenDxp\Bundle\AdminBundle\Exception\Document\DocumentNotFoundException; use OpenDxp\Model\Document; final class GetDocumentChildIdsHandler { - public function __invoke(int $sourceId): ChildIdsResult + public function __invoke(GetDocumentChildIdsPayload $payload): GetDocumentChildIdsResult { - $document = Document::getById($sourceId) ?? throw new DocumentNotFoundException($sourceId); + $document = Document::getById($payload->sourceId) ?? throw new DocumentNotFoundException($payload->sourceId); if (!$document->hasChildren()) { - return new ChildIdsResult([]); + return new GetDocumentChildIdsResult([]); } $list = new Document\Listing(); @@ -35,6 +35,6 @@ public function __invoke(int $sourceId): ChildIdsResult $list->setOrderKey('LENGTH(`path`)', false); $list->setOrder('ASC'); - return new ChildIdsResult($list->loadIdList()); + return new GetDocumentChildIdsResult($list->loadIdList()); } } diff --git a/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsPayload.php b/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsPayload.php new file mode 100644 index 00000000..8440e165 --- /dev/null +++ b/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsPayload.php @@ -0,0 +1,30 @@ +query->getInt('sourceId')); + } +} diff --git a/src/Handler/Document/Copy/ChildIdsResult.php b/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsResult.php similarity index 82% rename from src/Handler/Document/Copy/ChildIdsResult.php rename to src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsResult.php index 2bc3596c..04c6c5b6 100644 --- a/src/Handler/Document/Copy/ChildIdsResult.php +++ b/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsResult.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\GetDocumentChildIds; -final readonly class ChildIdsResult +final readonly class GetDocumentChildIdsResult { /** @param int[] $ids */ public function __construct( diff --git a/src/Handler/Document/Copy/RewriteDocumentIdsHandler.php b/src/Handler/Document/Copy/RewriteDocumentIds/RewriteDocumentIdsHandler.php similarity index 74% rename from src/Handler/Document/Copy/RewriteDocumentIdsHandler.php rename to src/Handler/Document/Copy/RewriteDocumentIds/RewriteDocumentIdsHandler.php index 2c93fd2b..d6122c83 100644 --- a/src/Handler/Document/Copy/RewriteDocumentIdsHandler.php +++ b/src/Handler/Document/Copy/RewriteDocumentIds/RewriteDocumentIdsHandler.php @@ -15,28 +15,26 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\RewriteDocumentIds; -use OpenDxp\Model\Document; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; +use OpenDxp\Model\Document; final class RewriteDocumentIdsHandler { - public function __construct(private readonly AdminUserContextInterface $userContext) - { - } + public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke(int $documentId, array $idMapping, bool $enableInheritance): void + public function __invoke(RewriteDocumentIdsPayload $payload): void { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $document = Document::getById($documentId); + $document = Document::getById($payload->documentId); if ($document === null) { return; } - $document = Document\Service::rewriteIds($document, ['document' => $idMapping], [ - 'enableInheritance' => $enableInheritance, + $document = Document\Service::rewriteIds($document, ['document' => $payload->idMapping], [ + 'enableInheritance' => $payload->enableInheritance, ]); $document->setUserModification($userId); $document->save(); diff --git a/src/Handler/Document/Copy/RewriteDocumentIds/RewriteDocumentIdsPayload.php b/src/Handler/Document/Copy/RewriteDocumentIds/RewriteDocumentIdsPayload.php new file mode 100644 index 00000000..7bb1f1f6 --- /dev/null +++ b/src/Handler/Document/Copy/RewriteDocumentIds/RewriteDocumentIdsPayload.php @@ -0,0 +1,52 @@ +request->getString('transactionId'); + $idStore = Session::getSessionBag($request->getSession(), 'opendxp_copy')->get($transactionId) ?? []; + + if (!array_key_exists('rewrite-stack', $idStore)) { + $idStore['rewrite-stack'] = array_values($idStore['idMapping'] ?? []); + } + + $documentId = (int) array_shift($idStore['rewrite-stack']); + + return new static( + documentId: $documentId, + idMapping: $idStore['idMapping'] ?? [], + enableInheritance: $request->request->getString('enableInheritance') === 'true', + transactionId: $transactionId, + updatedIdStore: $idStore, + ); + } +} diff --git a/src/Handler/Document/DeleteDocumentHandler.php b/src/Handler/Document/DeleteDocument/DeleteDocumentHandler.php similarity index 84% rename from src/Handler/Document/DeleteDocumentHandler.php rename to src/Handler/Document/DeleteDocument/DeleteDocumentHandler.php index da9a4b7c..b05d3546 100644 --- a/src/Handler/Document/DeleteDocumentHandler.php +++ b/src/Handler/Document/DeleteDocument/DeleteDocumentHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\DeleteDocument; use OpenDxp\Logger; use OpenDxp\Model\Document; @@ -24,10 +24,10 @@ final class DeleteDocumentHandler { - public function __invoke(string $type, int $id, int $amount): DeleteDocumentResult + public function __invoke(DeleteDocumentPayload $payload): DeleteDocumentResult { - if ($type === 'children') { - $parentDocument = Document::getById($id); + if ($payload->type === 'children') { + $parentDocument = Document::getById($payload->id); if (!$parentDocument) { throw new NotFoundHttpException('Parent document not found'); @@ -35,7 +35,7 @@ public function __invoke(string $type, int $id, int $amount): DeleteDocumentResu $list = new Document\Listing(); $list->setCondition('`path` LIKE ?', [$list->escapeLike($parentDocument->getRealFullPath()) . '/%']); - $list->setLimit($amount); + $list->setLimit($payload->amount); $list->setOrderKey('LENGTH(`path`)', false); $list->setOrder('DESC'); @@ -52,7 +52,7 @@ public function __invoke(string $type, int $id, int $amount): DeleteDocumentResu return new DeleteDocumentResult($deletedItems); } - $document = Document::getById($id); + $document = Document::getById($payload->id); if (!$document) { throw new NotFoundHttpException('Document not found'); diff --git a/src/Handler/Document/DeleteDocument/DeleteDocumentPayload.php b/src/Handler/Document/DeleteDocument/DeleteDocumentPayload.php new file mode 100644 index 00000000..8ffe1b63 --- /dev/null +++ b/src/Handler/Document/DeleteDocument/DeleteDocumentPayload.php @@ -0,0 +1,38 @@ +request->getString('type'), + id: $request->request->getInt('id'), + amount: $request->request->getInt('amount'), + ); + } +} diff --git a/src/Handler/Document/DeleteDocumentResult.php b/src/Handler/Document/DeleteDocument/DeleteDocumentResult.php similarity index 88% rename from src/Handler/Document/DeleteDocumentResult.php rename to src/Handler/Document/DeleteDocument/DeleteDocumentResult.php index b11f38a9..231dbc78 100644 --- a/src/Handler/Document/DeleteDocumentResult.php +++ b/src/Handler/Document/DeleteDocument/DeleteDocumentResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\DeleteDocument; final readonly class DeleteDocumentResult { diff --git a/src/Handler/Document/DocTypes/CreateDocType/CreateDocTypeHandler.php b/src/Handler/Document/DocTypes/CreateDocType/CreateDocTypeHandler.php new file mode 100644 index 00000000..838c1337 --- /dev/null +++ b/src/Handler/Document/DocTypes/CreateDocType/CreateDocTypeHandler.php @@ -0,0 +1,39 @@ +isWriteable()) { + throw new ConfigWriteException(); + } + $type = DocType::create(); + $type->setValues($payload->data); + $type->save(); + $responseData = $type->getObjectVars(); + $responseData['writeable'] = $type->isWriteable(); + + return new CreateDocTypeResult(data: $responseData); + } +} diff --git a/src/Handler/Document/DocTypes/CreateDocType/CreateDocTypeResult.php b/src/Handler/Document/DocTypes/CreateDocType/CreateDocTypeResult.php new file mode 100644 index 00000000..c09de389 --- /dev/null +++ b/src/Handler/Document/DocTypes/CreateDocType/CreateDocTypeResult.php @@ -0,0 +1,12 @@ +id); + if (!$type->isWriteable()) { + throw new ConfigWriteException(); + } + $type->delete(); + + return new DeleteDocTypeResult(); + } +} diff --git a/src/Handler/Document/DocTypes/DeleteDocType/DeleteDocTypeResult.php b/src/Handler/Document/DocTypes/DeleteDocType/DeleteDocTypeResult.php new file mode 100644 index 00000000..884f52b1 --- /dev/null +++ b/src/Handler/Document/DocTypes/DeleteDocType/DeleteDocTypeResult.php @@ -0,0 +1,12 @@ +request->get('data', ''), true) ?? []; + $id = (int) ($data['id'] ?? 0); + unset($data['id']); + + return new static(id: $id, data: $data); + } +} diff --git a/src/Handler/Document/GetDocTypesListHandler.php b/src/Handler/Document/DocTypes/GetDocTypesList/GetDocTypesListHandler.php similarity index 85% rename from src/Handler/Document/GetDocTypesListHandler.php rename to src/Handler/Document/DocTypes/GetDocTypesList/GetDocTypesListHandler.php index e25876bd..7656943e 100644 --- a/src/Handler/Document/GetDocTypesListHandler.php +++ b/src/Handler/Document/DocTypes/GetDocTypesList/GetDocTypesListHandler.php @@ -15,11 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\DocTypes\GetDocTypesList; -use OpenDxp\Model\Document\DocType; -use OpenDxp\Model\User; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; +use OpenDxp\Model\Document\DocType; final class GetDocTypesListHandler { @@ -27,7 +27,7 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(): GetDocTypesListResult + public function __invoke(EmptyPayload $payload): GetDocTypesListResult { $adminUser = $this->userContext->getAdminUser(); $list = new DocType\Listing(); diff --git a/src/Handler/Document/GetDocTypesListResult.php b/src/Handler/Document/DocTypes/GetDocTypesList/GetDocTypesListResult.php similarity index 89% rename from src/Handler/Document/GetDocTypesListResult.php rename to src/Handler/Document/DocTypes/GetDocTypesList/GetDocTypesListResult.php index 28a0c175..1f0b11e4 100644 --- a/src/Handler/Document/GetDocTypesListResult.php +++ b/src/Handler/Document/DocTypes/GetDocTypesList/GetDocTypesListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\DocTypes\GetDocTypesList; final readonly class GetDocTypesListResult { diff --git a/src/Handler/Document/DocTypes/UpdateDocType/UpdateDocTypeHandler.php b/src/Handler/Document/DocTypes/UpdateDocType/UpdateDocTypeHandler.php new file mode 100644 index 00000000..37944db6 --- /dev/null +++ b/src/Handler/Document/DocTypes/UpdateDocType/UpdateDocTypeHandler.php @@ -0,0 +1,39 @@ +id); + if (!$type->isWriteable()) { + throw new ConfigWriteException(); + } + $type->setValues($payload->data); + $type->save(); + $responseData = $type->getObjectVars(); + $responseData['writeable'] = $type->isWriteable(); + + return new UpdateDocTypeResult(data: $responseData); + } +} diff --git a/src/Handler/Document/DocTypes/UpdateDocType/UpdateDocTypeResult.php b/src/Handler/Document/DocTypes/UpdateDocType/UpdateDocTypeResult.php new file mode 100644 index 00000000..e7691644 --- /dev/null +++ b/src/Handler/Document/DocTypes/UpdateDocType/UpdateDocTypeResult.php @@ -0,0 +1,12 @@ +id); if (!$email) { throw new NotFoundHttpException('Email not found'); } diff --git a/src/Handler/Document/Email/GetEmailDataResult.php b/src/Handler/Document/Email/GetEmailData/GetEmailDataResult.php similarity index 91% rename from src/Handler/Document/Email/GetEmailDataResult.php rename to src/Handler/Document/Email/GetEmailData/GetEmailDataResult.php index ca567f34..d633a339 100644 --- a/src/Handler/Document/Email/GetEmailDataResult.php +++ b/src/Handler/Document/Email/GetEmailData/GetEmailDataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Email; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailData; use OpenDxp\Model\Document; use OpenDxp\Model\Version; diff --git a/src/Handler/Document/Email/SaveEmailHandler.php b/src/Handler/Document/Email/SaveEmail/SaveEmailHandler.php similarity index 88% rename from src/Handler/Document/Email/SaveEmailHandler.php rename to src/Handler/Document/Email/SaveEmail/SaveEmailHandler.php index 3013729d..f62111e5 100644 --- a/src/Handler/Document/Email/SaveEmailHandler.php +++ b/src/Handler/Document/Email/SaveEmail/SaveEmailHandler.php @@ -15,10 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Email; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Email\SaveEmail; use OpenDxp\Bundle\AdminBundle\Helper\DocumentVersionHelper; -use OpenDxp\Bundle\AdminBundle\Payload\Document\EmailPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\SaveEmail\SaveEmailPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator; @@ -35,9 +35,9 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function __invoke(int $id, EmailPayload $payload, bool $sessionAware = true): SaveEmailResult + public function __invoke(SaveEmailPayload $payload, bool $sessionAware = true): SaveEmailResult { - $email = Email::getById($id); + $email = Email::getById($payload->id); if (!$email) { throw new NotFoundHttpException('Email not found'); } diff --git a/src/Handler/Document/ManageDocTypesResult.php b/src/Handler/Document/Email/SaveEmail/SaveEmailPayload.php similarity index 71% rename from src/Handler/Document/ManageDocTypesResult.php rename to src/Handler/Document/Email/SaveEmail/SaveEmailPayload.php index a93dbfb8..087eb421 100644 --- a/src/Handler/Document/ManageDocTypesResult.php +++ b/src/Handler/Document/Email/SaveEmail/SaveEmailPayload.php @@ -15,11 +15,8 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Email\SaveEmail; -final readonly class ManageDocTypesResult -{ - public function __construct( - public array $data, - ) {} -} +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\PagePayload; + +final readonly class SaveEmailPayload extends PagePayload {} diff --git a/src/Handler/Document/Email/SaveEmailResult.php b/src/Handler/Document/Email/SaveEmail/SaveEmailResult.php similarity index 91% rename from src/Handler/Document/Email/SaveEmailResult.php rename to src/Handler/Document/Email/SaveEmail/SaveEmailResult.php index 20b595ae..935e89e9 100644 --- a/src/Handler/Document/Email/SaveEmailResult.php +++ b/src/Handler/Document/Email/SaveEmail/SaveEmailResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Email; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Email\SaveEmail; use OpenDxp\Model\Document\Email; use OpenDxp\Model\Version; diff --git a/src/Handler/Document/Folder/GetFolderDataHandler.php b/src/Handler/Document/Folder/GetFolderData/GetFolderDataHandler.php similarity index 85% rename from src/Handler/Document/Folder/GetFolderDataHandler.php rename to src/Handler/Document/Folder/GetFolderData/GetFolderDataHandler.php index 4271a13b..4d32bf6c 100644 --- a/src/Handler/Document/Folder/GetFolderDataHandler.php +++ b/src/Handler/Document/Folder/GetFolderData/GetFolderDataHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Model\Document\Folder; @@ -25,9 +25,9 @@ final class GetFolderDataHandler { public function __construct(private readonly ElementResponseNormalizer $normalizer) {} - public function __invoke(int $id): GetFolderDataResult + public function __invoke(GetFolderDataPayload $payload): GetFolderDataResult { - $folder = Folder::getById($id); + $folder = Folder::getById($payload->id); if (!$folder) { throw new NotFoundHttpException('Folder not found'); } diff --git a/src/Handler/Document/Folder/GetFolderData/GetFolderDataPayload.php b/src/Handler/Document/Folder/GetFolderData/GetFolderDataPayload.php new file mode 100644 index 00000000..882d91cc --- /dev/null +++ b/src/Handler/Document/Folder/GetFolderData/GetFolderDataPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/Document/Folder/GetFolderDataResult.php b/src/Handler/Document/Folder/GetFolderData/GetFolderDataResult.php similarity index 90% rename from src/Handler/Document/Folder/GetFolderDataResult.php rename to src/Handler/Document/Folder/GetFolderData/GetFolderDataResult.php index 5cee7cd6..bc9a5c5c 100644 --- a/src/Handler/Document/Folder/GetFolderDataResult.php +++ b/src/Handler/Document/Folder/GetFolderData/GetFolderDataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData; use OpenDxp\Model\Document\Folder; diff --git a/src/Handler/Document/Folder/SaveFolderHandler.php b/src/Handler/Document/Folder/SaveFolder/SaveFolderHandler.php similarity index 83% rename from src/Handler/Document/Folder/SaveFolderHandler.php rename to src/Handler/Document/Folder/SaveFolder/SaveFolderHandler.php index 50f0b41f..a4b1ee4a 100644 --- a/src/Handler/Document/Folder/SaveFolderHandler.php +++ b/src/Handler/Document/Folder/SaveFolder/SaveFolderHandler.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\SaveFolder; -use OpenDxp\Bundle\AdminBundle\Payload\Document\FolderPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\SaveFolder\SaveFolderPayload; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator; use OpenDxp\Model\Document\Folder; @@ -30,9 +30,9 @@ public function __construct( private readonly DocumentPersistenceCoordinator $coordinator, ) {} - public function __invoke(int $id, FolderPayload $payload): SaveFolderResult + public function __invoke(SaveFolderPayload $payload): SaveFolderResult { - $folder = Folder::getById($id); + $folder = Folder::getById($payload->id); if (!$folder) { throw new NotFoundHttpException('Folder not found'); } diff --git a/src/Payload/Document/FolderPayload.php b/src/Handler/Document/Folder/SaveFolder/SaveFolderPayload.php similarity index 68% rename from src/Payload/Document/FolderPayload.php rename to src/Handler/Document/Folder/SaveFolder/SaveFolderPayload.php index 74162938..f8e41396 100644 --- a/src/Payload/Document/FolderPayload.php +++ b/src/Handler/Document/Folder/SaveFolder/SaveFolderPayload.php @@ -15,19 +15,22 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\SaveFolder; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -final class FolderPayload +final readonly class SaveFolderPayload implements ExtJsPayloadInterface { public function __construct( + public readonly int $id, public readonly ?array $properties, ) {} - public static function fromRequest(Request $request): self + public static function fromRequest(Request $request): static { - return new self( + return new static( + id: $request->request->getInt('id'), properties: $request->request->has('properties') ? (json_decode($request->request->getString('properties'), true) ?? null) : null, diff --git a/src/Handler/Document/Folder/SaveFolderResult.php b/src/Handler/Document/Folder/SaveFolder/SaveFolderResult.php similarity index 90% rename from src/Handler/Document/Folder/SaveFolderResult.php rename to src/Handler/Document/Folder/SaveFolder/SaveFolderResult.php index c699be9b..99ce133e 100644 --- a/src/Handler/Document/Folder/SaveFolderResult.php +++ b/src/Handler/Document/Folder/SaveFolder/SaveFolderResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\SaveFolder; use OpenDxp\Model\Document\Folder; diff --git a/src/Handler/Document/GetDocTypesByTypeHandler.php b/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeHandler.php similarity index 86% rename from src/Handler/Document/GetDocTypesByTypeHandler.php rename to src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeHandler.php index 6a556e50..40f5012d 100644 --- a/src/Handler/Document/GetDocTypesByTypeHandler.php +++ b/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocTypesByType; use OpenDxp\Model\Document; use OpenDxp\Model\Document\DocType; @@ -23,10 +23,10 @@ final class GetDocTypesByTypeHandler { - public function __invoke(?string $type): GetDocTypesByTypeResult + public function __invoke(GetDocTypesByTypePayload $payload): GetDocTypesByTypeResult { $list = new DocType\Listing(); - if ($type) { + if ($payload->type) { if (!Document\Service::isValidType($type)) { throw new BadRequestHttpException('Invalid type: ' . $type); } diff --git a/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypePayload.php b/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypePayload.php new file mode 100644 index 00000000..85709e11 --- /dev/null +++ b/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypePayload.php @@ -0,0 +1,34 @@ +query->get('type'), + ); + } +} diff --git a/src/Handler/Document/GetDocTypesByTypeResult.php b/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeResult.php similarity index 89% rename from src/Handler/Document/GetDocTypesByTypeResult.php rename to src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeResult.php index 9a2e3073..2e0cd1d5 100644 --- a/src/Handler/Document/GetDocTypesByTypeResult.php +++ b/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocTypesByType; final readonly class GetDocTypesByTypeResult { diff --git a/src/Handler/Document/GetDocumentDataHandler.php b/src/Handler/Document/GetDocumentData/GetDocumentDataHandler.php similarity index 88% rename from src/Handler/Document/GetDocumentDataHandler.php rename to src/Handler/Document/GetDocumentData/GetDocumentDataHandler.php index 6f458fbd..8da9025e 100644 --- a/src/Handler/Document/GetDocumentDataHandler.php +++ b/src/Handler/Document/GetDocumentData/GetDocumentDataHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Exception\Document\DocumentNotFoundException; @@ -33,11 +33,11 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke(int $id): GetDocumentDataResult + public function __invoke(GetDocumentDataPayload $payload): GetDocumentDataResult { - $document = Document::getById($id); + $document = Document::getById($payload->id); if (!$document instanceof Document) { - throw new DocumentNotFoundException($id); + throw new DocumentNotFoundException($payload->id); } if (!$document->isAllowed('view')) { diff --git a/src/Handler/Document/GetDocumentData/GetDocumentDataPayload.php b/src/Handler/Document/GetDocumentData/GetDocumentDataPayload.php new file mode 100644 index 00000000..14b5d0cc --- /dev/null +++ b/src/Handler/Document/GetDocumentData/GetDocumentDataPayload.php @@ -0,0 +1,34 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/Document/GetDocumentDataResult.php b/src/Handler/Document/GetDocumentData/GetDocumentDataResult.php similarity index 88% rename from src/Handler/Document/GetDocumentDataResult.php rename to src/Handler/Document/GetDocumentData/GetDocumentDataResult.php index df3bd7be..ed5ac4b4 100644 --- a/src/Handler/Document/GetDocumentDataResult.php +++ b/src/Handler/Document/GetDocumentData/GetDocumentDataResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData; final readonly class GetDocumentDataResult { diff --git a/src/Handler/Document/GetDocumentIdForPathHandler.php b/src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathHandler.php similarity index 76% rename from src/Handler/Document/GetDocumentIdForPathHandler.php rename to src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathHandler.php index a3e04fe1..564fefb8 100644 --- a/src/Handler/Document/GetDocumentIdForPathHandler.php +++ b/src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathHandler.php @@ -15,15 +15,15 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentIdForPath; use OpenDxp\Model\Document; final class GetDocumentIdForPathHandler { - public function __invoke(?string $path): ?GetDocumentIdForPathResult + public function __invoke(GetDocumentIdForPathPayload $payload): ?GetDocumentIdForPathResult { - $document = Document::getByPath($path); + $document = Document::getByPath($payload->path); if (!$document) { return null; } diff --git a/src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathPayload.php b/src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathPayload.php new file mode 100644 index 00000000..a76fe167 --- /dev/null +++ b/src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathPayload.php @@ -0,0 +1,34 @@ +query->has('path') ? $request->query->getString('path') : null, + ); + } +} diff --git a/src/Handler/Document/GetDocumentIdForPathResult.php b/src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathResult.php similarity index 89% rename from src/Handler/Document/GetDocumentIdForPathResult.php rename to src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathResult.php index 1896f870..dcb34ea1 100644 --- a/src/Handler/Document/GetDocumentIdForPathResult.php +++ b/src/Handler/Document/GetDocumentIdForPath/GetDocumentIdForPathResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentIdForPath; final readonly class GetDocumentIdForPathResult { diff --git a/src/Handler/Document/Hardlink/GetHardlinkDataHandler.php b/src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataHandler.php similarity index 88% rename from src/Handler/Document/Hardlink/GetHardlinkDataHandler.php rename to src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataHandler.php index 899958a9..bf97b3bb 100644 --- a/src/Handler/Document/Hardlink/GetHardlinkDataHandler.php +++ b/src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkData; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; use OpenDxp\Model\Document\Hardlink; use OpenDxp\Model\Schedule\Task; @@ -31,9 +32,9 @@ public function __construct( private readonly ElementResponseNormalizer $normalizer, ) {} - public function __invoke(int $id): GetHardlinkDataResult + public function __invoke(IdQueryPayload $payload): GetHardlinkDataResult { - $link = Hardlink::getById($id); + $link = Hardlink::getById($payload->id); if (!$link) { throw new NotFoundHttpException('Hardlink not found'); } diff --git a/src/Handler/Document/Hardlink/GetHardlinkDataResult.php b/src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataResult.php similarity index 91% rename from src/Handler/Document/Hardlink/GetHardlinkDataResult.php rename to src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataResult.php index 7d25d7de..37e60c67 100644 --- a/src/Handler/Document/Hardlink/GetHardlinkDataResult.php +++ b/src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkData; use OpenDxp\Model\Document\Hardlink; diff --git a/src/Handler/Document/Hardlink/SaveHardlinkHandler.php b/src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkHandler.php similarity index 82% rename from src/Handler/Document/Hardlink/SaveHardlinkHandler.php rename to src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkHandler.php index 40570eb3..99ffb260 100644 --- a/src/Handler/Document/Hardlink/SaveHardlinkHandler.php +++ b/src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkHandler.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\SaveHardlink; -use OpenDxp\Bundle\AdminBundle\Payload\Document\HardlinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\SaveHardlink\SaveHardlinkPayload; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator; use OpenDxp\Model\Document\Hardlink; @@ -30,9 +30,9 @@ public function __construct( private readonly DocumentPersistenceCoordinator $coordinator, ) {} - public function __invoke(int $id, HardlinkPayload $payload): SaveHardlinkResult + public function __invoke(SaveHardlinkPayload $payload): SaveHardlinkResult { - $link = Hardlink::getById($id); + $link = Hardlink::getById($payload->id); if (!$link) { throw new NotFoundHttpException('Hardlink not found'); } diff --git a/src/Payload/Document/HardlinkPayload.php b/src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkPayload.php similarity index 77% rename from src/Payload/Document/HardlinkPayload.php rename to src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkPayload.php index 4bec4653..53554df9 100644 --- a/src/Payload/Document/HardlinkPayload.php +++ b/src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkPayload.php @@ -15,22 +15,25 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\SaveHardlink; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -final class HardlinkPayload +final readonly class SaveHardlinkPayload implements ExtJsPayloadInterface { public function __construct( + public readonly int $id, public readonly string $task, public readonly ?array $data, public readonly ?array $properties, public readonly ?array $scheduler, ) {} - public static function fromRequest(Request $request): self + public static function fromRequest(Request $request): static { - return new self( + return new static( + id: $request->request->getInt('id'), task: strtolower($request->query->getString('task')), data: $request->request->has('data') ? (json_decode($request->request->getString('data'), true) ?? null) diff --git a/src/Handler/Document/Hardlink/SaveHardlinkResult.php b/src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkResult.php similarity index 90% rename from src/Handler/Document/Hardlink/SaveHardlinkResult.php rename to src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkResult.php index c80b74bb..1c73721a 100644 --- a/src/Handler/Document/Hardlink/SaveHardlinkResult.php +++ b/src/Handler/Document/Hardlink/SaveHardlink/SaveHardlinkResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\SaveHardlink; use OpenDxp\Model\Document\Hardlink; diff --git a/src/Handler/Document/Link/GetLinkDataHandler.php b/src/Handler/Document/Link/GetLinkData/GetLinkDataHandler.php similarity index 89% rename from src/Handler/Document/Link/GetLinkDataHandler.php rename to src/Handler/Document/Link/GetLinkData/GetLinkDataHandler.php index dfc5477e..b235d9d6 100644 --- a/src/Handler/Document/Link/GetLinkDataHandler.php +++ b/src/Handler/Document/Link/GetLinkData/GetLinkDataHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkData; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; use OpenDxp\Model\Document\Link; use OpenDxp\Model\Schedule\Task; @@ -33,9 +34,9 @@ public function __construct( private readonly ElementResponseNormalizer $normalizer, ) {} - public function __invoke(int $id): GetLinkDataResult + public function __invoke(IdQueryPayload $payload): GetLinkDataResult { - $link = Link::getById($id); + $link = Link::getById($payload->id); if (!$link) { throw new NotFoundHttpException('Link not found'); } diff --git a/src/Handler/Document/Link/GetLinkDataResult.php b/src/Handler/Document/Link/GetLinkData/GetLinkDataResult.php similarity index 92% rename from src/Handler/Document/Link/GetLinkDataResult.php rename to src/Handler/Document/Link/GetLinkData/GetLinkDataResult.php index 1517eca6..e9c8e1c1 100644 --- a/src/Handler/Document/Link/GetLinkDataResult.php +++ b/src/Handler/Document/Link/GetLinkData/GetLinkDataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkData; use OpenDxp\Model\Document\Link; diff --git a/src/Handler/Document/Link/SaveLinkHandler.php b/src/Handler/Document/Link/SaveLink/SaveLinkHandler.php similarity index 84% rename from src/Handler/Document/Link/SaveLinkHandler.php rename to src/Handler/Document/Link/SaveLink/SaveLinkHandler.php index 16ccc0cc..85307df0 100644 --- a/src/Handler/Document/Link/SaveLinkHandler.php +++ b/src/Handler/Document/Link/SaveLink/SaveLinkHandler.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link\SaveLink; -use OpenDxp\Bundle\AdminBundle\Payload\Document\LinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\SaveLink\SaveLinkPayload; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator; use OpenDxp\Model\Document\Link; @@ -30,9 +30,9 @@ public function __construct( private readonly DocumentPersistenceCoordinator $coordinator, ) {} - public function __invoke(int $id, LinkPayload $payload): SaveLinkResult + public function __invoke(SaveLinkPayload $payload): SaveLinkResult { - $link = Link::getById($id); + $link = Link::getById($payload->id); if (!$link) { throw new NotFoundHttpException('Link not found'); } diff --git a/src/Payload/Document/LinkPayload.php b/src/Handler/Document/Link/SaveLink/SaveLinkPayload.php similarity index 78% rename from src/Payload/Document/LinkPayload.php rename to src/Handler/Document/Link/SaveLink/SaveLinkPayload.php index 2635dca7..6a1b5be7 100644 --- a/src/Payload/Document/LinkPayload.php +++ b/src/Handler/Document/Link/SaveLink/SaveLinkPayload.php @@ -15,22 +15,25 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link\SaveLink; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -final class LinkPayload +final readonly class SaveLinkPayload implements ExtJsPayloadInterface { public function __construct( + public readonly int $id, public readonly string $task, public readonly ?array $data, public readonly ?array $properties, public readonly ?array $scheduler, ) {} - public static function fromRequest(Request $request): self + public static function fromRequest(Request $request): static { - return new self( + return new static( + id: $request->request->getInt('id'), task: strtolower($request->query->getString('task')), data: $request->request->has('data') ? (json_decode($request->request->getString('data'), true) ?? null) diff --git a/src/Handler/Document/Link/SaveLinkResult.php b/src/Handler/Document/Link/SaveLink/SaveLinkResult.php similarity index 91% rename from src/Handler/Document/Link/SaveLinkResult.php rename to src/Handler/Document/Link/SaveLink/SaveLinkResult.php index 9abc55fb..9e004f06 100644 --- a/src/Handler/Document/Link/SaveLinkResult.php +++ b/src/Handler/Document/Link/SaveLink/SaveLinkResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link\SaveLink; use OpenDxp\Model\Document\Link; diff --git a/src/Handler/Document/ManageDocTypesHandler.php b/src/Handler/Document/ManageDocTypesHandler.php deleted file mode 100644 index 7c4b6d6f..00000000 --- a/src/Handler/Document/ManageDocTypesHandler.php +++ /dev/null @@ -1,67 +0,0 @@ -isWriteable()) { - throw new ConfigWriteException(); - } - $type->delete(); - - return new ManageDocTypesResult(data: []); - } - - if ($xaction === 'update') { - $type = DocType::getById($data['id']); - if (!$type->isWriteable()) { - throw new ConfigWriteException(); - } - $type->setValues($data); - $type->save(); - $responseData = $type->getObjectVars(); - $responseData['writeable'] = $type->isWriteable(); - - return new ManageDocTypesResult(data: $responseData); - } - - if ($xaction === 'create') { - if (!(new DocType())->isWriteable()) { - throw new ConfigWriteException(); - } - unset($data['id']); - $type = DocType::create(); - $type->setValues($data); - $type->save(); - $responseData = $type->getObjectVars(); - $responseData['writeable'] = $type->isWriteable(); - - return new ManageDocTypesResult(data: $responseData); - } - - throw new BadRequestHttpException('Unknown xaction: ' . $xaction); - } -} diff --git a/src/Handler/Document/Page/CheckPrettyUrlHandler.php b/src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlHandler.php similarity index 85% rename from src/Handler/Document/Page/CheckPrettyUrlHandler.php rename to src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlHandler.php index 49f51d70..e045312b 100644 --- a/src/Handler/Document/Page/CheckPrettyUrlHandler.php +++ b/src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlHandler.php @@ -15,17 +15,18 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\CheckPrettyUrl; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\CheckPrettyUrl\CheckPrettyUrlPayload; use OpenDxp\Model\Document; use OpenDxp\Model\Element; use OpenDxp\Tool\Frontend; final class CheckPrettyUrlHandler { - public function __invoke(int $docId, string $path): CheckPrettyUrlResult + public function __invoke(CheckPrettyUrlPayload $payload): CheckPrettyUrlResult { - $path = rtrim($path, '/'); + $path = rtrim($payload->path, '/'); if ($path === '') { return new CheckPrettyUrlResult(success: true, messages: []); @@ -54,11 +55,11 @@ public function __invoke(int $docId, string $path): CheckPrettyUrlResult $list = new Document\Listing(); $list->setCondition('(CONCAT(`path`, `key`) = ? OR id IN (SELECT id from documents_page WHERE prettyUrl = ?)) AND id != ?', [ - $path, $path, $docId, + $path, $path, $payload->id, ]); if ($list->getTotalCount() > 0) { - $checkDocument = Document::getById($docId); + $checkDocument = Document::getById($payload->id); $checkSite = Frontend::getSiteForDocument($checkDocument); $checkSiteId = empty($checkSite) ? 0 : $checkSite->getId(); diff --git a/src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlPayload.php b/src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlPayload.php new file mode 100644 index 00000000..ecad17cf --- /dev/null +++ b/src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlPayload.php @@ -0,0 +1,37 @@ +request->getInt('id'), + path: trim($request->request->get('path', '')), + ); + } +} diff --git a/src/Handler/Document/Page/CheckPrettyUrlResult.php b/src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlResult.php similarity index 90% rename from src/Handler/Document/Page/CheckPrettyUrlResult.php rename to src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlResult.php index 86796f0b..9d1ca77e 100644 --- a/src/Handler/Document/Page/CheckPrettyUrlResult.php +++ b/src/Handler/Document/Page/CheckPrettyUrl/CheckPrettyUrlResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\CheckPrettyUrl; final readonly class CheckPrettyUrlResult { diff --git a/src/Handler/Document/Page/GeneratePagePreviewsHandler.php b/src/Handler/Document/Page/GeneratePagePreviews/GeneratePagePreviewsHandler.php similarity index 85% rename from src/Handler/Document/Page/GeneratePagePreviewsHandler.php rename to src/Handler/Document/Page/GeneratePagePreviews/GeneratePagePreviewsHandler.php index 8408f316..f5f36e1f 100644 --- a/src/Handler/Document/Page/GeneratePagePreviewsHandler.php +++ b/src/Handler/Document/Page/GeneratePagePreviews/GeneratePagePreviewsHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GeneratePagePreviews; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Messenger\GeneratePagePreviewMessage; use OpenDxp\Model\Document; use Symfony\Component\Messenger\MessageBusInterface; @@ -27,7 +28,7 @@ public function __construct( private readonly MessageBusInterface $messengerBusOpendxpCore, ) {} - public function __invoke(): void + public function __invoke(EmptyPayload $payload): void { $list = new Document\Listing(); $list->setCondition('`type` = ?', ['page']); diff --git a/src/Handler/Document/Page/GenerateQrCodeHandler.php b/src/Handler/Document/Page/GenerateQrCode/GenerateQrCodeHandler.php similarity index 76% rename from src/Handler/Document/Page/GenerateQrCodeHandler.php rename to src/Handler/Document/Page/GenerateQrCode/GenerateQrCodeHandler.php index 73dc7d9a..1051e305 100644 --- a/src/Handler/Document/Page/GenerateQrCodeHandler.php +++ b/src/Handler/Document/Page/GenerateQrCode/GenerateQrCodeHandler.php @@ -15,18 +15,19 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GenerateQrCode; use Endroid\QrCode\Builder\Builder; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GenerateQrCode\GenerateQrCodePayload; use Endroid\QrCode\Writer\PngWriter; use OpenDxp\Model\Document; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class GenerateQrCodeHandler { - public function __invoke(int $id, bool $download): string + public function __invoke(GenerateQrCodePayload $payload): string { - $page = Document\Page::getById($id); + $page = Document\Page::getById($payload->id); if (!$page) { throw new NotFoundHttpException('Page not found'); @@ -37,7 +38,7 @@ public function __invoke(int $id, bool $download): string $result = Builder::create() ->writer(new PngWriter()) ->data($url) - ->size($download ? 4000 : 500) + ->size($payload->download ? 4000 : 500) ->build(); $tmpFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/qr-code-' . uniqid('', false) . '.png'; diff --git a/src/Handler/Document/Page/GenerateQrCode/GenerateQrCodePayload.php b/src/Handler/Document/Page/GenerateQrCode/GenerateQrCodePayload.php new file mode 100644 index 00000000..9d67f6ba --- /dev/null +++ b/src/Handler/Document/Page/GenerateQrCode/GenerateQrCodePayload.php @@ -0,0 +1,37 @@ +query->getInt('id'), + download: (bool) $request->query->get('download'), + ); + } +} diff --git a/src/Handler/Document/Page/GetPageDataHandler.php b/src/Handler/Document/Page/GetPageData/GetPageDataHandler.php similarity index 91% rename from src/Handler/Document/Page/GetPageDataHandler.php rename to src/Handler/Document/Page/GetPageData/GetPageDataHandler.php index 70e82297..c4512df3 100644 --- a/src/Handler/Document/Page/GetPageDataHandler.php +++ b/src/Handler/Document/Page/GetPageData/GetPageDataHandler.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Helper\DocumentVersionHelper; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; use OpenDxp\Document\StaticPageGenerator; @@ -37,9 +38,9 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function __invoke(int $id): GetPageDataResult + public function __invoke(GetPageDataPayload $payload): GetPageDataResult { - $page = Document\Page::getById($id); + $page = Document\Page::getById($payload->id); if (!$page) { throw new NotFoundHttpException('Page not found'); } diff --git a/src/Handler/Document/Page/GetPageData/GetPageDataPayload.php b/src/Handler/Document/Page/GetPageData/GetPageDataPayload.php new file mode 100644 index 00000000..7f8ef6e9 --- /dev/null +++ b/src/Handler/Document/Page/GetPageData/GetPageDataPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/Document/Page/GetPageDataResult.php b/src/Handler/Document/Page/GetPageData/GetPageDataResult.php similarity index 91% rename from src/Handler/Document/Page/GetPageDataResult.php rename to src/Handler/Document/Page/GetPageData/GetPageDataResult.php index 6fef999d..d7d37360 100644 --- a/src/Handler/Document/Page/GetPageDataResult.php +++ b/src/Handler/Document/Page/GetPageData/GetPageDataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData; use OpenDxp\Model\Document; use OpenDxp\Model\Version; diff --git a/src/Handler/Document/Page/GetPagePreviewImagePathHandler.php b/src/Handler/Document/Page/GetPagePreviewImagePath/GetPagePreviewImagePathHandler.php similarity index 72% rename from src/Handler/Document/Page/GetPagePreviewImagePathHandler.php rename to src/Handler/Document/Page/GetPagePreviewImagePath/GetPagePreviewImagePathHandler.php index 38cdeb02..178bf33e 100644 --- a/src/Handler/Document/Page/GetPagePreviewImagePathHandler.php +++ b/src/Handler/Document/Page/GetPagePreviewImagePath/GetPagePreviewImagePathHandler.php @@ -15,16 +15,17 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPagePreviewImagePath; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPagePreviewImagePath\GetPagePreviewImagePathPayload; use OpenDxp\Model\Document; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class GetPagePreviewImagePathHandler { - public function __invoke(int $id): string + public function __invoke(GetPagePreviewImagePathPayload $payload): string { - $document = Document\Page::getById($id); + $document = Document\Page::getById($payload->id); if (!$document instanceof Document\Page) { throw new NotFoundHttpException('Page not found'); } diff --git a/src/Handler/Document/Page/GetPagePreviewImagePath/GetPagePreviewImagePathPayload.php b/src/Handler/Document/Page/GetPagePreviewImagePath/GetPagePreviewImagePathPayload.php new file mode 100644 index 00000000..eca33487 --- /dev/null +++ b/src/Handler/Document/Page/GetPagePreviewImagePath/GetPagePreviewImagePathPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Payload/Document/PagePayload.php b/src/Handler/Document/Page/PagePayload.php similarity index 88% rename from src/Payload/Document/PagePayload.php rename to src/Handler/Document/Page/PagePayload.php index de4df4bb..ee975c7c 100644 --- a/src/Payload/Document/PagePayload.php +++ b/src/Handler/Document/Page/PagePayload.php @@ -15,13 +15,15 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -class PagePayload +readonly class PagePayload implements ExtJsPayloadInterface { public function __construct( + public readonly int $id, public readonly string $task, public readonly ?array $settings, public readonly ?array $editables, @@ -34,6 +36,7 @@ public function __construct( public static function fromRequest(Request $request): static { return new static( + id: $request->request->getInt('id'), task: strtolower($request->query->getString('task')), settings: $request->request->has('settings') ? (json_decode($request->request->getString('settings'), true) ?? null) diff --git a/src/Handler/Document/Page/RenderAreabrickIndexEditmodeHandler.php b/src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodeHandler.php similarity index 91% rename from src/Handler/Document/Page/RenderAreabrickIndexEditmodeHandler.php rename to src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodeHandler.php index 0d8d015a..3b7947a3 100644 --- a/src/Handler/Document/Page/RenderAreabrickIndexEditmodeHandler.php +++ b/src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodeHandler.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\RenderAreabrickIndexEditmode; -use OpenDxp\Bundle\AdminBundle\Payload\Document\RenderAreabrickIndexEditmodePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\RenderAreabrickIndexEditmode\RenderAreabrickIndexEditmodePayload; use OpenDxp\Document\Editable\Block\BlockStateStack; use OpenDxp\Document\Editable\EditmodeEditableDefinitionCollector; use OpenDxp\Localization\LocaleServiceInterface; diff --git a/src/Payload/Document/RenderAreabrickIndexEditmodePayload.php b/src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodePayload.php similarity index 85% rename from src/Payload/Document/RenderAreabrickIndexEditmodePayload.php rename to src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodePayload.php index de05db5c..1a67035d 100644 --- a/src/Payload/Document/RenderAreabrickIndexEditmodePayload.php +++ b/src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodePayload.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\RenderAreabrickIndexEditmode; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -class RenderAreabrickIndexEditmodePayload +final readonly class RenderAreabrickIndexEditmodePayload implements ExtJsPayloadInterface { public function __construct( public readonly int $documentId, @@ -41,4 +42,4 @@ public static function fromRequest(Request $request): static index: (int) $request->request->get('index'), ); } -} \ No newline at end of file +} diff --git a/src/Handler/Document/Page/RenderAreabrickIndexEditmodeResult.php b/src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodeResult.php similarity index 89% rename from src/Handler/Document/Page/RenderAreabrickIndexEditmodeResult.php rename to src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodeResult.php index 9a452945..fce9c458 100644 --- a/src/Handler/Document/Page/RenderAreabrickIndexEditmodeResult.php +++ b/src/Handler/Document/Page/RenderAreabrickIndexEditmode/RenderAreabrickIndexEditmodeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\RenderAreabrickIndexEditmode; use OpenDxp\Model\Document; diff --git a/src/Handler/Document/Page/ResetEditablesSessionHandler.php b/src/Handler/Document/Page/ResetEditablesSession/ResetEditablesSessionHandler.php similarity index 81% rename from src/Handler/Document/Page/ResetEditablesSessionHandler.php rename to src/Handler/Document/Page/ResetEditablesSession/ResetEditablesSessionHandler.php index d626b9cb..5a530523 100644 --- a/src/Handler/Document/Page/ResetEditablesSessionHandler.php +++ b/src/Handler/Document/Page/ResetEditablesSession/ResetEditablesSessionHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\ResetEditablesSession; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\ResetEditablesSession\ResetEditablesSessionPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Model\Document; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -25,9 +26,9 @@ final class ResetEditablesSessionHandler { public function __construct(private readonly SessionService $sessionService) {} - public function __invoke(int $docId): void + public function __invoke(ResetEditablesSessionPayload $payload): void { - $doc = Document\PageSnippet::getById($docId); + $doc = Document\PageSnippet::getById($payload->id); if (!$doc) { throw new NotFoundHttpException('Document not found'); diff --git a/src/Handler/Document/Page/ResetEditablesSession/ResetEditablesSessionPayload.php b/src/Handler/Document/Page/ResetEditablesSession/ResetEditablesSessionPayload.php new file mode 100644 index 00000000..6aaf5fcf --- /dev/null +++ b/src/Handler/Document/Page/ResetEditablesSession/ResetEditablesSessionPayload.php @@ -0,0 +1,35 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/Document/Page/SavePageHandler.php b/src/Handler/Document/Page/SavePage/SavePageHandler.php similarity index 90% rename from src/Handler/Document/Page/SavePageHandler.php rename to src/Handler/Document/Page/SavePage/SavePageHandler.php index cf989073..95809982 100644 --- a/src/Handler/Document/Page/SavePageHandler.php +++ b/src/Handler/Document/Page/SavePage/SavePageHandler.php @@ -15,10 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\SavePage; use OpenDxp\Bundle\AdminBundle\Helper\DocumentVersionHelper; -use OpenDxp\Bundle\AdminBundle\Payload\Document\PagePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\SavePage\SavePagePayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator; @@ -40,9 +40,9 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function __invoke(int $id, PagePayload $payload, bool $sessionAware = true): SavePageResult + public function __invoke(SavePagePayload $payload, bool $sessionAware = true): SavePageResult { - $oldPage = Page::getById($id); + $oldPage = Page::getById($payload->id); if (!$oldPage) { throw new NotFoundHttpException('Page not found'); } diff --git a/src/Payload/Document/SnippetPayload.php b/src/Handler/Document/Page/SavePage/SavePagePayload.php similarity index 72% rename from src/Payload/Document/SnippetPayload.php rename to src/Handler/Document/Page/SavePage/SavePagePayload.php index 67bdf2a3..ce59ac65 100644 --- a/src/Payload/Document/SnippetPayload.php +++ b/src/Handler/Document/Page/SavePage/SavePagePayload.php @@ -15,6 +15,8 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\SavePage; -final class SnippetPayload extends PagePayload {} +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\PagePayload; + +final readonly class SavePagePayload extends PagePayload {} diff --git a/src/Handler/Document/Page/SavePageResult.php b/src/Handler/Document/Page/SavePage/SavePageResult.php similarity index 92% rename from src/Handler/Document/Page/SavePageResult.php rename to src/Handler/Document/Page/SavePage/SavePageResult.php index 14d58d0d..a5831d1a 100644 --- a/src/Handler/Document/Page/SavePageResult.php +++ b/src/Handler/Document/Page/SavePage/SavePageResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\SavePage; use OpenDxp\Model\Document\Page; use OpenDxp\Model\Version; diff --git a/src/Handler/Document/RemoveFromSession/RemoveFromSessionHandler.php b/src/Handler/Document/RemoveFromSession/RemoveFromSessionHandler.php new file mode 100644 index 00000000..14f847b3 --- /dev/null +++ b/src/Handler/Document/RemoveFromSession/RemoveFromSessionHandler.php @@ -0,0 +1,32 @@ +sessionService->removeDocument($payload->id); + } +} diff --git a/src/Handler/Document/Renderlet/RenderRenderletHandler.php b/src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletHandler.php similarity index 94% rename from src/Handler/Document/Renderlet/RenderRenderletHandler.php rename to src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletHandler.php index 84247954..85e0c75a 100644 --- a/src/Handler/Document/Renderlet/RenderRenderletHandler.php +++ b/src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletHandler.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet\RenderRenderlet; -use OpenDxp\Bundle\AdminBundle\Payload\Document\RenderRenderletPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet\RenderRenderlet\RenderRenderletPayload; use OpenDxp\Document\Editable\EditableHandler; use OpenDxp\Event\DocumentEvents; use OpenDxp\Localization\LocaleServiceInterface; diff --git a/src/Payload/Document/RenderRenderletPayload.php b/src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletPayload.php similarity index 85% rename from src/Payload/Document/RenderRenderletPayload.php rename to src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletPayload.php index 997d7011..4ee24282 100644 --- a/src/Payload/Document/RenderRenderletPayload.php +++ b/src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletPayload.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet\RenderRenderlet; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -class RenderRenderletPayload +final readonly class RenderRenderletPayload implements ExtJsPayloadInterface { public function __construct( public readonly ?string $type, @@ -41,4 +42,4 @@ public static function fromRequest(Request $request): static query: $request->query->all(), ); } -} \ No newline at end of file +} diff --git a/src/Handler/Document/Renderlet/RenderRenderletResult.php b/src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletResult.php similarity index 88% rename from src/Handler/Document/Renderlet/RenderRenderletResult.php rename to src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletResult.php index 809a007f..09d7de97 100644 --- a/src/Handler/Document/Renderlet/RenderRenderletResult.php +++ b/src/Handler/Document/Renderlet/RenderRenderlet/RenderRenderletResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Renderlet\RenderRenderlet; final readonly class RenderRenderletResult { diff --git a/src/Handler/Document/SaveToSession/SaveToSessionHandler.php b/src/Handler/Document/SaveToSession/SaveToSessionHandler.php new file mode 100644 index 00000000..e24d4d19 --- /dev/null +++ b/src/Handler/Document/SaveToSession/SaveToSessionHandler.php @@ -0,0 +1,59 @@ +id) { + return; + } + + $document = $this->sessionService->getOrLoadDocument($payload->id); + if (!$document) { + throw new NotFoundHttpException('Document not found in session'); + } + + $document->setInDumpState(true); + + if ($document instanceof Document\Email) { + $this->mapper->applyPagePayload($payload->email, $document); + } elseif ($document instanceof Document\PageSnippet) { + $this->mapper->applyPagePayload($payload->page, $document); + } elseif ($document instanceof Document\Link) { + $this->mapper->applyLinkPayload($payload->link, $document); + } elseif ($document instanceof Document\Hardlink) { + $this->mapper->applyHardlinkPayload($payload->hardlink, $document); + } elseif ($document instanceof Document\Folder) { + $this->mapper->applyFolderPayload($payload->folder, $document); + } + + $this->sessionService->saveDocument($document); + } +} diff --git a/src/Handler/Document/SaveToSession/SaveToSessionPayload.php b/src/Handler/Document/SaveToSession/SaveToSessionPayload.php new file mode 100644 index 00000000..2c013aed --- /dev/null +++ b/src/Handler/Document/SaveToSession/SaveToSessionPayload.php @@ -0,0 +1,55 @@ +request->getInt('id'); + if (!$id) { + return new static(id: 0, page: null, email: null, link: null, hardlink: null, folder: null); + } + + return new static( + id: $id, + page: PagePayload::fromRequest($request), + email: SaveEmailPayload::fromRequest($request), + link: SaveLinkPayload::fromRequest($request), + hardlink: SaveHardlinkPayload::fromRequest($request), + folder: SaveFolderPayload::fromRequest($request), + ); + } +} diff --git a/src/Handler/Document/Site/GetSiteCustomSettingsHandler.php b/src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsHandler.php similarity index 82% rename from src/Handler/Document/Site/GetSiteCustomSettingsHandler.php rename to src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsHandler.php index d07cb35a..25d3fde4 100644 --- a/src/Handler/Document/Site/GetSiteCustomSettingsHandler.php +++ b/src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Site; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Site\GetSiteCustomSettings; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Event\SiteCustomSettingsEvent; @@ -28,9 +28,9 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke(int $siteId): GetSiteCustomSettingsResult + public function __invoke(GetSiteCustomSettingsPayload $payload): GetSiteCustomSettingsResult { - $site = Site::getById($siteId); + $site = Site::getById($payload->id); $event = new SiteCustomSettingsEvent($site); $this->eventDispatcher->dispatch($event, AdminEvents::SITE_CUSTOM_SETTINGS); diff --git a/src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsPayload.php b/src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsPayload.php new file mode 100644 index 00000000..14501c62 --- /dev/null +++ b/src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsPayload.php @@ -0,0 +1,34 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/Document/Site/GetSiteCustomSettingsResult.php b/src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsResult.php similarity index 88% rename from src/Handler/Document/Site/GetSiteCustomSettingsResult.php rename to src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsResult.php index cf2d6b59..ac7c3ac0 100644 --- a/src/Handler/Document/Site/GetSiteCustomSettingsResult.php +++ b/src/Handler/Document/Site/GetSiteCustomSettings/GetSiteCustomSettingsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Site; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Site\GetSiteCustomSettings; final readonly class GetSiteCustomSettingsResult { diff --git a/src/Handler/Document/Site/RemoveSiteHandler.php b/src/Handler/Document/Site/RemoveSite/RemoveSiteHandler.php similarity index 75% rename from src/Handler/Document/Site/RemoveSiteHandler.php rename to src/Handler/Document/Site/RemoveSite/RemoveSiteHandler.php index 8e04b7eb..55de0894 100644 --- a/src/Handler/Document/Site/RemoveSiteHandler.php +++ b/src/Handler/Document/Site/RemoveSite/RemoveSiteHandler.php @@ -15,14 +15,14 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Site; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Site\RemoveSite; use OpenDxp\Model\Site; final class RemoveSiteHandler { - public function __invoke(int $rootId): void + public function __invoke(RemoveSitePayload $payload): void { - Site::getByRootId($rootId)->delete(); + Site::getByRootId($payload->id)->delete(); } } diff --git a/src/Handler/Document/Site/RemoveSite/RemoveSitePayload.php b/src/Handler/Document/Site/RemoveSite/RemoveSitePayload.php new file mode 100644 index 00000000..1570d0a1 --- /dev/null +++ b/src/Handler/Document/Site/RemoveSite/RemoveSitePayload.php @@ -0,0 +1,34 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/Document/UpdateSiteHandler.php b/src/Handler/Document/Site/UpdateSite/UpdateSiteHandler.php similarity index 68% rename from src/Handler/Document/UpdateSiteHandler.php rename to src/Handler/Document/Site/UpdateSite/UpdateSiteHandler.php index 2809878a..819d1e04 100644 --- a/src/Handler/Document/UpdateSiteHandler.php +++ b/src/Handler/Document/Site/UpdateSite/UpdateSiteHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Site\UpdateSite; use OpenDxp\Bundle\AdminBundle\Enum\SiteCustomConfigNodeType; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; @@ -29,17 +29,10 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke( - int $rootId, - array $domains, - string $mainDomain, - string $errorDocument, - array $localizedErrorDocuments, - bool $redirectToMainDomain, - array $requestCustomSettings, - ): UpdateSiteResult { - if (!$site = Site::getByRootId($rootId)) { - $site = Site::create(['rootId' => $rootId]); + public function __invoke(UpdateSitePayload $payload): UpdateSiteResult + { + if (!$site = Site::getByRootId($payload->rootId)) { + $site = Site::create(['rootId' => $payload->rootId]); } $event = new SiteCustomSettingsEvent($site); @@ -49,8 +42,8 @@ public function __invoke( foreach ($event->getConfigNodes() as $scope => $nodes) { foreach ($nodes as $node) { $requestValueName = sprintf('customSettings_%s_%s', $scope, $node['name']); - if (isset($requestCustomSettings[$requestValueName])) { - $value = $requestCustomSettings[$requestValueName]; + if (isset($payload->requestCustomSettings[$requestValueName])) { + $value = $payload->requestCustomSettings[$requestValueName]; if ($node['type'] === SiteCustomConfigNodeType::CHECKBOX->value) { $value = $value === 'true'; } @@ -59,11 +52,11 @@ public function __invoke( } } - $site->setDomains($domains); - $site->setMainDomain($mainDomain); - $site->setErrorDocument($errorDocument); - $site->setLocalizedErrorDocuments($localizedErrorDocuments); - $site->setRedirectToMainDomain($redirectToMainDomain); + $site->setDomains($payload->domains); + $site->setMainDomain($payload->mainDomain); + $site->setErrorDocument($payload->errorDocument); + $site->setLocalizedErrorDocuments($payload->localizedErrorDocuments); + $site->setRedirectToMainDomain($payload->redirectToMainDomain); $site->setCustomSettings(count($customSettings) === 0 ? null : $customSettings); $site->save(); diff --git a/src/Handler/Document/Site/UpdateSite/UpdateSitePayload.php b/src/Handler/Document/Site/UpdateSite/UpdateSitePayload.php new file mode 100644 index 00000000..1b52a45c --- /dev/null +++ b/src/Handler/Document/Site/UpdateSite/UpdateSitePayload.php @@ -0,0 +1,58 @@ +request->getString('domains')); + $domains = $domainsRaw ? explode("\n", $domainsRaw) : []; + + $localizedErrorDocuments = []; + foreach (Tool::getValidLanguages() as $language) { + $requestValue = $request->request->get(sprintf('errorDocument_localized_%s', $language)); + if (isset($requestValue)) { + $localizedErrorDocuments[$language] = $requestValue; + } + } + + return new static( + rootId: $request->request->getInt('id'), + domains: $domains, + mainDomain: $request->request->getString('mainDomain'), + errorDocument: $request->request->getString('errorDocument'), + localizedErrorDocuments: $localizedErrorDocuments, + redirectToMainDomain: $request->request->getBoolean('redirectToMainDomain'), + requestCustomSettings: $request->request->all(), + ); + } +} diff --git a/src/Handler/Document/UpdateSiteResult.php b/src/Handler/Document/Site/UpdateSite/UpdateSiteResult.php similarity index 89% rename from src/Handler/Document/UpdateSiteResult.php rename to src/Handler/Document/Site/UpdateSite/UpdateSiteResult.php index 6d9ef8eb..9468ccbf 100644 --- a/src/Handler/Document/UpdateSiteResult.php +++ b/src/Handler/Document/Site/UpdateSite/UpdateSiteResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Site\UpdateSite; final readonly class UpdateSiteResult { diff --git a/src/Handler/Document/Snippet/GetSnippetDataHandler.php b/src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataHandler.php similarity index 91% rename from src/Handler/Document/Snippet/GetSnippetDataHandler.php rename to src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataHandler.php index f1f2ec5c..cafb7225 100644 --- a/src/Handler/Document/Snippet/GetSnippetDataHandler.php +++ b/src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataHandler.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Helper\DocumentVersionHelper; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; use OpenDxp\Model\Document\Snippet; @@ -35,9 +36,9 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function __invoke(int $id): GetSnippetDataResult + public function __invoke(IdQueryPayload $payload): GetSnippetDataResult { - $snippet = Snippet::getById($id); + $snippet = Snippet::getById($payload->id); if (!$snippet) { throw new NotFoundHttpException('Snippet not found'); } diff --git a/src/Handler/Document/Snippet/GetSnippetDataResult.php b/src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataResult.php similarity index 90% rename from src/Handler/Document/Snippet/GetSnippetDataResult.php rename to src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataResult.php index fa1b498f..115793a9 100644 --- a/src/Handler/Document/Snippet/GetSnippetDataResult.php +++ b/src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData; use OpenDxp\Model\Document\Snippet; use OpenDxp\Model\Version; diff --git a/src/Handler/Document/Snippet/SaveSnippetHandler.php b/src/Handler/Document/Snippet/SaveSnippet/SaveSnippetHandler.php similarity index 87% rename from src/Handler/Document/Snippet/SaveSnippetHandler.php rename to src/Handler/Document/Snippet/SaveSnippet/SaveSnippetHandler.php index 7fac2c96..17297422 100644 --- a/src/Handler/Document/Snippet/SaveSnippetHandler.php +++ b/src/Handler/Document/Snippet/SaveSnippet/SaveSnippetHandler.php @@ -15,10 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\SaveSnippet; use OpenDxp\Bundle\AdminBundle\Helper\DocumentVersionHelper; -use OpenDxp\Bundle\AdminBundle\Payload\Document\SnippetPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\SaveSnippet\SaveSnippetPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper; use OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator; @@ -35,9 +35,9 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function __invoke(int $id, SnippetPayload $payload, bool $sessionAware = true): SaveSnippetResult + public function __invoke(SaveSnippetPayload $payload, bool $sessionAware = true): SaveSnippetResult { - $snippet = Snippet::getById($id); + $snippet = Snippet::getById($payload->id); if (!$snippet) { throw new NotFoundHttpException('Snippet not found'); } diff --git a/src/Handler/Document/DocumentResult.php b/src/Handler/Document/Snippet/SaveSnippet/SaveSnippetPayload.php similarity index 71% rename from src/Handler/Document/DocumentResult.php rename to src/Handler/Document/Snippet/SaveSnippet/SaveSnippetPayload.php index f15e55e5..dfb6fef4 100644 --- a/src/Handler/Document/DocumentResult.php +++ b/src/Handler/Document/Snippet/SaveSnippet/SaveSnippetPayload.php @@ -15,13 +15,8 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\SaveSnippet; -use OpenDxp\Model\Document; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\PagePayload; -final readonly class DocumentResult -{ - public function __construct( - public Document $document, - ) {} -} +final readonly class SaveSnippetPayload extends PagePayload {} diff --git a/src/Handler/Document/Snippet/SaveSnippetResult.php b/src/Handler/Document/Snippet/SaveSnippet/SaveSnippetResult.php similarity index 91% rename from src/Handler/Document/Snippet/SaveSnippetResult.php rename to src/Handler/Document/Snippet/SaveSnippet/SaveSnippetResult.php index ec425da0..fbec27d0 100644 --- a/src/Handler/Document/Snippet/SaveSnippetResult.php +++ b/src/Handler/Document/Snippet/SaveSnippet/SaveSnippetResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\SaveSnippet; use OpenDxp\Model\Document\Snippet; use OpenDxp\Model\Version; diff --git a/src/Handler/Document/Translation/AddDocumentTranslationHandler.php b/src/Handler/Document/Translation/AddDocumentTranslation/AddDocumentTranslationHandler.php similarity index 87% rename from src/Handler/Document/Translation/AddDocumentTranslationHandler.php rename to src/Handler/Document/Translation/AddDocumentTranslation/AddDocumentTranslationHandler.php index 8560d128..b0a5ca6d 100644 --- a/src/Handler/Document/Translation/AddDocumentTranslationHandler.php +++ b/src/Handler/Document/Translation/AddDocumentTranslation/AddDocumentTranslationHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\AddDocumentTranslation; use Exception; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; @@ -26,10 +26,10 @@ public function __construct( private readonly ElementServiceFactory $serviceFactory, ) {} - public function __invoke(int $sourceId, string $targetPath): void + public function __invoke(AddDocumentTranslationPayload $payload): void { - $sourceDocument = Document::getById($sourceId); - $targetDocument = Document::getByPath($targetPath); + $sourceDocument = Document::getById($payload->sourceId); + $targetDocument = Document::getByPath($payload->targetPath); if (!$sourceDocument || !$targetDocument) { return; diff --git a/src/Handler/Document/Translation/AddDocumentTranslation/AddDocumentTranslationPayload.php b/src/Handler/Document/Translation/AddDocumentTranslation/AddDocumentTranslationPayload.php new file mode 100644 index 00000000..2dc4438a --- /dev/null +++ b/src/Handler/Document/Translation/AddDocumentTranslation/AddDocumentTranslationPayload.php @@ -0,0 +1,36 @@ +request->getInt('sourceId'), + targetPath: $request->request->getString('targetPath'), + ); + } +} diff --git a/src/Handler/Document/Translation/CheckTranslationLanguageHandler.php b/src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguageHandler.php similarity index 86% rename from src/Handler/Document/Translation/CheckTranslationLanguageHandler.php rename to src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguageHandler.php index b4f0f31d..bcd6ca08 100644 --- a/src/Handler/Document/Translation/CheckTranslationLanguageHandler.php +++ b/src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguageHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\CheckTranslationLanguage; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; use OpenDxp\Model\Document; @@ -25,9 +25,9 @@ public function __construct( private readonly ElementServiceFactory $serviceFactory, ) {} - public function __invoke(?string $path): CheckTranslationLanguageResult + public function __invoke(CheckTranslationLanguagePayload $payload): CheckTranslationLanguageResult { - $document = Document::getByPath($path); + $document = Document::getByPath($payload->path); if (!$document) { return new CheckTranslationLanguageResult(false, null, null); } diff --git a/src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguagePayload.php b/src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguagePayload.php new file mode 100644 index 00000000..4cd0d476 --- /dev/null +++ b/src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguagePayload.php @@ -0,0 +1,34 @@ +query->has('path') ? $request->query->getString('path') : null, + ); + } +} diff --git a/src/Handler/Document/Translation/CheckTranslationLanguageResult.php b/src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguageResult.php similarity index 96% rename from src/Handler/Document/Translation/CheckTranslationLanguageResult.php rename to src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguageResult.php index 71963729..107b0077 100644 --- a/src/Handler/Document/Translation/CheckTranslationLanguageResult.php +++ b/src/Handler/Document/Translation/CheckTranslationLanguage/CheckTranslationLanguageResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\CheckTranslationLanguage; final readonly class CheckTranslationLanguageResult { diff --git a/src/Handler/Document/Translation/DetermineTranslationParentHandler.php b/src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentHandler.php similarity index 80% rename from src/Handler/Document/Translation/DetermineTranslationParentHandler.php rename to src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentHandler.php index b079fd9a..1c9bb4e2 100644 --- a/src/Handler/Document/Translation/DetermineTranslationParentHandler.php +++ b/src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\DetermineTranslationParent; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; use OpenDxp\Model\Document; @@ -25,9 +25,9 @@ public function __construct( private readonly ElementServiceFactory $serviceFactory, ) {} - public function __invoke(int $id, ?string $language): DetermineTranslationParentResult + public function __invoke(DetermineTranslationParentPayload $payload): DetermineTranslationParentResult { - $document = Document::getById($id); + $document = Document::getById($payload->id); if (!$document) { return new DetermineTranslationParentResult(false, null, null); } @@ -36,8 +36,8 @@ public function __invoke(int $id, ?string $language): DetermineTranslationParent $document = $document->getId() === 1 ? $document : $document->getParent(); $translations = $service->getTranslations($document); - if (isset($translations[$language])) { - $targetDocument = Document::getById($translations[$language]); + if (isset($translations[$payload->language])) { + $targetDocument = Document::getById($translations[$payload->language]); return new DetermineTranslationParentResult( true, diff --git a/src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentPayload.php b/src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentPayload.php new file mode 100644 index 00000000..72991a3d --- /dev/null +++ b/src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentPayload.php @@ -0,0 +1,36 @@ +query->getInt('id'), + language: $request->query->has('language') ? $request->query->getString('language') : null, + ); + } +} diff --git a/src/Handler/Document/Translation/DetermineTranslationParentResult.php b/src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentResult.php similarity index 95% rename from src/Handler/Document/Translation/DetermineTranslationParentResult.php rename to src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentResult.php index a1a9c9b8..e8c75bba 100644 --- a/src/Handler/Document/Translation/DetermineTranslationParentResult.php +++ b/src/Handler/Document/Translation/DetermineTranslationParent/DetermineTranslationParentResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\DetermineTranslationParent; final readonly class DetermineTranslationParentResult { diff --git a/src/Handler/Document/Translation/GetLanguageTreeHandler.php b/src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreeHandler.php similarity index 93% rename from src/Handler/Document/Translation/GetLanguageTreeHandler.php rename to src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreeHandler.php index e9be9055..a0f890dd 100644 --- a/src/Handler/Document/Translation/GetLanguageTreeHandler.php +++ b/src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreeHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTree; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; @@ -30,9 +30,9 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function __invoke(int $nodeId, array $languages): GetLanguageTreeResult + public function __invoke(GetLanguageTreePayload $payload): GetLanguageTreeResult { - $document = Document::getById($nodeId); + $document = Document::getById($payload->node); if (!$document) { throw new NotFoundHttpException('Document not found'); @@ -40,7 +40,7 @@ public function __invoke(int $nodeId, array $languages): GetLanguageTreeResult $nodes = []; foreach ($document->getChildren() as $child) { - $nodes[] = $this->getTranslationTreeNodeConfig($child, $languages); + $nodes[] = $this->getTranslationTreeNodeConfig($child, $payload->languages); } return new GetLanguageTreeResult($nodes); diff --git a/src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreePayload.php b/src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreePayload.php new file mode 100644 index 00000000..c5adffe7 --- /dev/null +++ b/src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreePayload.php @@ -0,0 +1,36 @@ +query->getInt('node'), + languages: explode(',', $request->query->getString('languages')), + ); + } +} diff --git a/src/Handler/Document/Translation/GetLanguageTreeResult.php b/src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreeResult.php similarity index 97% rename from src/Handler/Document/Translation/GetLanguageTreeResult.php rename to src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreeResult.php index f7bce8a6..a94254ee 100644 --- a/src/Handler/Document/Translation/GetLanguageTreeResult.php +++ b/src/Handler/Document/Translation/GetLanguageTree/GetLanguageTreeResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTree; final readonly class GetLanguageTreeResult { diff --git a/src/Handler/Document/Translation/GetLanguageTreeRootHandler.php b/src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootHandler.php similarity index 96% rename from src/Handler/Document/Translation/GetLanguageTreeRootHandler.php rename to src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootHandler.php index 262d6afe..e05707f5 100644 --- a/src/Handler/Document/Translation/GetLanguageTreeRootHandler.php +++ b/src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTreeRoot; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; @@ -32,9 +32,9 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function __invoke(int $id): GetLanguageTreeRootResult + public function __invoke(GetLanguageTreeRootPayload $payload): GetLanguageTreeRootResult { - $document = Document::getById($id); + $document = Document::getById($payload->id); if (!$document) { throw new NotFoundHttpException('Document not found'); diff --git a/src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootPayload.php b/src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootPayload.php new file mode 100644 index 00000000..fab95e7d --- /dev/null +++ b/src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootPayload.php @@ -0,0 +1,34 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/Document/Translation/GetLanguageTreeRootResult.php b/src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootResult.php similarity index 96% rename from src/Handler/Document/Translation/GetLanguageTreeRootResult.php rename to src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootResult.php index cf3afe9d..ac5323a4 100644 --- a/src/Handler/Document/Translation/GetLanguageTreeRootResult.php +++ b/src/Handler/Document/Translation/GetLanguageTreeRoot/GetLanguageTreeRootResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\GetLanguageTreeRoot; final readonly class GetLanguageTreeRootResult { diff --git a/src/Handler/Document/Translation/RemoveDocumentTranslationHandler.php b/src/Handler/Document/Translation/RemoveDocumentTranslation/RemoveDocumentTranslationHandler.php similarity index 80% rename from src/Handler/Document/Translation/RemoveDocumentTranslationHandler.php rename to src/Handler/Document/Translation/RemoveDocumentTranslation/RemoveDocumentTranslationHandler.php index b6c1fd1f..69c69f85 100644 --- a/src/Handler/Document/Translation/RemoveDocumentTranslationHandler.php +++ b/src/Handler/Document/Translation/RemoveDocumentTranslation/RemoveDocumentTranslationHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Translation\RemoveDocumentTranslation; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; use OpenDxp\Model\Document; @@ -25,10 +25,10 @@ public function __construct( private readonly ElementServiceFactory $serviceFactory, ) {} - public function __invoke(int $sourceId, int $targetId): void + public function __invoke(RemoveDocumentTranslationPayload $payload): void { - $sourceDocument = Document::getById($sourceId); - $targetDocument = Document::getById($targetId); + $sourceDocument = Document::getById($payload->sourceId); + $targetDocument = Document::getById($payload->targetId); if (!$sourceDocument || !$targetDocument) { return; diff --git a/src/Handler/Document/Translation/RemoveDocumentTranslation/RemoveDocumentTranslationPayload.php b/src/Handler/Document/Translation/RemoveDocumentTranslation/RemoveDocumentTranslationPayload.php new file mode 100644 index 00000000..ee4c5f6b --- /dev/null +++ b/src/Handler/Document/Translation/RemoveDocumentTranslation/RemoveDocumentTranslationPayload.php @@ -0,0 +1,36 @@ +request->getInt('sourceId'), + targetId: $request->request->getInt('targetId'), + ); + } +} diff --git a/src/Handler/Document/TreeGetDocumentChildrenHandler.php b/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenHandler.php similarity index 83% rename from src/Handler/Document/TreeGetDocumentChildrenHandler.php rename to src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenHandler.php index dfcab24c..f2a83b80 100644 --- a/src/Handler/Document/TreeGetDocumentChildrenHandler.php +++ b/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenHandler.php @@ -8,15 +8,17 @@ * Full copyright and license information is available in * LICENSE.md which is distributed with this source code. * - * @copyright Copyright (c) OpenDXP (https://www.opendxp.io) + * @copyright Copyright (c) Pimcore GmbH (https://pimcore.com) + * @copyright Modification Copyright (c) OpenDXP (https://www.opendxp.io) * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildren; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentChildrenHandler; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Db; @@ -35,13 +37,13 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke(array $allParams): TreeGetDocumentChildrenResult + public function __invoke(TreeGetDocumentChildrenPayload $payload): TreeGetDocumentChildrenResult { - $paginated = isset($allParams['limit']); - $limit = (int)($allParams['limit'] ?? 100000000); - $offset = (int)($allParams['start'] ?? 0); + $paginated = isset($payload->allParams['limit']); + $limit = (int)($payload->allParams['limit'] ?? 100000000); + $offset = (int)($payload->allParams['start'] ?? 0); - $filter = $allParams['filter'] ?? null; + $filter = $payload->allParams['filter'] ?? null; if (!is_null($filter)) { if (!str_ends_with($filter, '*')) { $filter .= '*'; @@ -51,7 +53,7 @@ public function __invoke(array $allParams): TreeGetDocumentChildrenResult $offset = 0; } - $document = Document::getById((int) $allParams['node']); + $document = Document::getById((int) $payload->allParams['node']); if (!$document) { throw new NotFoundHttpException('Document was not found'); } @@ -60,8 +62,8 @@ public function __invoke(array $allParams): TreeGetDocumentChildrenResult $documents = []; $cv = []; if ($document->hasChildren()) { - if ($allParams['view'] ?? null) { - $cv = $this->elementService->getCustomViewById($allParams['view']); + if ($payload->allParams['view'] ?? null) { + $cv = $this->elementService->getCustomViewById($payload->allParams['view']); } $db = Db::get(); @@ -95,7 +97,7 @@ public function __invoke(array $allParams): TreeGetDocumentChildrenResult $beforeListLoadEvent = new GenericEvent($this, [ 'list' => $list, - 'context' => $allParams, + 'context' => $payload->allParams, ]); $this->eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::DOCUMENT_LIST_BEFORE_LIST_LOAD); diff --git a/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenPayload.php b/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenPayload.php new file mode 100644 index 00000000..d43530fd --- /dev/null +++ b/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenPayload.php @@ -0,0 +1,39 @@ +query->getInt('node'), + inSearch: $request->query->getInt('inSearch'), + allParams: $request->query->all(), + ); + } +} diff --git a/src/Handler/Document/TreeGetDocumentChildrenResult.php b/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenResult.php similarity index 74% rename from src/Handler/Document/TreeGetDocumentChildrenResult.php rename to src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenResult.php index 34952e4e..2ba6cdd0 100644 --- a/src/Handler/Document/TreeGetDocumentChildrenResult.php +++ b/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenResult.php @@ -8,13 +8,14 @@ * Full copyright and license information is available in * LICENSE.md which is distributed with this source code. * - * @copyright Copyright (c) OpenDXP (https://www.opendxp.io) + * @copyright Copyright (c) Pimcore GmbH (https://pimcore.com) + * @copyright Modification Copyright (c) OpenDXP (https://www.opendxp.io) * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildren; final readonly class TreeGetDocumentChildrenResult { diff --git a/src/Handler/Document/UpdateDocumentHandler.php b/src/Handler/Document/UpdateDocument/UpdateDocumentHandler.php similarity index 97% rename from src/Handler/Document/UpdateDocumentHandler.php rename to src/Handler/Document/UpdateDocument/UpdateDocumentHandler.php index 7ececb8a..f0a582b2 100644 --- a/src/Handler/Document/UpdateDocumentHandler.php +++ b/src/Handler/Document/UpdateDocument/UpdateDocumentHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateDocument; use OpenDxp\Bundle\AdminBundle\Exception\Document\DocumentNotFoundException; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; @@ -35,9 +35,9 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke(int $id, array $updateData): UpdateDocumentResult + public function __invoke(UpdateDocumentPayload $payload): UpdateDocumentResult { - $document = Document::getById($id); + $document = Document::getById($payload->id); if (!$document instanceof Document) { throw new DocumentNotFoundException($id); } diff --git a/src/Handler/Document/UpdateDocument/UpdateDocumentPayload.php b/src/Handler/Document/UpdateDocument/UpdateDocumentPayload.php new file mode 100644 index 00000000..9eb9bae2 --- /dev/null +++ b/src/Handler/Document/UpdateDocument/UpdateDocumentPayload.php @@ -0,0 +1,36 @@ +request->get('id'), + updateData: [...$request->request->all(), ...$request->query->all()], + ); + } +} diff --git a/src/Handler/Document/UpdateDocumentResult.php b/src/Handler/Document/UpdateDocument/UpdateDocumentResult.php similarity index 88% rename from src/Handler/Document/UpdateDocumentResult.php rename to src/Handler/Document/UpdateDocument/UpdateDocumentResult.php index 256497a5..7152e173 100644 --- a/src/Handler/Document/UpdateDocumentResult.php +++ b/src/Handler/Document/UpdateDocument/UpdateDocumentResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateDocument; final readonly class UpdateDocumentResult { diff --git a/src/Handler/Document/Version/DiffVersionsHandler.php b/src/Handler/Document/Version/DiffVersions/DiffVersionsHandler.php similarity index 88% rename from src/Handler/Document/Version/DiffVersionsHandler.php rename to src/Handler/Document/Version/DiffVersions/DiffVersionsHandler.php index d022d7b5..d84831b8 100644 --- a/src/Handler/Document/Version/DiffVersionsHandler.php +++ b/src/Handler/Document/Version/DiffVersions/DiffVersionsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version\DiffVersions; use Imagick; use OpenDxp\Bundle\AdminBundle\Exception\Document\DocumentNotFoundException; @@ -33,24 +33,24 @@ public function __construct( private readonly RouterInterface $router, ) {} - public function __invoke(int $fromId, int $toId, string $requestSchemeAndHost): DiffVersionsResult + public function __invoke(DiffVersionsPayload $payload): DiffVersionsResult { if (!HtmlToImage::isSupported() || !class_exists('Imagick')) { return new DiffVersionsResult(supported: false); } - $versionFrom = Version::getById($fromId); + $versionFrom = Version::getById($payload->from); $docFrom = $versionFrom?->loadData(); if (!$docFrom instanceof Document) { - throw new DocumentNotFoundException($fromId); + throw new DocumentNotFoundException($payload->from); } - $versionTo = Version::getById($toId); + $versionTo = Version::getById($payload->to); $docTo = $versionTo?->loadData(); if (!$docTo instanceof Document) { - throw new DocumentNotFoundException($toId); + throw new DocumentNotFoundException($payload->to); } $comparisonId = uniqid(date('Y-m-d') . '-', true); @@ -63,7 +63,7 @@ public function __invoke(int $fromId, int $toId, string $requestSchemeAndHost): file_put_contents($fromHtmlFile, $this->documentRenderer->render($docFrom)); file_put_contents($toHtmlFile, $this->documentRenderer->render($docTo)); - $prefix = Config::getSystemConfiguration('documents')['preview_url_prefix'] ?: $requestSchemeAndHost; + $prefix = Config::getSystemConfiguration('documents')['preview_url_prefix'] ?: $payload->schemeAndHost; try { HtmlToImage::convert($prefix . $this->router->generate('opendxp_admin_document_document_diff_versions_html', ['id' => basename($fromHtmlFile)]), $fromImageFile); diff --git a/src/Handler/Document/Version/DiffVersions/DiffVersionsPayload.php b/src/Handler/Document/Version/DiffVersions/DiffVersionsPayload.php new file mode 100644 index 00000000..c62f95e8 --- /dev/null +++ b/src/Handler/Document/Version/DiffVersions/DiffVersionsPayload.php @@ -0,0 +1,38 @@ +attributes->getInt('from'), + to: $request->attributes->getInt('to'), + schemeAndHost: $request->getSchemeAndHttpHost(), + ); + } +} diff --git a/src/Handler/Document/Version/DiffVersionsResult.php b/src/Handler/Document/Version/DiffVersions/DiffVersionsResult.php similarity index 86% rename from src/Handler/Document/Version/DiffVersionsResult.php rename to src/Handler/Document/Version/DiffVersions/DiffVersionsResult.php index 2b0eb16d..ec8806a3 100644 --- a/src/Handler/Document/Version/DiffVersionsResult.php +++ b/src/Handler/Document/Version/DiffVersions/DiffVersionsResult.php @@ -15,12 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version\DiffVersions; final readonly class DiffVersionsResult { public function __construct( - public bool $supported, + public bool $supported, public ?string $image = null, public ?string $image1 = null, public ?string $image2 = null, diff --git a/src/Handler/Document/Version/PublishVersionHandler.php b/src/Handler/Document/Version/PublishVersion/PublishVersionHandler.php similarity index 86% rename from src/Handler/Document/Version/PublishVersionHandler.php rename to src/Handler/Document/Version/PublishVersion/PublishVersionHandler.php index 5040180b..fa994f8d 100644 --- a/src/Handler/Document/Version/PublishVersionHandler.php +++ b/src/Handler/Document/Version/PublishVersion/PublishVersionHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version\PublishVersion; use OpenDxp\Bundle\AdminBundle\Exception\Document\DocumentNotFoundException; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Model\Document; @@ -33,14 +34,14 @@ public function __construct( private readonly ElementResponseNormalizer $normalizer, ) {} - public function __invoke(int $versionId): PublishVersionResult + public function __invoke(IdBodyPayload $payload): PublishVersionResult { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $version = Version::getById($versionId); + $version = Version::getById($payload->id); $document = $version?->loadData(); if (!$document instanceof Document) { - throw new DocumentNotFoundException($versionId); + throw new DocumentNotFoundException($payload->id); } $this->sessionService->saveDocument($document); diff --git a/src/Handler/Document/Version/PublishVersionResult.php b/src/Handler/Document/Version/PublishVersion/PublishVersionResult.php similarity index 88% rename from src/Handler/Document/Version/PublishVersionResult.php rename to src/Handler/Document/Version/PublishVersion/PublishVersionResult.php index 24068bcc..f061c88e 100644 --- a/src/Handler/Document/Version/PublishVersionResult.php +++ b/src/Handler/Document/Version/PublishVersion/PublishVersionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version\PublishVersion; final readonly class PublishVersionResult { diff --git a/src/Handler/Document/Version/SaveVersionToSessionHandler.php b/src/Handler/Document/Version/SaveVersionToSession/SaveVersionToSessionHandler.php similarity index 75% rename from src/Handler/Document/Version/SaveVersionToSessionHandler.php rename to src/Handler/Document/Version/SaveVersionToSession/SaveVersionToSessionHandler.php index e93939de..42430fdc 100644 --- a/src/Handler/Document/Version/SaveVersionToSessionHandler.php +++ b/src/Handler/Document/Version/SaveVersionToSession/SaveVersionToSessionHandler.php @@ -15,9 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version\SaveVersionToSession; use OpenDxp\Bundle\AdminBundle\Exception\Document\DocumentNotFoundException; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Model\Document; use OpenDxp\Model\Version; @@ -26,13 +27,13 @@ final class SaveVersionToSessionHandler { public function __construct(private readonly SessionService $sessionService) {} - public function __invoke(int $versionId): void + public function __invoke(IdBodyPayload $payload): void { - $version = Version::getById($versionId); + $version = Version::getById($payload->id); $document = $version?->loadData(); if (!$document instanceof Document) { - throw new DocumentNotFoundException($versionId); + throw new DocumentNotFoundException($payload->id); } $this->sessionService->saveDocument($document); diff --git a/src/Handler/Element/DeleteNoteHandler.php b/src/Handler/Element/DeleteNoteHandler.php index a92a97f7..fe431204 100644 --- a/src/Handler/Element/DeleteNoteHandler.php +++ b/src/Handler/Element/DeleteNoteHandler.php @@ -18,19 +18,22 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Element; use OpenDxp\Model\Element\Note; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; final class DeleteNoteHandler { - public function __invoke(int $id): bool + public function __invoke(NoteListPayload $payload): void { - $note = Note::getById($id); + $note = Note::getById($payload->id); - if ($note && !$note->getLocked()) { - $note->delete(); + if (!$note) { + return; + } - return true; + if ($note->getLocked()) { + throw new BadRequestHttpException('note_is_locked'); } - return false; + $note->delete(); } } diff --git a/src/Handler/Element/GetNoteListHandler.php b/src/Handler/Element/GetNoteListHandler.php index 6676fb88..ebe3c281 100644 --- a/src/Handler/Element/GetNoteListHandler.php +++ b/src/Handler/Element/GetNoteListHandler.php @@ -22,23 +22,16 @@ final class GetNoteListHandler { - public function __invoke( - int $offset, - ?int $limit, - array $sortingSettings, - ?string $filterText, - ?string $filterJson, - ?string $cid, - ?string $ctype, - ): GetNoteListResult { + public function __invoke(NoteListPayload $payload): GetNoteListResult + { $list = new Element\Note\Listing(); - $list->setLimit($limit); - $list->setOffset($offset); + $list->setLimit($payload->limit); + $list->setOffset($payload->offset); - if ($sortingSettings['orderKey'] && $sortingSettings['order']) { - $list->setOrderKey($sortingSettings['orderKey']); - $list->setOrder($sortingSettings['order']); + if ($payload->sortingSettings['orderKey'] && $payload->sortingSettings['order']) { + $list->setOrderKey($payload->sortingSettings['orderKey']); + $list->setOrder($payload->sortingSettings['order']); } else { $list->setOrderKey(['date', 'id']); $list->setOrder(['DESC', 'DESC']); @@ -46,19 +39,19 @@ public function __invoke( $conditions = []; - if ($filterText) { + if ($payload->filterText) { $conditions[] = '(' - . '`title` LIKE ' . $list->quote('%' . $filterText . '%') - . ' OR `description` LIKE ' . $list->quote('%' . $filterText . '%') - . ' OR `type` LIKE ' . $list->quote('%' . $filterText . '%') - . ' OR `user` IN (SELECT `id` FROM `users` WHERE `name` LIKE ' . $list->quote('%' . $filterText . '%') . ')' - . " OR DATE_FORMAT(FROM_UNIXTIME(`date`), '%Y-%m-%d') LIKE " . $list->quote('%' . $filterText . '%') + . '`title` LIKE ' . $list->quote('%' . $payload->filterText . '%') + . ' OR `description` LIKE ' . $list->quote('%' . $payload->filterText . '%') + . ' OR `type` LIKE ' . $list->quote('%' . $payload->filterText . '%') + . ' OR `user` IN (SELECT `id` FROM `users` WHERE `name` LIKE ' . $list->quote('%' . $payload->filterText . '%') . ')' + . " OR DATE_FORMAT(FROM_UNIXTIME(`date`), '%Y-%m-%d') LIKE " . $list->quote('%' . $payload->filterText . '%') . ')'; } - if ($filterJson) { + if ($payload->filterJson) { $db = Db::get(); - $filters = json_decode($filterJson, true) ?? []; + $filters = json_decode($payload->filterJson, true) ?? []; $propertyKey = 'property'; $comparisonKey = 'operator'; @@ -103,8 +96,8 @@ public function __invoke( } } - if ($cid !== null && $ctype !== null) { - $conditions[] = '(cid = ' . $list->quote($cid) . ' AND ctype = ' . $list->quote($ctype) . ')'; + if ($payload->cid !== null && $payload->ctype !== null) { + $conditions[] = '(cid = ' . $list->quote($payload->cid) . ' AND ctype = ' . $list->quote($payload->ctype) . ')'; } if ($conditions !== []) { diff --git a/src/Handler/Element/NoteListPayload.php b/src/Handler/Element/NoteListPayload.php new file mode 100644 index 00000000..29fc02f5 --- /dev/null +++ b/src/Handler/Element/NoteListPayload.php @@ -0,0 +1,59 @@ +request->has('data')) { + $data = json_decode($request->request->getString('data'), true) ?? []; + + return new static( + hasData: true, + id: (int) ($data['id'] ?? 0), + ); + } + + return new static( + hasData: false, + offset: $request->request->getInt('start', 0), + limit: $request->request->getInt('limit') ?: null, + sortingSettings: QueryParams::extractSortingSettings($request->request->all()), + filterText: $request->request->get('filterText'), + filterJson: $request->request->get('filter'), + cid: $request->request->has('cid') ? $request->request->getString('cid') : null, + ctype: $request->request->has('ctype') ? $request->request->getString('ctype') : null, + ); + } +} diff --git a/src/Handler/Email/BlocklistPayload.php b/src/Handler/Email/BlocklistPayload.php new file mode 100644 index 00000000..9d9c1982 --- /dev/null +++ b/src/Handler/Email/BlocklistPayload.php @@ -0,0 +1,62 @@ +request->has('data')) { + $data = json_decode($request->request->getString('data'), true) ?? []; + + if (is_array($data)) { + foreach ($data as $key => &$value) { + if (is_string($value)) { + if ($key === 'address') { + $value = filter_var($value, FILTER_SANITIZE_EMAIL); + } + $value = trim($value); + } + } + unset($value); + } + + return new static(hasData: true, data: $data); + } + + return new static( + hasData: false, + limit: $request->request->getInt('limit', 50), + offset: $request->request->getInt('start', 0), + sortingSettings: QueryParams::extractSortingSettings($request->request->all()), + filter: $request->request->has('filter') ? $request->request->getString('filter') : null, + ); + } +} diff --git a/src/Handler/Email/CreateBlocklistEntryHandler.php b/src/Handler/Email/CreateBlocklistEntryHandler.php index 62ab0345..4d0726db 100644 --- a/src/Handler/Email/CreateBlocklistEntryHandler.php +++ b/src/Handler/Email/CreateBlocklistEntryHandler.php @@ -21,8 +21,9 @@ final class CreateBlocklistEntryHandler { - public function __invoke(array $data): array + public function __invoke(BlocklistPayload $payload): array { + $data = $payload->data; unset($data['id']); $address = new Tool\Email\Blocklist(); diff --git a/src/Handler/Email/DeleteBlocklistEntryHandler.php b/src/Handler/Email/DeleteBlocklistEntryHandler.php index 552f78ec..dc632cc4 100644 --- a/src/Handler/Email/DeleteBlocklistEntryHandler.php +++ b/src/Handler/Email/DeleteBlocklistEntryHandler.php @@ -21,9 +21,9 @@ final class DeleteBlocklistEntryHandler { - public function __invoke(string $address): void + public function __invoke(BlocklistPayload $payload): void { - $entry = Tool\Email\Blocklist::getByAddress($address); + $entry = Tool\Email\Blocklist::getByAddress($payload->data['address']); $entry->delete(); } } diff --git a/src/Handler/Email/GetBlocklistHandler.php b/src/Handler/Email/GetBlocklistHandler.php index 173d2275..8e42aacd 100644 --- a/src/Handler/Email/GetBlocklistHandler.php +++ b/src/Handler/Email/GetBlocklistHandler.php @@ -17,29 +17,24 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Email; -use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Model\Tool; final class GetBlocklistHandler { - public function __invoke( - int $limit, - int $offset, - array $sortingSettings, - ?string $filter, - ): GetBlocklistResult { + public function __invoke(BlocklistPayload $payload): GetBlocklistResult + { $list = new Tool\Email\Blocklist\Listing(); - $list->setLimit($limit); - $list->setOffset($offset); + $list->setLimit($payload->limit); + $list->setOffset($payload->offset); - if ($sortingSettings['orderKey']) { - $list->setOrderKey($sortingSettings['orderKey']); - $list->setOrder($sortingSettings['order']); + if ($payload->sortingSettings['orderKey']) { + $list->setOrderKey($payload->sortingSettings['orderKey']); + $list->setOrder($payload->sortingSettings['order']); } - if ($filter !== null) { - $list->setCondition('`address` LIKE ' . $list->quote('%' . $filter . '%')); + if ($payload->filter !== null) { + $list->setCondition('`address` LIKE ' . $list->quote('%' . $payload->filter . '%')); } $data = $list->load(); diff --git a/src/Handler/Email/UpdateBlocklistEntryHandler.php b/src/Handler/Email/UpdateBlocklistEntryHandler.php index c67038b3..00272d9c 100644 --- a/src/Handler/Email/UpdateBlocklistEntryHandler.php +++ b/src/Handler/Email/UpdateBlocklistEntryHandler.php @@ -21,10 +21,10 @@ final class UpdateBlocklistEntryHandler { - public function __invoke(array $data): array + public function __invoke(BlocklistPayload $payload): array { - $address = Tool\Email\Blocklist::getByAddress($data['address']); - $address->setValues($data); + $address = Tool\Email\Blocklist::getByAddress($payload->data['address']); + $address->setValues($payload->data); $address->save(); return $address->getObjectVars(); diff --git a/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php b/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php index 8d15d1b6..eff75ba6 100644 --- a/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php +++ b/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php @@ -21,9 +21,9 @@ final class DeleteRecyclebinItemHandler { - public function __invoke(int $id): void + public function __invoke(RecyclebinPayload $payload): void { - $item = Recyclebin\Item::getById($id); + $item = Recyclebin\Item::getById($payload->id); if ($item) { $item->delete(); } diff --git a/src/Handler/Recyclebin/ListRecyclebinHandler.php b/src/Handler/Recyclebin/ListRecyclebinHandler.php index 9e9ec778..7c669235 100644 --- a/src/Handler/Recyclebin/ListRecyclebinHandler.php +++ b/src/Handler/Recyclebin/ListRecyclebinHandler.php @@ -21,29 +21,23 @@ final class ListRecyclebinHandler { - public function __invoke( - int $limit, - int $offset, - string $orderKey, - string $order, - ?string $filterFullText, - array $filters, - ): ListRecyclebinResult { + public function __invoke(RecyclebinPayload $payload): ListRecyclebinResult + { $db = \OpenDxp\Db::get(); $list = new Recyclebin\Item\Listing(); - $list->setLimit($limit); - $list->setOffset($offset); - $list->setOrderKey($orderKey); - $list->setOrder($order); + $list->setLimit($payload->limit); + $list->setOffset($payload->offset); + $list->setOrderKey($payload->orderKey); + $list->setOrder($payload->order); $conditionFilters = []; - if ($filterFullText) { - $conditionFilters[] = '`path` LIKE ' . $list->quote('%' . $list->escapeLike($filterFullText) . '%'); + if ($payload->filterFullText) { + $conditionFilters[] = '`path` LIKE ' . $list->quote('%' . $list->escapeLike($payload->filterFullText) . '%'); } - foreach ($filters as $filter) { + foreach ($payload->filters as $filter) { $operator = '='; $filterField = $filter['property']; diff --git a/src/Handler/Recyclebin/RecyclebinPayload.php b/src/Handler/Recyclebin/RecyclebinPayload.php new file mode 100644 index 00000000..42dad6e0 --- /dev/null +++ b/src/Handler/Recyclebin/RecyclebinPayload.php @@ -0,0 +1,59 @@ +request->has('data')) { + return new static( + hasData: true, + id: QueryParams::getRecordIdForGridRequest($request->request->getString('data')), + ); + } + + $sortingSettings = QueryParams::extractSortingSettings($request->request->all()); + $orderKey = $sortingSettings['orderKey'] ?: 'date'; + $order = $sortingSettings['orderKey'] ? $sortingSettings['order'] : 'DESC'; + + return new static( + hasData: false, + limit: $request->request->getInt('limit', 50), + offset: $request->request->getInt('start', 0), + orderKey: $orderKey, + order: $order, + filterFullText: $request->request->get('filterFullText') ?: null, + filters: json_decode($request->request->getString('filter', '[]'), true) ?? [], + ); + } +} diff --git a/src/Handler/Settings/CreatePredefinedMetadataHandler.php b/src/Handler/Settings/CreatePredefinedMetadataHandler.php index 78e58387..4a26f5b3 100644 --- a/src/Handler/Settings/CreatePredefinedMetadataHandler.php +++ b/src/Handler/Settings/CreatePredefinedMetadataHandler.php @@ -23,8 +23,11 @@ final class CreatePredefinedMetadataHandler { - public function __invoke(array $data): CreatePredefinedMetadataResult + public function __invoke(PredefinedMetadataPayload $payload): CreatePredefinedMetadataResult { + $data = $payload->data; + unset($data['id']); + if (!(new Metadata\Predefined())->isWriteable()) { throw new ConfigWriteException(); } diff --git a/src/Handler/Settings/CreatePredefinedPropertyHandler.php b/src/Handler/Settings/CreatePredefinedPropertyHandler.php index 7a47ef13..734b2dd9 100644 --- a/src/Handler/Settings/CreatePredefinedPropertyHandler.php +++ b/src/Handler/Settings/CreatePredefinedPropertyHandler.php @@ -22,8 +22,11 @@ final class CreatePredefinedPropertyHandler { - public function __invoke(array $data): CreatePredefinedPropertyResult + public function __invoke(PredefinedPropertyPayload $payload): CreatePredefinedPropertyResult { + $data = $payload->data; + unset($data['id']); + if (!(new Property\Predefined())->isWriteable()) { throw new ConfigWriteException(); } diff --git a/src/Handler/Settings/CreateWebsiteSettingHandler.php b/src/Handler/Settings/CreateWebsiteSettingHandler.php index e06cb293..38608b56 100644 --- a/src/Handler/Settings/CreateWebsiteSettingHandler.php +++ b/src/Handler/Settings/CreateWebsiteSettingHandler.php @@ -21,8 +21,11 @@ final class CreateWebsiteSettingHandler { - public function __invoke(array $data): CreateWebsiteSettingResult + public function __invoke(WebsiteSettingPayload $payload): CreateWebsiteSettingResult { + $data = $payload->data; + unset($data['id']); + $setting = new WebsiteSetting(); $setting->setValues($data); $setting->save(); diff --git a/src/Handler/Settings/DeletePredefinedMetadataHandler.php b/src/Handler/Settings/DeletePredefinedMetadataHandler.php index c1dce578..23d06a0f 100644 --- a/src/Handler/Settings/DeletePredefinedMetadataHandler.php +++ b/src/Handler/Settings/DeletePredefinedMetadataHandler.php @@ -22,9 +22,9 @@ final class DeletePredefinedMetadataHandler { - public function __invoke(string $id): void + public function __invoke(PredefinedMetadataPayload $payload): void { - $metadata = Metadata\Predefined::getById($id); + $metadata = Metadata\Predefined::getById((string) $payload->data['id']); if (!$metadata->isWriteable()) { throw new ConfigWriteException(); diff --git a/src/Handler/Settings/DeletePredefinedPropertyHandler.php b/src/Handler/Settings/DeletePredefinedPropertyHandler.php index 094ddf6e..f1e3ebe1 100644 --- a/src/Handler/Settings/DeletePredefinedPropertyHandler.php +++ b/src/Handler/Settings/DeletePredefinedPropertyHandler.php @@ -22,9 +22,9 @@ final class DeletePredefinedPropertyHandler { - public function __invoke(string $id): void + public function __invoke(PredefinedPropertyPayload $payload): void { - $property = Property\Predefined::getById($id); + $property = Property\Predefined::getById((string) $payload->data['id']); if (!$property->isWriteable()) { throw new ConfigWriteException(); diff --git a/src/Handler/Settings/DeleteWebsiteSettingHandler.php b/src/Handler/Settings/DeleteWebsiteSettingHandler.php index 4c73cea9..fd204112 100644 --- a/src/Handler/Settings/DeleteWebsiteSettingHandler.php +++ b/src/Handler/Settings/DeleteWebsiteSettingHandler.php @@ -21,9 +21,9 @@ final class DeleteWebsiteSettingHandler { - public function __invoke(int $id): void + public function __invoke(WebsiteSettingPayload $payload): void { - $setting = WebsiteSetting::getById($id); + $setting = WebsiteSetting::getById((int) $payload->data['id']); if (!$setting instanceof WebsiteSetting) { return; diff --git a/src/Handler/Settings/GetPredefinedMetadataListHandler.php b/src/Handler/Settings/GetPredefinedMetadataListHandler.php index d71ebbbf..f36c36df 100644 --- a/src/Handler/Settings/GetPredefinedMetadataListHandler.php +++ b/src/Handler/Settings/GetPredefinedMetadataListHandler.php @@ -21,14 +21,14 @@ final class GetPredefinedMetadataListHandler { - public function __invoke(?string $filter): GetPredefinedMetadataListResult + public function __invoke(PredefinedMetadataPayload $payload): GetPredefinedMetadataListResult { $list = new Metadata\Predefined\Listing(); - if ($filter) { - $list->setFilter(function (Metadata\Predefined $predefined) use ($filter) { + if ($payload->filter) { + $list->setFilter(function (Metadata\Predefined $predefined) use ($payload) { foreach ($predefined->getObjectVars() as $value) { - if (stripos((string) $value, $filter) !== false) { + if (stripos((string) $value, $payload->filter) !== false) { return true; } } diff --git a/src/Handler/Settings/GetPredefinedPropertiesListHandler.php b/src/Handler/Settings/GetPredefinedPropertiesListHandler.php index 0e148642..d80e2650 100644 --- a/src/Handler/Settings/GetPredefinedPropertiesListHandler.php +++ b/src/Handler/Settings/GetPredefinedPropertiesListHandler.php @@ -21,18 +21,18 @@ final class GetPredefinedPropertiesListHandler { - public function __invoke(?string $filter): GetPredefinedPropertiesListResult + public function __invoke(PredefinedPropertyPayload $payload): GetPredefinedPropertiesListResult { $list = new Property\Predefined\Listing(); - if ($filter) { - $list->setFilter(function (Property\Predefined $predefined) use ($filter) { + if ($payload->filter) { + $list->setFilter(function (Property\Predefined $predefined) use ($payload) { foreach ($predefined->getObjectVars() as $value) { if ($value) { $cellValues = is_array($value) ? $value : [$value]; foreach ($cellValues as $cellValue) { - if (stripos((string) $cellValue, $filter) !== false) { + if (stripos((string) $cellValue, $payload->filter) !== false) { return true; } } diff --git a/src/Handler/Settings/GetWebsiteSettingsListHandler.php b/src/Handler/Settings/GetWebsiteSettingsListHandler.php index 18ec4166..e3065bd8 100644 --- a/src/Handler/Settings/GetWebsiteSettingsListHandler.php +++ b/src/Handler/Settings/GetWebsiteSettingsListHandler.php @@ -22,27 +22,22 @@ final class GetWebsiteSettingsListHandler { - public function __invoke( - int $limit, - int $offset, - ?string $orderKey, - ?string $order, - ?string $filter, - ): GetWebsiteSettingsListResult { + public function __invoke(WebsiteSettingPayload $payload): GetWebsiteSettingsListResult + { $list = new WebsiteSetting\Listing(); - $list->setLimit($limit); - $list->setOffset($offset); + $list->setLimit($payload->limit); + $list->setOffset($payload->offset); - if ($orderKey) { - $list->setOrderKey($orderKey); - $list->setOrder($order); + if ($payload->orderKey) { + $list->setOrderKey($payload->orderKey); + $list->setOrder($payload->order); } else { $list->setOrderKey('name'); $list->setOrder('asc'); } - if ($filter) { - $list->setCondition('`name` LIKE ' . $list->quote('%' . $filter . '%')); + if ($payload->filter) { + $list->setCondition('`name` LIKE ' . $list->quote('%' . $payload->filter . '%')); } $totalCount = $list->getTotalCount(); diff --git a/src/Handler/Settings/PredefinedMetadataPayload.php b/src/Handler/Settings/PredefinedMetadataPayload.php new file mode 100644 index 00000000..44c511dd --- /dev/null +++ b/src/Handler/Settings/PredefinedMetadataPayload.php @@ -0,0 +1,44 @@ +request->has('data')) { + return new static( + hasData: true, + data: json_decode($request->request->getString('data'), true) ?? [], + ); + } + + return new static( + hasData: false, + filter: $request->request->get('filter'), + ); + } +} diff --git a/src/Handler/Settings/PredefinedPropertyPayload.php b/src/Handler/Settings/PredefinedPropertyPayload.php new file mode 100644 index 00000000..eeba4688 --- /dev/null +++ b/src/Handler/Settings/PredefinedPropertyPayload.php @@ -0,0 +1,44 @@ +request->has('data')) { + return new static( + hasData: true, + data: json_decode($request->request->getString('data'), true) ?? [], + ); + } + + return new static( + hasData: false, + filter: $request->request->get('filter'), + ); + } +} diff --git a/src/Handler/Settings/UpdatePredefinedMetadataHandler.php b/src/Handler/Settings/UpdatePredefinedMetadataHandler.php index 6190ade9..07957edb 100644 --- a/src/Handler/Settings/UpdatePredefinedMetadataHandler.php +++ b/src/Handler/Settings/UpdatePredefinedMetadataHandler.php @@ -23,8 +23,9 @@ final class UpdatePredefinedMetadataHandler { - public function __invoke(array $data): UpdatePredefinedMetadataResult + public function __invoke(PredefinedMetadataPayload $payload): UpdatePredefinedMetadataResult { + $data = $payload->data; $metadata = Metadata\Predefined::getById($data['id']); if (!$metadata->isWriteable()) { diff --git a/src/Handler/Settings/UpdatePredefinedPropertyHandler.php b/src/Handler/Settings/UpdatePredefinedPropertyHandler.php index cbb8749a..923a6a36 100644 --- a/src/Handler/Settings/UpdatePredefinedPropertyHandler.php +++ b/src/Handler/Settings/UpdatePredefinedPropertyHandler.php @@ -22,8 +22,9 @@ final class UpdatePredefinedPropertyHandler { - public function __invoke(array $data): UpdatePredefinedPropertyResult + public function __invoke(PredefinedPropertyPayload $payload): UpdatePredefinedPropertyResult { + $data = $payload->data; $property = Property\Predefined::getById($data['id']); if (!$property->isWriteable()) { diff --git a/src/Handler/Settings/UpdateWebsiteSettingHandler.php b/src/Handler/Settings/UpdateWebsiteSettingHandler.php index 8d3b4db2..a4faa651 100644 --- a/src/Handler/Settings/UpdateWebsiteSettingHandler.php +++ b/src/Handler/Settings/UpdateWebsiteSettingHandler.php @@ -23,8 +23,9 @@ final class UpdateWebsiteSettingHandler { - public function __invoke(array $data): UpdateWebsiteSettingResult + public function __invoke(WebsiteSettingPayload $payload): UpdateWebsiteSettingResult { + $data = $payload->data; $setting = WebsiteSetting::getById($data['id']); if (!$setting instanceof WebsiteSetting) { diff --git a/src/Handler/Settings/WebsiteSettingPayload.php b/src/Handler/Settings/WebsiteSettingPayload.php new file mode 100644 index 00000000..86bcb333 --- /dev/null +++ b/src/Handler/Settings/WebsiteSettingPayload.php @@ -0,0 +1,63 @@ +request->has('data')) { + $data = json_decode($request->request->getString('data'), true) ?? []; + + if (is_array($data)) { + foreach ($data as &$value) { + if (is_string($value)) { + $value = trim($value); + } + } + unset($value); + } + + return new static(hasData: true, data: $data); + } + + $sortingSettings = QueryParams::extractSortingSettings([...$request->request->all(), ...$request->query->all()]); + + return new static( + hasData: false, + limit: $request->request->getInt('limit', 50), + offset: $request->request->getInt('start', 0), + orderKey: $sortingSettings['orderKey'] ?: null, + order: $sortingSettings['order'] ?? null, + filter: $request->request->has('filter') ? $request->request->getString('filter') : null, + ); + } +} diff --git a/src/Handler/Translation/CreateTranslationHandler.php b/src/Handler/Translation/CreateTranslationHandler.php index 1d6aff2c..1c8c6014 100644 --- a/src/Handler/Translation/CreateTranslationHandler.php +++ b/src/Handler/Translation/CreateTranslationHandler.php @@ -26,18 +26,19 @@ final class CreateTranslationHandler { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke(array $data, string $domain): CreateTranslationResult + public function __invoke(TranslationPayload $payload): CreateTranslationResult { - $admin = $domain === Translation::DOMAIN_ADMIN; + $data = $payload->data; + $admin = $payload->domain === Translation::DOMAIN_ADMIN; $validLanguages = $admin ? Tool\Admin::getLanguages() : $this->userContext->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); - if (Translation::getByKey($data['key'], $domain)) { + if (Translation::getByKey($data['key'], $payload->domain)) { throw new BadRequestHttpException('identifier_already_exists'); } $t = new Translation(); - $t->setDomain($domain); + $t->setDomain($payload->domain); $t->setKey($data['key']); $t->setCreationDate(time()); $t->setModificationDate(time()); diff --git a/src/Handler/Translation/DeleteTranslationHandler.php b/src/Handler/Translation/DeleteTranslationHandler.php index b6a1feb6..f01969ff 100644 --- a/src/Handler/Translation/DeleteTranslationHandler.php +++ b/src/Handler/Translation/DeleteTranslationHandler.php @@ -21,9 +21,9 @@ final class DeleteTranslationHandler { - public function __invoke(string $key, string $domain): void + public function __invoke(TranslationPayload $payload): void { - $t = Translation::getByKey($key, $domain); + $t = Translation::getByKey($payload->data['key'], $payload->domain); if ($t instanceof Translation) { $t->delete(); } diff --git a/src/Handler/Translation/GetTranslationsHandler.php b/src/Handler/Translation/GetTranslationsHandler.php index e2595db0..516589fa 100644 --- a/src/Handler/Translation/GetTranslationsHandler.php +++ b/src/Handler/Translation/GetTranslationsHandler.php @@ -28,29 +28,23 @@ final class GetTranslationsHandler public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke( - string $domain, - array $requestParams, - int $limit, - int $offset, - ?string $filter, - ?string $searchString, - ): GetTranslationsResult { - $admin = $domain === Translation::DOMAIN_ADMIN; + public function __invoke(TranslationPayload $payload): GetTranslationsResult + { + $admin = $payload->domain === Translation::DOMAIN_ADMIN; $validLanguages = $admin ? Tool\Admin::getLanguages() : $this->userContext->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); $translation = new Translation(); - $translation->setDomain($domain); + $translation->setDomain($payload->domain); $tableName = $translation->getDao()->getDatabaseTableName(); $list = new Translation\Listing(); - $list->setDomain($domain); + $list->setDomain($payload->domain); $list->setOrder('asc'); $list->setOrderKey($tableName . '.key', false); $list->setLanguages($validLanguages); - $sortingSettings = QueryParams::extractSortingSettings($requestParams); + $sortingSettings = QueryParams::extractSortingSettings($payload->requestParams); $joins = []; @@ -69,12 +63,12 @@ public function __invoke( $list->setOrder($sortingSettings['order']); } - $list->setLimit($limit); - $list->setOffset($offset); + $list->setLimit($payload->limit); + $list->setOffset($payload->offset); $filterParameters = [ - 'filter' => $filter, - 'searchString' => $searchString, + 'filter' => $payload->filter, + 'searchString' => $payload->searchString, ]; $conditions = $this->getGridFilterCondition($filterParameters, $tableName, false, $validLanguages); @@ -92,7 +86,7 @@ public function __invoke( $translations = []; foreach ($list->getTranslations() as $t) { - if ($searchString && !strpos($searchString, (string) $t->getKey()) && !$t = Translation::getByKey($t->getKey(), $domain)) { + if ($payload->searchString && !strpos($payload->searchString, (string) $t->getKey()) && !$t = Translation::getByKey($t->getKey(), $payload->domain)) { continue; } diff --git a/src/Handler/Translation/TranslationPayload.php b/src/Handler/Translation/TranslationPayload.php new file mode 100644 index 00000000..dd9271aa --- /dev/null +++ b/src/Handler/Translation/TranslationPayload.php @@ -0,0 +1,59 @@ +request->getString('domain', Translation::DOMAIN_DEFAULT); + $hasData = $request->request->has('data'); + + if ($hasData) { + return new static( + domain: $domain, + hasData: true, + data: json_decode($request->request->getString('data'), true) ?? [], + ); + } + + return new static( + domain: $domain, + hasData: false, + requestParams: [...$request->request->all(), ...$request->query->all()], + limit: $request->request->getInt('limit', 50), + offset: $request->request->getInt('start', 0), + filter: $request->request->has('filter') ? $request->request->getString('filter') : null, + searchString: $request->request->has('searchString') ? $request->request->getString('searchString') : null, + ); + } +} diff --git a/src/Handler/Translation/UpdateTranslationHandler.php b/src/Handler/Translation/UpdateTranslationHandler.php index a4266078..a98e9568 100644 --- a/src/Handler/Translation/UpdateTranslationHandler.php +++ b/src/Handler/Translation/UpdateTranslationHandler.php @@ -22,9 +22,11 @@ final class UpdateTranslationHandler { - public function __invoke(array $data, string $domain): UpdateTranslationResult + public function __invoke(TranslationPayload $payload): UpdateTranslationResult { - $t = Translation::getByKey($data['key'], $domain); + $data = $payload->data; + + $t = Translation::getByKey($data['key'], $payload->domain); if (!$t instanceof Translation) { throw new NotFoundHttpException(sprintf('Translation with key "%s" not found.', $data['key'])); } diff --git a/src/Http/ExtJsValueResolver.php b/src/Http/ExtJsValueResolver.php new file mode 100644 index 00000000..4e6e7f39 --- /dev/null +++ b/src/Http/ExtJsValueResolver.php @@ -0,0 +1,35 @@ +getType(); + if (!$type || !is_subclass_of($type, ExtJsPayloadInterface::class, true)) { + return []; + } + + return [$type::fromRequest($request)]; + } +} \ No newline at end of file diff --git a/src/Normalizer/Document/DocumentMetaNormalizer.php b/src/Normalizer/Document/DocumentMetaNormalizer.php index a7780f0d..b1ed9814 100644 --- a/src/Normalizer/Document/DocumentMetaNormalizer.php +++ b/src/Normalizer/Document/DocumentMetaNormalizer.php @@ -17,13 +17,13 @@ namespace OpenDxp\Bundle\AdminBundle\Normalizer\Document; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailData\GetEmailDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData\GetFolderDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData\GetDocumentDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkData\GetHardlinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkData\GetLinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData\GetSnippetDataHandler; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface; use OpenDxp\Model\Document; use OpenDxp\Model\Element\ElementInterface; diff --git a/src/Normalizer/Document/DraftNormalizer.php b/src/Normalizer/Document/DraftNormalizer.php index dc4b5197..7eb66345 100644 --- a/src/Normalizer/Document/DraftNormalizer.php +++ b/src/Normalizer/Document/DraftNormalizer.php @@ -17,9 +17,9 @@ namespace OpenDxp\Bundle\AdminBundle\Normalizer\Document; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailData\GetEmailDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData\GetSnippetDataHandler; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface; use OpenDxp\Model\Document; use OpenDxp\Model\Element\ElementInterface; diff --git a/src/Normalizer/Document/PropertiesNormalizer.php b/src/Normalizer/Document/PropertiesNormalizer.php index 73cd2d59..eedcd816 100644 --- a/src/Normalizer/Document/PropertiesNormalizer.php +++ b/src/Normalizer/Document/PropertiesNormalizer.php @@ -17,13 +17,13 @@ namespace OpenDxp\Bundle\AdminBundle\Normalizer\Document; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailData\GetEmailDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData\GetFolderDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData\GetDocumentDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkData\GetHardlinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkData\GetLinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData\GetSnippetDataHandler; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface; use OpenDxp\Model\Document; use OpenDxp\Model\Element\ElementInterface; diff --git a/src/Normalizer/Document/TranslationNormalizer.php b/src/Normalizer/Document/TranslationNormalizer.php index fc410c9c..dfd149dc 100644 --- a/src/Normalizer/Document/TranslationNormalizer.php +++ b/src/Normalizer/Document/TranslationNormalizer.php @@ -17,13 +17,13 @@ namespace OpenDxp\Bundle\AdminBundle\Normalizer\Document; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailData\GetEmailDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData\GetFolderDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData\GetDocumentDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkData\GetHardlinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkData\GetLinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData\GetSnippetDataHandler; use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface; use OpenDxp\Model\Document; use OpenDxp\Model\Element\ElementInterface; diff --git a/src/Normalizer/Element/UserNamesNormalizer.php b/src/Normalizer/Element/UserNamesNormalizer.php index 3ee86121..54c7121d 100644 --- a/src/Normalizer/Element/UserNamesNormalizer.php +++ b/src/Normalizer/Element/UserNamesNormalizer.php @@ -18,13 +18,13 @@ namespace OpenDxp\Bundle\AdminBundle\Normalizer\Element; use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Email\GetEmailData\GetEmailDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData\GetFolderDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData\GetDocumentDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkData\GetHardlinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkData\GetLinkDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData\GetSnippetDataHandler; use OpenDxp\Model\Asset; use OpenDxp\Model\Document; use OpenDxp\Model\Element\ElementInterface; diff --git a/src/Payload/Common/EmptyPayload.php b/src/Payload/Common/EmptyPayload.php new file mode 100644 index 00000000..07c18c16 --- /dev/null +++ b/src/Payload/Common/EmptyPayload.php @@ -0,0 +1,28 @@ +request->getInt('id')); + } +} diff --git a/src/Payload/Common/IdQueryPayload.php b/src/Payload/Common/IdQueryPayload.php new file mode 100644 index 00000000..88b8f352 --- /dev/null +++ b/src/Payload/Common/IdQueryPayload.php @@ -0,0 +1,30 @@ +query->getInt('id')); + } +} diff --git a/src/Payload/Common/StringIdBodyPayload.php b/src/Payload/Common/StringIdBodyPayload.php new file mode 100644 index 00000000..9a30b898 --- /dev/null +++ b/src/Payload/Common/StringIdBodyPayload.php @@ -0,0 +1,30 @@ +request->getString('id')); + } +} diff --git a/src/Payload/DataObject/AddObjectFolderPayload.php b/src/Payload/DataObject/AddObjectFolderPayload.php new file mode 100644 index 00000000..5864cc4b --- /dev/null +++ b/src/Payload/DataObject/AddObjectFolderPayload.php @@ -0,0 +1,37 @@ +request->getInt('parentId'), + key: $request->request->getString('key'), + ); + } +} diff --git a/src/Payload/DataObject/AddObjectPayload.php b/src/Payload/DataObject/AddObjectPayload.php new file mode 100644 index 00000000..227c083a --- /dev/null +++ b/src/Payload/DataObject/AddObjectPayload.php @@ -0,0 +1,45 @@ +request->getString('className'), + classId: $request->request->getString('classId'), + parentId: $request->request->getInt('parentId'), + key: $request->request->getString('key'), + objectType: $request->request->getString('objecttype'), + variantViaTree: (bool) $request->request->get('variantViaTree'), + ); + } +} diff --git a/src/Payload/DataObject/ChangeChildrenSortByPayload.php b/src/Payload/DataObject/ChangeChildrenSortByPayload.php new file mode 100644 index 00000000..f6175786 --- /dev/null +++ b/src/Payload/DataObject/ChangeChildrenSortByPayload.php @@ -0,0 +1,39 @@ +request->getInt('id'), + sortBy: $request->request->getString('sortBy'), + sortOrder: $request->request->getString('childrenSortOrder'), + ); + } +} diff --git a/src/Payload/DataObject/DataObjectGridProxyPayload.php b/src/Payload/DataObject/DataObjectGridProxyPayload.php new file mode 100644 index 00000000..a1b8a925 --- /dev/null +++ b/src/Payload/DataObject/DataObjectGridProxyPayload.php @@ -0,0 +1,45 @@ +request->all(), ...$request->query->all()]; + + if (!empty($allParams['context'])) { + $allParams['context'] = json_decode($allParams['context'], true); + } else { + $allParams['context'] = []; + } + + return new static( + allParams: $allParams, + locale: $request->getLocale(), + ); + } +} diff --git a/src/Payload/DataObject/DeleteDataObjectPayload.php b/src/Payload/DataObject/DeleteDataObjectPayload.php new file mode 100644 index 00000000..09799da6 --- /dev/null +++ b/src/Payload/DataObject/DeleteDataObjectPayload.php @@ -0,0 +1,39 @@ +request->getString('type'), + id: $request->request->getInt('id'), + amount: $request->request->getInt('amount'), + ); + } +} diff --git a/src/Payload/DataObject/GetDataObjectPayload.php b/src/Payload/DataObject/GetDataObjectPayload.php new file mode 100644 index 00000000..02afaeca --- /dev/null +++ b/src/Payload/DataObject/GetDataObjectPayload.php @@ -0,0 +1,36 @@ +query->getInt('id'), + layoutId: is_numeric($request->query->get('layoutId')) ? $request->query->getInt('layoutId') : null, + ); + } +} diff --git a/src/Payload/DataObject/GetSelectOptionsPayload.php b/src/Payload/DataObject/GetSelectOptionsPayload.php new file mode 100644 index 00000000..14985d66 --- /dev/null +++ b/src/Payload/DataObject/GetSelectOptionsPayload.php @@ -0,0 +1,40 @@ +request->getInt('objectId'), + changedData: $request->request->has('changedData') ? json_decode($request->request->getString('changedData'), true) : null, + fieldDefinitionConfig: json_decode($request->request->getString('fieldDefinition'), true) ?? [], + context: json_decode($request->request->getString('context'), true) ?? [], + ); + } +} diff --git a/src/Payload/DataObject/SaveDataObjectFolderPayload.php b/src/Payload/DataObject/SaveDataObjectFolderPayload.php new file mode 100644 index 00000000..f061d181 --- /dev/null +++ b/src/Payload/DataObject/SaveDataObjectFolderPayload.php @@ -0,0 +1,43 @@ +request->has('properties') + ? json_decode($request->request->getString('properties'), true) + : null; + + return new static( + id: $request->request->getInt('id'), + general: json_decode($request->request->getString('general'), true) ?? [], + propertiesData: is_array($propertiesRaw) ? $propertiesRaw : null, + ); + } +} diff --git a/src/Payload/DataObject/TreeGetChildrenByIdPayload.php b/src/Payload/DataObject/TreeGetChildrenByIdPayload.php new file mode 100644 index 00000000..3ed23f6e --- /dev/null +++ b/src/Payload/DataObject/TreeGetChildrenByIdPayload.php @@ -0,0 +1,46 @@ +query->getInt('node'), + filter: $request->query->getString('filter') ?: null, + start: $request->query->getInt('start'), + limit: $request->query->getInt('limit', 100000000), + view: $request->query->getString('view'), + fromPaging: $request->query->getInt('fromPaging'), + allParams: $request->query->all(), + ); + } +} diff --git a/src/Payload/DataObject/UpdateDataObjectPayload.php b/src/Payload/DataObject/UpdateDataObjectPayload.php new file mode 100644 index 00000000..9952bff8 --- /dev/null +++ b/src/Payload/DataObject/UpdateDataObjectPayload.php @@ -0,0 +1,39 @@ +request->getString('id'), true); + + return new static( + ids: is_array($rawIds) ? array_map('intval', $rawIds) : [(int) ($rawIds ?? 0)], + values: json_decode($request->request->getString('values'), true) ?? [], + ); + } +} diff --git a/src/Payload/Document/EmailPayload.php b/src/Payload/ExtJsPayloadInterface.php similarity index 59% rename from src/Payload/Document/EmailPayload.php rename to src/Payload/ExtJsPayloadInterface.php index 188890b2..947eaf64 100644 --- a/src/Payload/Document/EmailPayload.php +++ b/src/Payload/ExtJsPayloadInterface.php @@ -8,13 +8,17 @@ * Full copyright and license information is available in * LICENSE.md which is distributed with this source code. * - * @copyright Copyright (c) Pimcore GmbH (https://pimcore.com) - * @copyright Modification Copyright (c) OpenDXP (https://www.opendxp.io) + * @copyright Copyright (c) OpenDXP (https://www.opendxp.io) * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\Document; +namespace OpenDxp\Bundle\AdminBundle\Payload; -final class EmailPayload extends PagePayload {} +use Symfony\Component\HttpFoundation\Request; + +interface ExtJsPayloadInterface +{ + public static function fromRequest(Request $request): static; +} \ No newline at end of file diff --git a/src/Service/Asset/AssetPayloadMapper.php b/src/Service/Asset/AssetPayloadMapper.php index 10a5e43e..63772335 100644 --- a/src/Service/Asset/AssetPayloadMapper.php +++ b/src/Service/Asset/AssetPayloadMapper.php @@ -18,7 +18,7 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Payload\Asset\AssetPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset\SaveAssetPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Logger; use OpenDxp\Model; @@ -34,7 +34,7 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function applyPayload(AssetPayload $payload, Asset $asset): void + public function applyPayload(SaveAssetPayload $payload, Asset $asset): void { if ($payload->metadata !== null) { $metadataEvent = new GenericEvent(null, [ diff --git a/src/Service/Asset/AssetPersistenceCoordinator.php b/src/Service/Asset/AssetPersistenceCoordinator.php index 7e2fee96..9c8f3fa8 100644 --- a/src/Service/Asset/AssetPersistenceCoordinator.php +++ b/src/Service/Asset/AssetPersistenceCoordinator.php @@ -16,7 +16,7 @@ namespace OpenDxp\Bundle\AdminBundle\Service\Asset; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAssetResult; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset\SaveAssetResult; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; diff --git a/src/Service/DataObject/DataObjectPayloadMapper.php b/src/Service/DataObject/DataObjectPayloadMapper.php index 76ae284a..3d4cf752 100644 --- a/src/Service/DataObject/DataObjectPayloadMapper.php +++ b/src/Service/DataObject/DataObjectPayloadMapper.php @@ -18,7 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Service\DataObject; use Exception; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject\SaveDataObjectPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Logger; use OpenDxp\Model; @@ -32,7 +32,7 @@ final class DataObjectPayloadMapper { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function applyPayload(DataObjectPayload $payload, DataObject\Concrete $object, DataObject\Concrete $objectFromDatabase): void + public function applyPayload(SaveDataObjectPayload $payload, DataObject\Concrete $object, DataObject\Concrete $objectFromDatabase): void { if ($payload->data !== []) { try { diff --git a/src/Service/DataObject/DataObjectPersistenceCoordinator.php b/src/Service/DataObject/DataObjectPersistenceCoordinator.php index 7a0a84ab..c28264ba 100644 --- a/src/Service/DataObject/DataObjectPersistenceCoordinator.php +++ b/src/Service/DataObject/DataObjectPersistenceCoordinator.php @@ -17,7 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Service\DataObject; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectResult; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject\SaveDataObjectResult; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Model\DataObject; diff --git a/src/Service/Document/DocumentPayloadMapper.php b/src/Service/Document/DocumentPayloadMapper.php index 2127c7f9..b76b8c75 100644 --- a/src/Service/Document/DocumentPayloadMapper.php +++ b/src/Service/Document/DocumentPayloadMapper.php @@ -18,10 +18,10 @@ namespace OpenDxp\Bundle\AdminBundle\Service\Document; use Exception; -use OpenDxp\Bundle\AdminBundle\Payload\Document\FolderPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Document\HardlinkPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Document\LinkPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Document\PagePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\SaveFolder\SaveFolderPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\SaveHardlink\SaveHardlinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Link\SaveLink\SaveLinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\PagePayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\PersonalizationBundle\Model\Document\Targeting\TargetingDocumentInterface; use OpenDxp\Logger; @@ -56,21 +56,21 @@ public function applyPagePayload(PagePayload $payload, Document\PageSnippet $doc $this->applyScheduler($payload->scheduler, $document); } - public function applyLinkPayload(LinkPayload $payload, Link $document): void + public function applyLinkPayload(SaveLinkPayload $payload, Link $document): void { $this->applyLinkData($payload->data, $document); $this->applyProperties($payload->properties, $document); $this->applyScheduler($payload->scheduler, $document); } - public function applyHardlinkPayload(HardlinkPayload $payload, Hardlink $document): void + public function applyHardlinkPayload(SaveHardlinkPayload $payload, Hardlink $document): void { $this->applyHardlinkData($payload->data, $document); $this->applyProperties($payload->properties, $document); $this->applyScheduler($payload->scheduler, $document); } - public function applyFolderPayload(FolderPayload $payload, Folder $document): void + public function applyFolderPayload(SaveFolderPayload $payload, Folder $document): void { $this->applyProperties($payload->properties, $document); } diff --git a/src/Service/Grid/DataObjectGridColumnConfigResolver.php b/src/Service/Grid/DataObjectGridColumnConfigResolver.php index e730f25e..3b8f4e65 100644 --- a/src/Service/Grid/DataObjectGridColumnConfigResolver.php +++ b/src/Service/Grid/DataObjectGridColumnConfigResolver.php @@ -26,8 +26,6 @@ use OpenDxp\Model\DataObject; use OpenDxp\Model\User; use OpenDxp\Tool; -use OpenDxp\Tool\Session; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; final class DataObjectGridColumnConfigResolver @@ -40,7 +38,7 @@ public function __construct( private readonly AdminUserContextInterface $userContext, ) {} - public function resolve(Request $request, array $params, bool $isDelete = false): GridColumnConfigResult + public function resolve(string $locale, array $params, ?AttributeBagInterface $helperColumnsBag = null, bool $isDelete = false): GridColumnConfigResult { $user = $this->userContext->getAdminUser(); $class = null; @@ -183,7 +181,9 @@ public function resolve(Request $request, array $params, bool $isDelete = false) } } } elseif (DataObject\Service::isHelperGridColumnConfig($key)) { - $calculatedColumnConfig = $this->getCalculatedColumnConfig($request, $sc); + $calculatedColumnConfig = $helperColumnsBag !== null + ? $this->getCalculatedColumnConfig($helperColumnsBag, $sc) + : null; if ($calculatedColumnConfig) { $availableFields[] = $calculatedColumnConfig; } @@ -214,7 +214,7 @@ public function resolve(Request $request, array $params, bool $isDelete = false) usort($availableFields, static fn ($a, $b) => $a['position'] <=> $b['position']); $frontendLanguages = Tool\Admin::reorderWebsiteLanguages(Tool\Admin::getCurrentUser(), $this->config['general']['valid_languages']); - $language = $frontendLanguages ? $frontendLanguages[0] : $request->getLocale(); + $language = $frontendLanguages ? $frontendLanguages[0] : $locale; if (!Tool::isValidLanguage($language)) { $validLanguages = Tool::getValidLanguages(); $language = $validLanguages[0]; @@ -368,34 +368,32 @@ private function injectCustomLayoutValues(array $fieldConfig, array $savedColumn return $fieldConfig; } - private function getCalculatedColumnConfig(Request $request, array $config): mixed + private function getCalculatedColumnConfig(AttributeBagInterface $helperColumnsBag, array $config): mixed { try { - return Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($config) { - $existingKey = $config['fieldConfig']['key']; - $calculatedColumnConfig['key'] = $existingKey; - $calculatedColumnConfig['position'] = $config['position']; - $calculatedColumnConfig['isOperator'] = true; - $calculatedColumnConfig['attributes'] = $config['fieldConfig']['attributes']; - $calculatedColumnConfig['width'] = $config['width']; - $calculatedColumnConfig['locked'] = $config['locked']; - - $existingColumns = $session->get('helpercolumns', []); - - if (isset($existingColumns[$existingKey])) { - return $calculatedColumnConfig; - } - - $newKey = '#' . uniqid('', false); - $calculatedColumnConfig['key'] = $newKey; + $existingKey = $config['fieldConfig']['key']; + $calculatedColumnConfig['key'] = $existingKey; + $calculatedColumnConfig['position'] = $config['position']; + $calculatedColumnConfig['isOperator'] = true; + $calculatedColumnConfig['attributes'] = $config['fieldConfig']['attributes']; + $calculatedColumnConfig['width'] = $config['width']; + $calculatedColumnConfig['locked'] = $config['locked']; - $phpConfig = json_encode($config['fieldConfig']); - $phpConfig = json_decode($phpConfig); - $helperColumns = [$newKey => $phpConfig, ...$existingColumns]; - $session->set('helpercolumns', $helperColumns); + $existingColumns = $helperColumnsBag->get('helpercolumns', []); + if (isset($existingColumns[$existingKey])) { return $calculatedColumnConfig; - }, 'opendxp_gridconfig'); + } + + $newKey = '#' . uniqid('', false); + $calculatedColumnConfig['key'] = $newKey; + + $phpConfig = json_encode($config['fieldConfig']); + $phpConfig = json_decode($phpConfig); + $helperColumns = [$newKey => $phpConfig, ...$existingColumns]; + $helperColumnsBag->set('helpercolumns', $helperColumns); + + return $calculatedColumnConfig; } catch (Exception $e) { Logger::error((string) $e); } From bf60deba1cd30cec6de06d56fe1e49d273e9c0b7 Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Thu, 18 Jun 2026 13:53:59 +0200 Subject: [PATCH 03/10] remove unused admin bundle handler classes and traits (CQRS refactor cleanup) --- config/services.yaml | 26 +- phpstan-baseline.neon | 5 - src/Builder/AdminSettingsAssembler.php | 3 + .../Admin/Asset/AssetController.php | 49 +-- .../Admin/Asset/AssetCopyController.php | 89 +--- .../Admin/Asset/AssetDownloadController.php | 10 +- .../Admin/Asset/AssetEditorController.php | 4 +- .../Admin/Asset/AssetHelperController.php | 62 ++- .../Admin/Asset/AssetMediaController.php | 10 +- .../Admin/Asset/AssetThumbnailController.php | 10 +- .../Admin/Asset/AssetUploadController.php | 8 +- .../Admin/Asset/AssetVersionController.php | 8 +- .../Admin/DataObject/ClassController.php | 102 ++--- .../ClassificationstoreController.php | 100 ++--- .../DataObject/CustomLayoutController.php | 18 +- .../Admin/DataObject/DataObjectController.php | 69 ++-- .../DataObject/DataObjectCopyController.php | 76 +--- .../DataObject/DataObjectHelperController.php | 61 +-- .../DataObjectVersionController.php | 11 +- .../DataObject/FieldCollectionController.php | 16 +- .../DataObject/ObjectBrickController.php | 16 +- .../DataObject/QuantityValueController.php | 12 +- .../Admin/DataObject/VariantsController.php | 4 +- .../Admin/Document/DocumentController.php | 11 +- .../Admin/Document/DocumentControllerBase.php | 11 +- .../Admin/Document/DocumentCopyController.php | 87 +--- src/Controller/Admin/ElementController.php | 391 ++++++++---------- .../Admin/ElementControllerBase.php | 12 +- src/Controller/Admin/EmailController.php | 143 +++---- src/Controller/Admin/IndexController.php | 4 +- src/Controller/Admin/InstallController.php | 19 +- src/Controller/Admin/LoginController.php | 122 +++--- src/Controller/Admin/MiscController.php | 236 +++++------ .../Admin/NotificationController.php | 94 ++--- src/Controller/Admin/PortalController.php | 146 +++---- src/Controller/Admin/RecyclebinController.php | 34 +- .../Admin/Settings/ThumbnailController.php | 33 +- .../Settings/VideoThumbnailController.php | 33 +- src/Controller/Admin/SettingsController.php | 124 +++--- src/Controller/Admin/TagsController.php | 130 +++--- .../Admin/TranslationController.php | 142 +++---- src/Controller/Admin/User/RoleController.php | 26 +- .../Admin/User/UserProfileController.php | 64 ++- src/Controller/Admin/UserController.php | 177 ++++---- src/Controller/Admin/WorkflowController.php | 89 ++-- src/Controller/GDPR/AdminController.php | 15 +- src/Controller/GDPR/AssetController.php | 43 +- src/Controller/GDPR/DataObjectController.php | 49 +-- .../GDPR/OpenDxpUsersController.php | 34 +- src/Controller/GDPR/SentMailController.php | 22 +- .../DataObject/CustomLayoutEnricher.php} | 17 +- .../DataObject/DraftEnricher.php} | 20 +- .../Document/DocumentMetaEnricher.php | 36 ++ src/Enricher/Document/DraftEnricher.php | 40 ++ .../Document/PropertiesEnricher.php} | 15 +- src/Enricher/Document/TranslationEnricher.php | 34 ++ src/Enricher/Element/AdminStyleEnricher.php | 66 +++ .../Element/UserNamesEnricher.php} | 23 +- .../Traits/ControllerTypeTrait.php | 54 --- .../ClearAssetThumbnailHandler.php | 2 +- .../Copy/{ => CopyAsset}/CopyAssetHandler.php | 2 +- .../Asset/Copy/CopyAsset/CopyAssetPayload.php | 25 +- .../Copy/{ => CopyAsset}/CopyAssetResult.php | 2 +- .../Asset/Copy/CopyInfo/CopyInfoHandler.php | 83 ++++ .../Asset/Copy/CopyInfo/CopyInfoPayload.php | 39 ++ .../Asset/Copy/CopyInfo/CopyInfoResult.php | 26 ++ .../Asset/Copy/GetAssetChildIdsHandler.php | 42 -- .../CreateAssetFolderHandler.php | 2 +- .../{ => DeleteAsset}/DeleteAssetHandler.php | 2 +- .../{ => DeleteAsset}/DeleteAssetResult.php | 2 +- .../AddFilesToZipHandler.php | 2 +- .../DownloadAssetHandler.php | 2 +- .../DownloadImageThumbnailHandler.php | 2 +- .../DownloadImageThumbnailResult.php | 2 +- .../{ => DownloadZip}/DownloadZipHandler.php | 2 +- .../{ => DownloadZip}/DownloadZipResult.php | 2 +- .../GetDownloadZipJobsHandler.php | 2 +- .../GetDownloadZipJobsResult.php | 2 +- .../LoadAssetForEditorHandler.php | 3 +- .../SaveImageEditorHandler.php | 2 +- .../GetAssetChildrenHandler.php | 2 +- .../GetAssetChildrenPayload.php | 4 + .../GetAssetChildrenResult.php | 2 +- .../GetAssetDataHandler.php | 41 +- .../{ => GetAssetData}/GetAssetDataResult.php | 2 +- .../Asset/GridProxy/GridProxyHandler.php | 44 ++ .../Asset/GridProxy/GridProxyPayload.php | 39 ++ .../GridProxy/GridProxyResult.php} | 8 +- .../DeleteGridColumnConfigHandler.php | 2 +- .../DeleteGridColumnConfigPayload.php | 4 +- .../DoAssetExportHandler.php | 2 +- .../ExecuteAssetBatchHandler.php | 2 +- .../GetAssetBatchJobsHandler.php | 2 +- .../GetAssetBatchJobsResult.php | 2 +- ...GetAssetMetadataForColumnConfigHandler.php | 2 +- .../GetAssetMetadataForColumnConfigResult.php | 2 +- .../GetExportJobsHandler.php | 2 +- .../GetExportJobsResult.php | 2 +- .../MarkGridConfigFavouriteHandler.php | 2 +- .../MarkGridConfigFavouriteResult.php | 2 +- .../PrepareHelperColumnConfigsHandler.php | 46 +++ .../PrepareHelperColumnConfigsPayload.php | 34 ++ .../PrepareHelperColumnConfigsResult.php} | 12 +- .../SaveGridColumnConfigHandler.php | 2 +- .../SaveGridColumnConfigResult.php | 2 +- .../{ => GetAssetText}/AssetTextResult.php | 2 +- .../GetAssetTextHandler.php | 2 +- .../GetDocumentPreviewHandler.php | 2 +- .../PreviewDocumentResult.php | 2 +- .../GetVideoPreviewHandler.php | 2 +- .../PreviewVideoResult.php | 2 +- .../ServeVideoPreviewHandler.php | 2 +- .../ServeVideoPreviewResult.php | 2 +- .../GetDocumentThumbnailHandler.php | 2 +- .../GetDocumentThumbnailResult.php | 2 +- .../GetFolderContentPreviewHandler.php | 2 +- .../GetFolderContentPreviewResult.php | 2 +- .../FolderThumbnailResult.php | 2 +- .../GetFolderThumbnailHandler.php | 2 +- .../GetImageThumbnailHandler.php | 2 +- .../GetImageThumbnailResult.php | 2 +- .../GetVideoThumbnailHandler.php | 2 +- .../GetVideoThumbnailResult.php | 2 +- .../{ => UpdateAsset}/UpdateAssetHandler.php | 2 +- .../{ => UpdateAsset}/UpdateAssetResult.php | 2 +- .../CheckAssetExistsHandler.php | 2 +- .../{ => ImportZip}/ImportZipHandler.php | 2 +- .../{ => ImportZip}/ImportZipResult.php | 2 +- .../ImportZipFilesHandler.php | 2 +- .../ReplaceAssetHandler.php | 2 +- .../PublishVersionHandler.php | 2 +- .../{ => ShowVersion}/ShowVersionHandler.php | 2 +- .../ShowVersion/ShowVersionPayload.php | 4 +- .../{ => ShowVersion}/ShowVersionResult.php | 2 +- .../{ => AddObject}/AddObjectHandler.php | 3 +- .../AddObject}/AddObjectPayload.php | 2 +- .../{ => AddObject}/AddObjectResult.php | 2 +- .../AddObjectFolderHandler.php | 3 +- .../AddObjectFolderPayload.php | 2 +- .../ChangeChildrenSortByHandler.php | 3 +- .../ChangeChildrenSortByPayload.php | 2 +- .../{ => AddClass}/AddClassHandler.php | 2 +- .../{ => AddClass}/AddClassPayload.php | 2 +- .../{ => AddClass}/AddClassResult.php | 2 +- .../{ => BulkCommit}/BulkCommitHandler.php | 2 +- .../{ => BulkCommit}/BulkCommitPayload.php | 2 +- .../BulkExportPreparePayload.php | 2 +- .../ClassDef/BulkExportPrepareHandler.php | 38 -- .../{ => BulkImport}/BulkImportHandler.php | 18 +- .../{ => BulkImport}/BulkImportPayload.php | 2 +- .../{ => BulkImport}/BulkImportResult.php | 3 +- .../{ => DeleteClass}/DeleteClassHandler.php | 2 +- .../{ => DeleteClass}/DeleteClassPayload.php | 2 +- .../DeleteSelectOptionsHandler.php | 2 +- .../DeleteSelectOptionsPayload.php | 2 +- .../DoBulkExportHandler.php | 2 +- .../{ => DoBulkExport}/DoBulkExportResult.php | 2 +- .../{ => ExportClass}/ExportClassHandler.php | 2 +- .../{ => ExportClass}/ExportClassPayload.php | 2 +- .../{ => ExportClass}/ExportClassResult.php | 2 +- .../GetAssetTypesHandler.php | 2 +- .../GetAssetTypesResult.php | 2 +- .../{ => GetClass}/GetClassHandler.php | 2 +- .../{ => GetClass}/GetClassPayload.php | 2 +- .../{ => GetClass}/GetClassResult.php | 2 +- .../GetClassBulkExportListHandler.php | 2 +- .../GetClassBulkExportListResult.php | 2 +- ...tClassDefinitionForColumnConfigHandler.php | 2 +- ...tClassDefinitionForColumnConfigPayload.php | 2 +- ...etClassDefinitionForColumnConfigResult.php | 2 +- .../GetClassIconsHandler.php | 2 +- .../GetClassIconsPayload.php | 2 +- .../GetClassIconsResult.php | 2 +- .../GetClassTreeHandler.php | 2 +- .../GetClassTreePayload.php | 2 +- .../{ => GetClassTree}/GetClassTreeResult.php | 2 +- .../GetDocumentTypesHandler.php | 2 +- .../GetDocumentTypesResult.php | 2 +- .../GetSelectOptionsHandler.php | 2 +- .../GetSelectOptionsPayload.php | 2 +- .../GetSelectOptionsResult.php | 2 +- .../GetSelectOptionsTreeHandler.php | 2 +- .../GetSelectOptionsTreePayload.php | 2 +- .../GetSelectOptionsTreeResult.php | 2 +- .../GetSelectOptionsUsagesHandler.php | 2 +- .../GetSelectOptionsUsagesPayload.php | 2 +- .../GetSelectOptionsUsagesResult.php | 2 +- .../GetTextLayoutPreviewHandler.php | 2 +- .../GetTextLayoutPreviewPayload.php | 2 +- .../GetTextLayoutPreviewResult.php | 2 +- .../GetVideoAllowedTypesHandler.php | 2 +- .../GetVideoAllowedTypesResult.php | 2 +- .../{ => ImportClass}/ImportClassHandler.php | 2 +- .../{ => ImportClass}/ImportClassPayload.php | 2 +- .../SaveClassDefinitionHandler.php | 2 +- .../SaveClassDefinitionPayload.php | 2 +- .../SaveClassDefinitionResult.php | 2 +- .../SaveSelectOptionsHandler.php | 2 +- .../SaveSelectOptionsPayload.php | 2 +- .../SaveSelectOptionsResult.php | 2 +- .../SuggestClassIdentifierHandler.php | 2 +- .../SuggestClassIdentifierResult.php | 2 +- .../AddCollectionsHandler.php | 2 +- .../AddCollectionsPayload.php | 2 +- .../AddCollectionsResult.php | 2 +- .../{ => AddGroups}/AddGroupsHandler.php | 2 +- .../{ => AddGroups}/AddGroupsPayload.php | 2 +- .../{ => AddGroups}/AddGroupsResult.php | 2 +- .../{ => AddProperty}/AddPropertyHandler.php | 2 +- .../{ => AddProperty}/AddPropertyPayload.php | 2 +- .../{ => AddProperty}/AddPropertyResult.php | 2 +- .../CreateCollectionHandler.php | 2 +- .../CreateCollectionPayload.php | 2 +- .../CreateCollectionResult.php | 2 +- .../{ => CreateGroup}/CreateGroupHandler.php | 2 +- .../{ => CreateGroup}/CreateGroupPayload.php | 2 +- .../{ => CreateGroup}/CreateGroupResult.php | 2 +- .../{ => CreateStore}/CreateStoreHandler.php | 2 +- .../{ => CreateStore}/CreateStorePayload.php | 2 +- .../{ => CreateStore}/CreateStoreResult.php | 2 +- .../DeleteCollectionHandler.php | 2 +- .../DeleteCollectionPayload.php | 2 +- .../DeleteCollectionRelationHandler.php | 2 +- .../DeleteCollectionRelationPayload.php | 2 +- .../{ => DeleteGroup}/DeleteGroupHandler.php | 2 +- .../{ => DeleteGroup}/DeleteGroupPayload.php | 2 +- .../DeletePropertyHandler.php | 2 +- .../DeletePropertyPayload.php | 2 +- .../DeleteRelationHandler.php | 2 +- .../DeleteRelationPayload.php | 2 +- .../{ => EditStore}/EditStoreHandler.php | 2 +- .../{ => EditStore}/EditStorePayload.php | 2 +- .../GetCollectionRelationsHandler.php | 2 +- .../GetCollectionRelationsPayload.php | 2 +- .../GetCollectionRelationsResult.php | 2 +- .../GetCollectionsHandler.php | 2 +- .../GetCollectionsPayload.php | 2 +- .../GetCollectionsResult.php | 2 +- .../{ => GetGroups}/GetGroupsHandler.php | 2 +- .../{ => GetGroups}/GetGroupsPayload.php | 2 +- .../{ => GetGroups}/GetGroupsResult.php | 2 +- .../{ => GetPage}/GetPageHandler.php | 2 +- .../{ => GetPage}/GetPagePayload.php | 2 +- .../{ => GetPage}/GetPageResult.php | 2 +- .../GetPropertiesHandler.php | 2 +- .../GetPropertiesPayload.php | 2 +- .../GetPropertiesResult.php | 2 +- .../GetRelationsHandler.php | 2 +- .../GetRelationsPayload.php | 2 +- .../{ => GetRelations}/GetRelationsResult.php | 2 +- .../GetStoreTreeHandler.php | 2 +- .../{ => GetStoreTree}/GetStoreTreeResult.php | 2 +- .../{ => ListStores}/ListStoresHandler.php | 2 +- .../{ => ListStores}/ListStoresResult.php | 2 +- .../SaveCollectionRelationsHandler.php | 2 +- .../SaveCollectionRelationsPayload.php | 2 +- .../SaveCollectionRelationsResult.php | 2 +- .../SaveRelationHandler.php | 2 +- .../SaveRelationPayload.php | 2 +- .../{ => SaveRelation}/SaveRelationResult.php | 2 +- .../SearchRelationsHandler.php | 2 +- .../SearchRelationsPayload.php | 2 +- .../SearchRelationsResult.php | 2 +- .../UpdateCollectionHandler.php | 2 +- .../UpdateCollectionPayload.php | 2 +- .../UpdateCollectionResult.php | 2 +- .../{ => UpdateGroup}/UpdateGroupHandler.php | 2 +- .../{ => UpdateGroup}/UpdateGroupPayload.php | 2 +- .../{ => UpdateGroup}/UpdateGroupResult.php | 2 +- .../UpdatePropertyHandler.php | 2 +- .../UpdatePropertyPayload.php | 2 +- .../UpdatePropertyResult.php | 2 +- .../Copy/CopyInfo/CopyInfoHandler.php | 99 +++++ .../Copy/CopyInfo/CopyInfoPayload.php | 39 ++ .../Copy/CopyInfo/CopyInfoResult.php | 26 ++ .../GetDataObjectChildIdsHandler.php | 41 -- .../AddCustomLayoutHandler.php | 2 +- .../DeleteCustomLayoutHandler.php | 2 +- .../ExportCustomLayoutHandler.php | 2 +- .../ExportCustomLayoutResult.php | 2 +- .../{ => GetAllLayouts}/AllLayoutsResult.php | 2 +- .../GetAllLayoutsHandler.php | 2 +- .../GetCustomLayoutHandler.php | 2 +- .../GetCustomLayoutResult.php | 2 +- .../CustomLayoutDefinitionsResult.php | 2 +- .../GetCustomLayoutDefinitionsHandler.php | 2 +- .../ImportCustomLayoutHandler.php | 2 +- .../SaveCustomLayoutHandler.php | 2 +- .../SuggestCustomLayoutIdentifierHandler.php | 2 +- .../SuggestCustomLayoutIdentifierResult.php | 2 +- .../DataObjectGridProxyHandler.php | 3 +- .../DataObjectGridProxyPayload.php | 2 +- .../DataObjectGridProxyResult.php | 2 +- .../DeleteDataObjectHandler.php | 3 +- .../DeleteDataObjectPayload.php | 2 +- .../DeleteDataObjectResult.php | 2 +- .../DeleteFieldCollectionHandler.php | 2 +- .../ExportFieldCollectionHandler.php | 2 +- .../ExportFieldCollectionResult.php | 2 +- .../GetFieldCollectionHandler.php | 2 +- .../GetFieldCollectionResult.php | 2 +- .../FieldCollectionListResult.php | 2 +- .../GetFieldCollectionListHandler.php | 2 +- .../FieldCollectionTreeResult.php | 2 +- .../GetFieldCollectionTreeHandler.php | 2 +- .../GetFieldCollectionUsagesHandler.php | 2 +- .../ImportFieldCollectionHandler.php | 2 +- .../UpdateFieldCollectionHandler.php | 2 +- .../GetDataObjectHandler.php | 19 +- .../GetDataObject}/GetDataObjectPayload.php | 2 +- .../GetDataObjectResult.php | 2 +- .../GetDataObjectChildrenHandler.php | 3 +- .../GetDataObjectFolderHandler.php | 8 +- .../GetDataObjectFolderResult.php | 2 +- .../GetDataObjectPreviewUrlHandler.php | 13 +- .../GetDataObjectPreviewUrlPayload.php | 15 +- .../GetIdPathPagingInfoHandler.php | 2 +- .../GetIdPathPagingInfoResult.php | 2 +- .../GetSelectOptionsHandler.php | 3 +- .../GetSelectOptionsPayload.php | 2 +- .../ApplyGridConfigToAllHandler.php | 2 +- .../ApplyGridConfigToAllPayload.php | 2 +- ...eleteDataObjectGridColumnConfigHandler.php | 46 --- .../DeleteGridColumnConfigHandler.php | 17 + .../DoDataObjectExportHandler.php | 2 +- .../DoDataObjectExportPayload.php | 2 +- .../ExecuteBatchHandler.php | 2 +- .../ExecuteBatchPayload.php | 2 +- .../GetAvailableVisibleFieldsHandler.php | 2 +- .../GetAvailableVisibleFieldsPayload.php | 2 +- .../GetAvailableVisibleFieldsResult.php | 2 +- .../GetBatchJobsHandler.php | 2 +- .../GetBatchJobsPayload.php | 2 +- .../{ => GetBatchJobs}/GetBatchJobsResult.php | 2 +- .../GetExportConfigsHandler.php | 2 +- .../GetExportConfigsPayload.php | 2 +- .../GetExportJobsHandler.php | 2 +- .../GetExportJobsPayload.php | 2 +- .../GetExportJobsResult.php | 2 +- .../ImportUploadHandler.php | 2 +- .../ImportUploadPayload.php | 2 +- .../LoadObjectDataHandler.php | 2 +- .../LoadObjectDataPayload.php | 2 +- ...rkDataObjectGridConfigFavouriteHandler.php | 2 +- ...rkDataObjectGridConfigFavouritePayload.php | 2 +- ...arkDataObjectGridConfigFavouriteResult.php | 2 +- .../PrepareHelperColumnConfigsHandler.php | 5 +- .../PrepareHelperColumnConfigsPayload.php | 14 +- .../SaveDataObjectGridColumnConfigHandler.php | 2 +- .../SaveDataObjectGridColumnConfigPayload.php | 2 +- .../SaveDataObjectGridColumnConfigResult.php | 2 +- .../DeleteObjectBrickHandler.php | 2 +- .../ExportObjectBrickHandler.php | 2 +- .../ExportObjectBrickResult.php | 2 +- .../BrickUsagesResult.php | 2 +- .../GetBrickUsagesHandler.php | 2 +- .../GetObjectBrickHandler.php | 2 +- .../GetObjectBrickResult.php | 2 +- .../GetObjectBrickListHandler.php | 2 +- .../ObjectBrickListResult.php | 2 +- .../GetObjectBrickTreeHandler.php | 2 +- .../ObjectBrickTreeResult.php | 2 +- .../ImportObjectBrickHandler.php | 2 +- .../UpdateObjectBrickHandler.php | 2 +- .../ConvertAllQuantityValuesHandler.php | 2 +- .../ConvertAllQuantityValuesResult.php | 2 +- .../ConvertQuantityValueHandler.php | 2 +- .../ConvertQuantityValueResult.php | 2 +- .../ExportQuantityValueUnitsHandler.php | 2 +- .../GetQuantityValueUnitListHandler.php | 2 +- .../GetQuantityValueUnitListResult.php | 2 +- .../GetQuantityValueUnitsHandler.php | 2 +- .../GetQuantityValueUnitsResult.php | 2 +- .../ImportQuantityValueUnitsHandler.php | 2 +- .../SaveDataObjectFolderHandler.php | 3 +- .../SaveDataObjectFolderPayload.php | 2 +- .../TreeGetChildrenByIdHandler.php | 5 +- .../TreeGetChildrenByIdPayload.php | 4 +- .../UpdateDataObjectHandler.php | 3 +- .../UpdateDataObjectPayload.php | 2 +- .../UpdateDataObjectResult.php | 2 +- .../{ => GetVariants}/GetVariantsHandler.php | 2 +- .../{ => GetVariants}/GetVariantsResult.php | 2 +- .../UpdateObjectKeyHandler.php | 2 +- .../UpdateObjectKeyResult.php | 2 +- .../DiffVersions/DiffVersionsPayload.php | 6 +- .../PreviewVersion/PreviewVersionHandler.php | 3 +- .../PreviewVersion/PreviewVersionPayload.php | 38 ++ .../PublishVersion/PublishVersionHandler.php | 6 +- .../Copy/CopyInfo/CopyInfoHandler.php | 107 +++++ .../Copy/CopyInfo/CopyInfoPayload.php | 43 ++ .../Document/Copy/CopyInfo/CopyInfoResult.php | 26 ++ .../GetDocumentChildIdsHandler.php | 40 -- .../GetDocumentChildIdsResult.php | 26 -- .../GetEmailData/GetEmailDataHandler.php | 21 +- .../GetFolderData/GetFolderDataHandler.php | 20 +- .../GetDocumentChildrenHandler.php | 2 +- .../GetDocumentChildrenResult.php | 2 +- .../GetDocumentDataHandler.php | 18 +- .../GetHardlinkDataHandler.php | 18 +- .../Link/GetLinkData/GetLinkDataHandler.php | 18 +- .../Page/GetPageData/GetPageDataHandler.php | 21 +- .../GetSnippetData/GetSnippetDataHandler.php | 21 +- .../TreeGetDocumentChildrenHandler.php | 2 +- .../PublishVersion/PublishVersionHandler.php | 6 +- .../Element/AddNote/AddNoteHandler.php | 23 ++ .../Element/AddNote/AddNotePayload.php | 30 ++ src/Handler/Element/AddNoteHandler.php | 41 -- .../AnalyzePermissionsHandler.php | 30 ++ .../AnalyzePermissionsPayload.php | 26 ++ .../AnalyzePermissionsResult.php | 12 + .../Element/AnalyzePermissionsHandler.php | 43 -- .../DeleteAllVersionsHandler.php | 23 ++ .../DeleteAllVersionsPayload.php | 26 ++ .../DeleteAllVersionsResult.php | 10 + .../Element/DeleteAllVersionsHandler.php | 36 -- .../DeleteDraft/DeleteDraftHandler.php | 19 + .../Element/DeleteNote/DeleteNoteHandler.php | 27 ++ src/Handler/Element/DeleteNoteHandler.php | 39 -- .../DeleteVersion/DeleteVersionHandler.php | 15 + .../{ => FindUsages}/FindUsagesHandler.php | 45 +- .../Element/FindUsages/FindUsagesPayload.php | 32 ++ .../Element/FindUsages/FindUsagesResult.php | 14 + .../GetDeleteInfoHandler.php | 7 +- .../GetDeleteInfo/GetDeleteInfoPayload.php | 26 ++ .../GetDependencies/GetDependenciesResult.php | 12 + .../Element/GetDependenciesPayload.php | 30 ++ .../{ => GetNicePath}/GetNicePathHandler.php | 41 +- .../GetNicePath/GetNicePathPayload.php | 47 +++ .../Element/GetNicePath/GetNicePathResult.php | 12 + .../{ => GetNoteList}/GetNoteListHandler.php | 16 +- .../Element/GetNoteList/GetNoteListResult.php | 13 + .../GetNoteTypes/GetNoteTypesHandler.php | 31 ++ .../GetNoteTypes/GetNoteTypesPayload.php | 22 + .../GetNoteTypes/GetNoteTypesResult.php | 12 + .../GetPredefinedPropertiesHandler.php | 39 ++ .../GetPredefinedPropertiesPayload.php | 24 ++ .../GetPredefinedPropertiesResult.php | 12 + .../GetPredefinedPropertiesHandler.php | 52 --- .../GetReplaceAssignmentsBatchJobsHandler.php | 29 ++ .../GetReplaceAssignmentsBatchJobsPayload.php | 26 ++ .../GetReplaceAssignmentsBatchJobsResult.php | 12 + .../GetRequiredByDependenciesHandler.php | 58 +++ .../GetRequiredByDependenciesPayload.php | 30 ++ .../GetRequiredByDependenciesHandler.php | 74 ---- .../GetRequiresDependenciesHandler.php | 58 +++ .../GetRequiresDependenciesPayload.php | 30 ++ .../GetRequiresDependenciesHandler.php | 74 ---- .../{ => GetSubtype}/GetSubtypeHandler.php | 23 +- .../Element/GetSubtype/GetSubtypePayload.php | 24 ++ .../Element/GetSubtype/GetSubtypeResult.php | 14 + src/Handler/Element/GetSubtypeResult.php | 27 -- .../{ => GetVersions}/GetVersionsHandler.php | 25 +- .../GetVersions/GetVersionsPayload.php | 24 ++ .../Element/GetVersions/GetVersionsResult.php | 12 + src/Handler/Element/GetVersionsResult.php | 25 -- .../LockElement/LockElementHandler.php | 15 + .../LockElement/LockElementPayload.php | 26 ++ .../Element/LockElement/LockElementResult.php | 10 + src/Handler/Element/NoteListPayload.php | 12 - .../ReplaceAssignmentsHandler.php | 35 +- .../ReplaceAssignmentsPayload.php | 32 ++ .../ReplaceAssignmentsResult.php | 10 + .../Element/TypePath/TypePathHandler.php | 37 ++ .../Element/TypePath/TypePathPayload.php | 24 ++ .../Element/TypePath/TypePathResult.php | 16 + src/Handler/Element/TypePathHandler.php | 50 --- src/Handler/Element/TypePathResult.php | 29 -- .../UnlockElement/UnlockElementHandler.php | 15 + .../UnlockElement/UnlockElementPayload.php | 24 ++ .../UnlockElement/UnlockElementResult.php | 10 + src/Handler/Element/UnlockElementHandler.php | 28 -- .../UnlockElements/UnlockElementsHandler.php | 18 + .../UnlockElements/UnlockElementsPayload.php | 25 ++ .../UnlockElements/UnlockElementsResult.php | 10 + src/Handler/Element/UnlockElementsHandler.php | 31 -- .../UnlockPropagateHandler.php | 22 + .../UnlockPropagatePayload.php | 24 ++ .../UnlockPropagate/UnlockPropagateResult.php | 12 + .../VersionUpdate/VersionUpdateHandler.php | 22 + .../VersionUpdate/VersionUpdatePayload.php | 30 ++ .../VersionUpdate/VersionUpdateResult.php | 10 + src/Handler/Element/VersionUpdateHandler.php | 34 -- .../CreateBlocklistEntryHandler.php | 23 ++ .../DeleteBlocklistEntryHandler.php | 17 + .../UpdateBlocklistEntryHandler.php | 20 + src/Handler/Email/BlocklistPayload.php | 12 - .../Email/CreateBlocklistEntryHandler.php | 35 -- .../Email/DeleteBlocklistEntryHandler.php | 29 -- .../DeleteEmailLog/DeleteEmailLogHandler.php | 22 + src/Handler/Email/DeleteEmailLogHandler.php | 34 -- .../GetBlocklistHandler.php | 16 +- .../Email/GetBlocklist/GetBlocklistResult.php | 13 + src/Handler/Email/GetEmailLogHandler.php | 39 -- src/Handler/Email/GetEmailLogResult.php | 27 -- .../GetEmailLogsHandler.php | 41 +- .../GetEmailLogs/GetEmailLogsPayload.php | 28 ++ .../Email/GetEmailLogs/GetEmailLogsResult.php | 13 + src/Handler/Email/GetEmailLogsResult.php | 26 -- .../{ => ResendEmail}/ResendEmailHandler.php | 31 +- .../Email/ResendEmail/ResendEmailPayload.php | 35 ++ .../SendTestEmail/SendTestEmailHandler.php | 57 +++ .../SendTestEmail/SendTestEmailPayload.php | 44 ++ src/Handler/Email/SendTestEmailHandler.php | 77 ---- .../GetEmailLogParamsHandler.php | 17 +- .../Email/ShowEmailLog/GetEmailLogResult.php | 14 + .../ShowEmailLog/ShowEmailLogHandler.php | 26 ++ .../ShowEmailLog/ShowEmailLogPayload.php | 24 ++ .../Email/UpdateBlocklistEntryHandler.php | 32 -- .../Asset/ExportAsset/ExportAssetHandler.php | 44 ++ .../Asset/ExportAsset/ExportAssetResult.php} | 9 +- .../SearchAssets/SearchAssetsHandler.php | 43 ++ .../SearchAssets/SearchAssetsResult.php} | 5 +- .../ExportDataObjectHandler.php | 44 ++ .../ExportDataObjectResult.php | 26 ++ .../SearchDataObjectsHandler.php | 43 ++ .../SearchDataObjectsResult.php | 25 ++ .../GetDataProvidersHandler.php | 41 ++ .../GetDataProvidersResult.php | 25 ++ .../ExportUserData/ExportUserDataHandler.php | 33 ++ .../ExportUserData/ExportUserDataResult.php} | 5 +- .../SearchUsers/SearchUsersHandler.php | 43 ++ .../SearchUsers/SearchUsersResult.php | 25 ++ src/Handler/GDPR/SearchDataPayload.php | 49 +++ .../ExportSentMail/ExportSentMailHandler.php} | 27 +- .../ExportSentMail/ExportSentMailResult.php | 26 ++ .../CheckSystem/CheckSystemHandler.php | 23 ++ .../CheckSystem/CheckSystemPayload.php | 22 + .../Install/CheckSystem/CheckSystemResult.php | 12 + .../Login/Deeplink/DeeplinkHandler.php | 59 +++ .../Login/Deeplink/DeeplinkPayload.php | 35 ++ src/Handler/Login/Deeplink/DeeplinkResult.php | 14 + .../GenerateTwoFactorSetupHandler.php | 14 +- .../GenerateTwoFactorSetupPayload.php | 22 + .../GenerateTwoFactorSetupResult.php | 13 + .../LostPasswordHandler.php | 34 +- .../LostPassword/LostPasswordPayload.php | 28 ++ .../Login/LostPassword/LostPasswordResult.php | 15 + .../SaveTwoFactorSetupHandler.php | 22 +- .../SaveTwoFactorSetupPayload.php | 24 ++ src/Handler/Misc/AdminCss/AdminCssHandler.php | 42 ++ src/Handler/Misc/AdminCss/AdminCssResult.php | 27 ++ ...etAvailableControllerReferencesHandler.php | 42 ++ ...etAvailableControllerReferencesResult.php} | 5 +- .../GetAvailableLanguagesHandler.php} | 11 +- .../GetAvailableLanguagesResult.php} | 7 +- .../GetAvailableTemplatesHandler.php | 41 ++ .../GetAvailableTemplatesResult.php | 25 ++ .../GetCountryList/GetCountryListHandler.php | 46 +++ .../GetCountryList/GetCountryListResult.php} | 4 +- .../{ => GetIconList}/GetIconListHandler.php | 12 +- .../Misc/GetIconList/GetIconListPayload.php | 35 ++ .../{ => GetIconList}/GetIconListResult.php | 2 +- .../GetJsonTranslationsHandler.php | 22 +- .../GetJsonTranslationsPayload.php | 35 ++ .../GetJsonTranslationsResult.php | 2 +- .../GetLanguageFlagHandler.php} | 12 +- .../GetLanguageFlagPayload.php | 35 ++ .../GetLanguageFlagResult.php} | 6 +- .../GetLanguageListHandler.php | 39 ++ .../GetLanguageList/GetLanguageListResult.php | 25 ++ .../GetValidFilenameHandler.php} | 17 +- .../GetValidFilenamePayload.php | 37 ++ .../GetValidFilenameResult.php} | 6 +- .../Misc/Maintenance/MaintenanceHandler.php | 38 ++ .../Misc/Maintenance/MaintenancePayload.php | 39 ++ .../Misc/ScriptProxy/ScriptProxyHandler.php | 47 +++ .../Misc/ScriptProxy/ScriptProxyPayload.php | 35 ++ .../Misc/ScriptProxy/ScriptProxyResult.php | 26 ++ .../DeleteAllNotificationsHandler.php | 12 +- .../DeleteNotificationHandler.php | 12 +- .../FindAllNotificationsHandler.php | 32 +- .../FindAllNotificationsPayload.php | 38 ++ .../FindAllNotificationsResult.php | 2 +- .../FindLastUnreadNotificationsHandler.php | 23 +- .../FindLastUnreadNotificationsPayload.php | 36 ++ .../FindLastUnreadNotificationsResult.php | 2 +- .../FindNotificationHandler.php | 20 +- .../FindNotificationResult.php | 2 +- .../GetRecipientsHandler.php | 20 +- .../GetRecipientsResult.php | 2 +- .../MarkAsReadNotificationHandler.php | 12 +- .../SendNotificationHandler.php | 47 +++ .../SendNotificationPayload.php | 44 ++ .../Notification/SendNotificationHandler.php | 44 -- .../{ => AddWidget}/AddWidgetHandler.php | 10 +- .../Portal/AddWidget/AddWidgetPayload.php | 37 ++ .../{ => AddWidget}/AddWidgetResult.php | 4 +- .../CreateDashboardHandler.php | 10 +- .../CreateDashboardPayload.php | 32 ++ .../DeleteDashboardHandler.php | 6 +- .../DeleteDashboardPayload.php | 32 ++ .../GetDashboardConfigurationHandler.php | 6 +- .../GetDashboardConfigurationPayload.php | 34 ++ .../GetDashboardConfigurationResult.php | 4 +- .../GetDashboardListHandler.php | 5 +- .../GetDashboardListResult.php | 4 +- .../GetModificationStatisticsHandler.php | 5 +- .../GetModificationStatisticsResult.php | 5 +- .../GetModifiedAssetsHandler.php | 7 +- .../GetModifiedAssetsResult.php | 5 +- .../GetModifiedDocumentsHandler.php | 7 +- .../GetModifiedDocumentsResult.php | 5 +- .../GetModifiedObjectsHandler.php | 7 +- .../GetModifiedObjectsResult.php | 5 +- .../RemoveWidgetHandler.php | 10 +- .../RemoveWidget/RemoveWidgetPayload.php | 37 ++ .../ReorderWidgetHandler.php | 12 +- .../ReorderWidget/ReorderWidgetPayload.php | 41 ++ .../UpdatePortletConfigHandler.php | 12 +- .../UpdatePortletConfigPayload.php | 39 ++ .../AddToRecyclebinHandler.php | 7 +- .../AddToRecyclebinPayload.php | 36 ++ .../DeleteRecyclebinItemHandler.php | 3 +- .../FlushRecyclebinHandler.php} | 16 +- .../ListRecyclebinHandler.php | 3 +- .../ListRecyclebinResult.php | 2 +- .../RestoreRecyclebinItemHandler.php | 6 +- .../RestoreRecyclebinItemPayload.php} | 8 +- .../AddThumbnailHandler.php | 9 +- .../AddThumbnail/AddThumbnailPayload.php | 34 ++ .../{ => AddThumbnail}/AddThumbnailResult.php | 2 +- .../AddVideoThumbnailHandler.php | 9 +- .../AddVideoThumbnailPayload.php | 34 ++ .../AddVideoThumbnailResult.php | 2 +- .../Settings/ClearCache/ClearCacheHandler.php | 43 ++ .../Settings/ClearCache/ClearCachePayload.php | 38 ++ .../ClearOpenDxpCacheHandler.php | 30 ++ .../ClearOutputCacheHandler.php | 2 +- .../ClearSymfonyCacheHandler.php | 30 ++ .../ClearTemporaryFilesHandler.php | 2 +- .../CreatePredefinedMetadataHandler.php | 3 +- .../CreatePredefinedMetadataResult.php | 2 +- .../CreatePredefinedPropertyHandler.php | 3 +- .../CreatePredefinedPropertyResult.php | 2 +- .../CreateWebsiteSettingHandler.php | 3 +- .../CreateWebsiteSettingResult.php | 2 +- .../DeleteCustomLogoHandler.php | 2 +- .../DeletePredefinedMetadataHandler.php | 3 +- .../DeletePredefinedPropertyHandler.php | 3 +- .../DeleteThumbnailHandler.php | 7 +- .../DeleteThumbnailPayload.php | 34 ++ .../DeleteVideoThumbnailHandler.php | 7 +- .../DeleteVideoThumbnailPayload.php | 34 ++ .../DeleteWebsiteSettingHandler.php | 3 +- .../DisplayCustomLogoHandler.php | 2 +- .../DisplayCustomLogoResult.php | 2 +- .../GetAppearanceSettingsHandler.php | 2 +- .../GetAppearanceSettingsResult.php | 2 +- .../GetAvailableAdminLanguagesHandler.php | 2 +- .../GetAvailableAdminLanguagesResult.php | 2 +- .../GetAvailableAlgorithmsHandler.php | 2 +- .../GetAvailableAlgorithmsResult.php | 2 +- .../GetAvailableCountriesHandler.php | 2 +- .../GetAvailableCountriesResult.php | 2 +- .../GetAvailableSitesHandler.php | 2 +- .../GetAvailableSitesResult.php | 2 +- .../GetDownloadableThumbnailsHandler.php | 2 +- .../GetDownloadableThumbnailsResult.php | 2 +- .../GetFilteredPredefinedMetadataHandler.php | 2 +- .../GetFilteredPredefinedMetadataResult.php | 2 +- .../GetPredefinedMetadataListHandler.php | 3 +- .../GetPredefinedMetadataListResult.php | 2 +- .../GetPredefinedPropertiesListHandler.php | 3 +- .../GetPredefinedPropertiesListResult.php | 2 +- .../GetSystemSettingsHandler.php | 2 +- .../GetSystemSettingsResult.php | 2 +- .../GetThumbnailHandler.php | 2 +- .../{ => GetThumbnail}/GetThumbnailResult.php | 2 +- .../GetThumbnailTreeHandler.php | 2 +- .../GetThumbnailTreeResult.php | 2 +- .../GetVideoThumbnailHandler.php | 2 +- .../GetVideoThumbnailResult.php | 2 +- .../GetVideoThumbnailListHandler.php | 2 +- .../GetVideoThumbnailListResult.php | 2 +- .../GetVideoThumbnailTreeHandler.php | 2 +- .../GetVideoThumbnailTreeResult.php | 2 +- .../GetWebsiteSettingsListHandler.php | 4 +- .../GetWebsiteSettingsListResult.php | 2 +- .../SaveAppearanceSettingsHandler.php | 25 +- src/Handler/Settings/SaveSettingsPayload.php | 36 ++ .../SaveSystemSettingsHandler.php | 25 +- .../ThumbnailAdapterCheckHandler.php | 2 +- .../ThumbnailAdapterCheckResult.php | 2 +- .../UpdatePredefinedMetadataHandler.php | 3 +- .../UpdatePredefinedMetadataResult.php | 2 +- .../UpdatePredefinedPropertyHandler.php | 3 +- .../UpdatePredefinedPropertyResult.php | 2 +- .../UpdateThumbnailHandler.php | 18 +- .../UpdateThumbnailPayload.php | 40 ++ .../UpdateVideoThumbnailHandler.php | 18 +- .../UpdateVideoThumbnailPayload.php | 40 ++ .../UpdateWebsiteSettingHandler.php | 3 +- .../UpdateWebsiteSettingResult.php | 2 +- .../UploadCustomLogoHandler.php | 2 +- .../Tags/{ => AddTag}/AddTagHandler.php | 8 +- src/Handler/Tags/AddTag/AddTagPayload.php | 37 ++ .../Tags/{ => AddTag}/AddTagResult.php | 2 +- .../AddTagToElementHandler.php | 10 +- .../AddTagToElementPayload.php | 39 ++ .../AddTagToElementResult.php | 2 +- .../Tags/{ => DeleteTag}/DeleteTagHandler.php | 8 +- .../DeleteTag/DeleteTagPayload.php} | 12 +- .../DoBatchAssignmentHandler.php | 6 +- .../DoBatchAssignmentPayload.php | 41 ++ .../GetBatchAssignmentJobsHandler.php | 10 +- .../GetBatchAssignmentJobsPayload.php | 37 ++ .../GetBatchAssignmentJobsResult.php | 2 +- .../GetTagTreeChildrenHandler.php | 27 +- .../GetTagTreeChildrenPayload.php | 43 ++ .../GetTagTreeChildrenResult.php | 2 +- .../GetTagsForElementHandler.php | 6 +- .../GetTagsForElementResult.php | 2 +- .../LoadTagsForElementPayload.php | 37 ++ .../RemoveTagFromElementHandler.php | 10 +- .../RemoveTagFromElementPayload.php | 39 ++ .../RemoveTagFromElementResult.php | 2 +- .../Tags/{ => UpdateTag}/UpdateTagHandler.php | 16 +- .../Tags/UpdateTag/UpdateTagPayload.php | 41 ++ .../AddAdminTranslationKeysHandler.php | 6 +- .../AddAdminTranslationKeysPayload.php | 36 ++ .../BuildContentExportJobsHandler.php | 23 +- .../BuildContentExportJobsPayload.php | 46 +++ .../BuildContentExportJobsResult.php | 2 +- .../CleanupTranslationsHandler.php | 6 +- .../CleanupTranslationsPayload.php | 35 ++ .../CreateTranslationHandler.php | 2 +- .../CreateTranslationResult.php | 2 +- .../DeleteTranslationHandler.php | 2 +- .../ExportTranslationsHandler.php | 22 +- .../ExportTranslationsPayload.php | 38 ++ .../ExportTranslationsResult.php | 2 +- .../GetTranslationDomainsHandler.php | 5 +- .../GetTranslationDomainsResult.php | 2 +- .../GetTranslationsHandler.php | 3 +- .../GetTranslationsResult.php | 2 +- .../GetWebsiteTranslationLanguagesHandler.php | 5 +- .../GetWebsiteTranslationLanguagesResult.php | 2 +- .../ImportTranslationsHandler.php | 32 +- .../ImportTranslationsPayload.php | 53 +++ .../ImportTranslationsResult.php | 2 +- .../MergeTranslationItemsHandler.php | 8 +- .../MergeTranslationItemsPayload.php | 37 ++ .../UpdateTranslationHandler.php | 2 +- .../UpdateTranslationResult.php | 2 +- .../UploadTranslationImportFileHandler.php | 7 +- .../UploadTranslationImportFilePayload.php | 35 ++ .../UploadTranslationImportFileResult.php | 2 +- .../User/{ => AddUser}/AddUserHandler.php | 27 +- src/Handler/User/AddUser/AddUserPayload.php | 46 +++ .../User/{ => AddUser}/AddUserResult.php | 2 +- .../{ => DeleteUser}/DeleteUserHandler.php | 6 +- .../DeleteUser/DeleteUserPayload.php} | 8 +- .../DeleteUserImageHandler.php | 6 +- .../DeleteUserImagePayload.php | 35 ++ .../{ => Disable2Fa}/Disable2FaHandler.php | 4 +- .../User/Disable2Fa/Disable2FaPayload.php | 31 ++ .../GetCurrentUserHandler.php | 6 +- .../GetCurrentUser/GetCurrentUserPayload.php | 33 ++ .../GetCurrentUserResult.php | 2 +- .../GetMinimalUserHandler.php | 6 +- .../GetMinimalUser/GetMinimalUserPayload.php | 35 ++ .../GetMinimalUserResult.php | 2 +- .../User/{ => GetRole}/GetRoleHandler.php | 6 +- src/Handler/User/GetRole/GetRolePayload.php | 35 ++ .../User/{ => GetRole}/GetRoleResult.php | 2 +- .../GetRoleTreeChildrenHandler.php | 6 +- .../GetRoleTreeChildrenPayload.php} | 8 +- .../User/{ => GetRoles}/GetRolesHandler.php | 6 +- src/Handler/User/GetRoles/GetRolesPayload.php | 30 ++ .../GetTokenLoginLinkHandler.php | 6 +- .../GetTokenLoginLinkPayload.php | 35 ++ src/Handler/User/GetTokenLoginLinkResult.php | 25 -- .../User/{ => GetUser}/GetUserHandler.php | 8 +- src/Handler/User/GetUser/GetUserPayload.php | 35 ++ .../User/{ => GetUser}/GetUserResult.php | 2 +- .../GetUserImageHandler.php | 13 +- .../User/GetUserImage/GetUserImagePayload.php | 35 ++ .../User/GetUserImage/GetUserImageResult.php | 27 ++ .../GetUserTreeChildrenHandler.php | 6 +- .../GetUserTreeChildrenPayload.php | 35 ++ .../User/{ => GetUsers}/GetUsersHandler.php | 8 +- src/Handler/User/GetUsers/GetUsersPayload.php | 39 ++ .../Reset2FaSecretHandler.php | 6 +- .../Reset2FaSecret/Reset2FaSecretPayload.php | 35 ++ .../ResetMy2FaSecretHandler.php | 5 +- .../{ => SearchUsers}/SearchUsersHandler.php | 10 +- .../User/SearchUsers/SearchUsersPayload.php | 35 ++ .../SendInvitationLinkHandler.php | 14 +- .../SendInvitationLinkPayload.php | 35 ++ .../SendInvitationLinkResult.php | 4 +- .../UpdateCurrentUserHandler.php | 19 +- .../UpdateCurrentUserPayload.php | 41 ++ .../{ => UpdateUser}/UpdateUserHandler.php | 23 +- .../User/UpdateUser/UpdateUserPayload.php | 47 +++ .../UploadUserImageHandler.php | 11 +- .../UploadUserImagePayload.php | 37 ++ .../GetModalCustomHtmlHandler.php | 23 +- .../GetModalCustomHtmlPayload.php | 42 ++ .../GetModalCustomHtmlResult.php | 2 +- .../GetWorkflowDetailsHandler.php | 10 +- .../GetWorkflowDetailsPayload.php | 36 ++ .../GetWorkflowDetailsResult.php | 2 +- .../GetWorkflowFormHandler.php | 18 +- .../GetWorkflowFormPayload.php | 40 ++ .../GetWorkflowFormResult.php | 2 +- .../GetWorkflowSvg}/GetWorkflowSvgHandler.php | 8 +- .../Workflow/ShowGraph/ShowGraphPayload.php | 38 ++ .../SubmitGlobalActionHandler.php | 23 +- .../SubmitGlobalActionPayload.php | 42 ++ .../SubmitWorkflowTransitionHandler.php | 25 +- .../SubmitWorkflowTransitionPayload.php | 42 ++ .../SubmitWorkflowTransitionResult.php | 2 +- .../DataObject/AdminStyleNormalizer.php | 61 --- .../DataObject/TreeStyleNormalizer.php | 58 --- .../DataObject/UserNamesNormalizer.php | 46 --- .../Document/AdminStyleNormalizer.php | 55 --- .../Document/DocumentMetaNormalizer.php | 59 --- src/Normalizer/Document/DraftNormalizer.php | 52 --- .../Document/PropertiesNormalizer.php | 51 --- .../Document/TranslationNormalizer.php | 56 --- .../Element/UserNamesNormalizer.php | 60 --- src/Normalizer/ElementResponseNormalizer.php | 35 -- .../ElementResponseNormalizerInterface.php | 27 -- .../Cache/OpenDxpCacheClearingService.php} | 9 +- .../Cache/SymfonyCacheClearingService.php} | 9 +- 826 files changed, 8768 insertions(+), 5064 deletions(-) rename src/{Normalizer/DataObject/CustomLayoutNormalizer.php => Enricher/DataObject/CustomLayoutEnricher.php} (67%) rename src/{Normalizer/DataObject/DraftNormalizer.php => Enricher/DataObject/DraftEnricher.php} (53%) create mode 100644 src/Enricher/Document/DocumentMetaEnricher.php create mode 100644 src/Enricher/Document/DraftEnricher.php rename src/{Handler/Element/DeleteDraftHandler.php => Enricher/Document/PropertiesEnricher.php} (60%) create mode 100644 src/Enricher/Document/TranslationEnricher.php create mode 100644 src/Enricher/Element/AdminStyleEnricher.php rename src/{Normalizer/Element/AbstractUserNamesNormalizer.php => Enricher/Element/UserNamesEnricher.php} (54%) delete mode 100644 src/EventListener/Traits/ControllerTypeTrait.php rename src/Handler/Asset/{ => ClearAssetThumbnail}/ClearAssetThumbnailHandler.php (94%) rename src/Handler/Asset/Copy/{ => CopyAsset}/CopyAssetHandler.php (97%) rename src/Handler/Asset/Copy/{ => CopyAsset}/CopyAssetResult.php (90%) create mode 100644 src/Handler/Asset/Copy/CopyInfo/CopyInfoHandler.php create mode 100644 src/Handler/Asset/Copy/CopyInfo/CopyInfoPayload.php create mode 100644 src/Handler/Asset/Copy/CopyInfo/CopyInfoResult.php delete mode 100644 src/Handler/Asset/Copy/GetAssetChildIdsHandler.php rename src/Handler/Asset/{ => CreateAssetFolder}/CreateAssetFolderHandler.php (96%) rename src/Handler/Asset/{ => DeleteAsset}/DeleteAssetHandler.php (97%) rename src/Handler/Asset/{ => DeleteAsset}/DeleteAssetResult.php (90%) rename src/Handler/Asset/Download/{ => AddFilesToZip}/AddFilesToZipHandler.php (98%) rename src/Handler/Asset/Download/{ => DownloadAsset}/DownloadAssetHandler.php (94%) rename src/Handler/Asset/Download/{ => DownloadImageThumbnail}/DownloadImageThumbnailHandler.php (98%) rename src/Handler/Asset/Download/{ => DownloadImageThumbnail}/DownloadImageThumbnailResult.php (90%) rename src/Handler/Asset/Download/{ => DownloadZip}/DownloadZipHandler.php (94%) rename src/Handler/Asset/Download/{ => DownloadZip}/DownloadZipResult.php (90%) rename src/Handler/Asset/Download/{ => GetDownloadZipJobs}/GetDownloadZipJobsHandler.php (97%) rename src/Handler/Asset/Download/{ => GetDownloadZipJobs}/GetDownloadZipJobsResult.php (89%) rename src/Handler/Asset/Editor/{ => LoadAssetForEditor}/LoadAssetForEditorHandler.php (87%) rename src/Handler/Asset/Editor/{ => SaveImageEditor}/SaveImageEditorHandler.php (95%) rename src/Handler/Asset/{ => GetAssetChildren}/GetAssetChildrenHandler.php (98%) rename src/Handler/Asset/{ => GetAssetChildren}/GetAssetChildrenResult.php (91%) rename src/Handler/Asset/{ => GetAssetData}/GetAssetDataHandler.php (82%) rename src/Handler/Asset/{ => GetAssetData}/GetAssetDataResult.php (90%) create mode 100644 src/Handler/Asset/GridProxy/GridProxyHandler.php create mode 100644 src/Handler/Asset/GridProxy/GridProxyPayload.php rename src/Handler/{Element/GetNicePathResult.php => Asset/GridProxy/GridProxyResult.php} (85%) rename src/Handler/Asset/Helper/{ => DeleteGridColumnConfig}/DeleteGridColumnConfigHandler.php (95%) rename src/Handler/Asset/Helper/{ => DoAssetExport}/DoAssetExportHandler.php (98%) rename src/Handler/Asset/Helper/{ => ExecuteAssetBatch}/ExecuteAssetBatchHandler.php (94%) rename src/Handler/Asset/Helper/{ => GetAssetBatchJobs}/GetAssetBatchJobsHandler.php (94%) rename src/Handler/Asset/Helper/{ => GetAssetBatchJobs}/GetAssetBatchJobsResult.php (89%) rename src/Handler/Asset/Helper/{ => GetAssetMetadataForColumnConfig}/GetAssetMetadataForColumnConfigHandler.php (97%) rename src/Handler/Asset/Helper/{ => GetAssetMetadataForColumnConfig}/GetAssetMetadataForColumnConfigResult.php (87%) rename src/Handler/Asset/Helper/{ => GetExportJobs}/GetExportJobsHandler.php (96%) rename src/Handler/Asset/Helper/{ => GetExportJobs}/GetExportJobsResult.php (90%) rename src/Handler/Asset/Helper/{ => MarkGridConfigFavourite}/MarkGridConfigFavouriteHandler.php (96%) rename src/Handler/Asset/Helper/{ => MarkGridConfigFavourite}/MarkGridConfigFavouriteResult.php (88%) create mode 100644 src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsHandler.php create mode 100644 src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsPayload.php rename src/Handler/{Login/GenerateTwoFactorSetupResult.php => Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsResult.php} (67%) rename src/Handler/Asset/Helper/{ => SaveGridColumnConfig}/SaveGridColumnConfigHandler.php (98%) rename src/Handler/Asset/Helper/{ => SaveGridColumnConfig}/SaveGridColumnConfigResult.php (89%) rename src/Handler/Asset/Media/{ => GetAssetText}/AssetTextResult.php (89%) rename src/Handler/Asset/Media/{ => GetAssetText}/GetAssetTextHandler.php (94%) rename src/Handler/Asset/Media/{ => GetDocumentPreview}/GetDocumentPreviewHandler.php (97%) rename src/Handler/Asset/Media/{ => GetDocumentPreview}/PreviewDocumentResult.php (92%) rename src/Handler/Asset/Media/{ => GetVideoPreview}/GetVideoPreviewHandler.php (95%) rename src/Handler/Asset/Media/{ => GetVideoPreview}/PreviewVideoResult.php (91%) rename src/Handler/Asset/Media/{ => ServeVideoPreview}/ServeVideoPreviewHandler.php (96%) rename src/Handler/Asset/Media/{ => ServeVideoPreview}/ServeVideoPreviewResult.php (89%) rename src/Handler/Asset/Thumbnail/{ => GetDocumentThumbnail}/GetDocumentThumbnailHandler.php (96%) rename src/Handler/Asset/Thumbnail/{ => GetDocumentThumbnail}/GetDocumentThumbnailResult.php (88%) rename src/Handler/Asset/Thumbnail/{ => GetFolderContentPreview}/GetFolderContentPreviewHandler.php (98%) rename src/Handler/Asset/Thumbnail/{ => GetFolderContentPreview}/GetFolderContentPreviewResult.php (88%) rename src/Handler/Asset/Thumbnail/{ => GetFolderThumbnail}/FolderThumbnailResult.php (89%) rename src/Handler/Asset/Thumbnail/{ => GetFolderThumbnail}/GetFolderThumbnailHandler.php (95%) rename src/Handler/Asset/Thumbnail/{ => GetImageThumbnail}/GetImageThumbnailHandler.php (97%) rename src/Handler/Asset/Thumbnail/{ => GetImageThumbnail}/GetImageThumbnailResult.php (90%) rename src/Handler/Asset/Thumbnail/{ => GetVideoThumbnail}/GetVideoThumbnailHandler.php (97%) rename src/Handler/Asset/Thumbnail/{ => GetVideoThumbnail}/GetVideoThumbnailResult.php (89%) rename src/Handler/Asset/{ => UpdateAsset}/UpdateAssetHandler.php (98%) rename src/Handler/Asset/{ => UpdateAsset}/UpdateAssetResult.php (90%) rename src/Handler/Asset/Upload/{ => CheckAssetExists}/CheckAssetExistsHandler.php (95%) rename src/Handler/Asset/Upload/{ => ImportZip}/ImportZipHandler.php (97%) rename src/Handler/Asset/Upload/{ => ImportZip}/ImportZipResult.php (90%) rename src/Handler/Asset/Upload/{ => ImportZipFiles}/ImportZipFilesHandler.php (98%) rename src/Handler/Asset/Upload/{ => ReplaceAsset}/ReplaceAssetHandler.php (97%) rename src/Handler/Asset/Version/{ => PublishVersion}/PublishVersionHandler.php (96%) rename src/Handler/Asset/Version/{ => ShowVersion}/ShowVersionHandler.php (96%) rename src/Handler/Asset/Version/{ => ShowVersion}/ShowVersionResult.php (91%) rename src/Handler/DataObject/{ => AddObject}/AddObjectHandler.php (96%) rename src/{Payload/DataObject => Handler/DataObject/AddObject}/AddObjectPayload.php (95%) rename src/Handler/DataObject/{ => AddObject}/AddObjectResult.php (90%) rename src/Handler/DataObject/{ => AddObjectFolder}/AddObjectFolderHandler.php (93%) rename src/{Payload/DataObject => Handler/DataObject/AddObjectFolder}/AddObjectFolderPayload.php (93%) rename src/Handler/DataObject/{ => ChangeChildrenSortBy}/ChangeChildrenSortByHandler.php (97%) rename src/{Payload/DataObject => Handler/DataObject/ChangeChildrenSortBy}/ChangeChildrenSortByPayload.php (93%) rename src/Handler/DataObject/ClassDef/{ => AddClass}/AddClassHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => AddClass}/AddClassPayload.php (98%) rename src/Handler/DataObject/ClassDef/{ => AddClass}/AddClassResult.php (98%) rename src/Handler/DataObject/ClassDef/{ => BulkCommit}/BulkCommitHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => BulkCommit}/BulkCommitPayload.php (97%) rename src/Handler/DataObject/ClassDef/{ => BulkExportPrepare}/BulkExportPreparePayload.php (96%) delete mode 100644 src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php rename src/Handler/DataObject/ClassDef/{ => BulkImport}/BulkImportHandler.php (79%) rename src/Handler/DataObject/ClassDef/{ => BulkImport}/BulkImportPayload.php (98%) rename src/Handler/DataObject/ClassDef/{ => BulkImport}/BulkImportResult.php (93%) rename src/Handler/DataObject/ClassDef/{ => DeleteClass}/DeleteClassHandler.php (98%) rename src/Handler/DataObject/ClassDef/{ => DeleteClass}/DeleteClassPayload.php (97%) rename src/Handler/DataObject/ClassDef/{ => DeleteSelectOptions}/DeleteSelectOptionsHandler.php (97%) rename src/Handler/DataObject/ClassDef/{ => DeleteSelectOptions}/DeleteSelectOptionsPayload.php (96%) rename src/Handler/DataObject/ClassDef/{ => DoBulkExport}/DoBulkExportHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => DoBulkExport}/DoBulkExportResult.php (97%) rename src/Handler/DataObject/ClassDef/{ => ExportClass}/ExportClassHandler.php (98%) rename src/Handler/DataObject/ClassDef/{ => ExportClass}/ExportClassPayload.php (97%) rename src/Handler/DataObject/ClassDef/{ => ExportClass}/ExportClassResult.php (98%) rename src/Handler/DataObject/ClassDef/{ => GetAssetTypes}/GetAssetTypesHandler.php (98%) rename src/Handler/DataObject/ClassDef/{ => GetAssetTypes}/GetAssetTypesResult.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetClass}/GetClassHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => GetClass}/GetClassPayload.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetClass}/GetClassResult.php (98%) rename src/Handler/DataObject/ClassDef/{ => GetClassBulkExportList}/GetClassBulkExportListHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => GetClassBulkExportList}/GetClassBulkExportListResult.php (96%) rename src/Handler/DataObject/ClassDef/{ => GetClassDefinitionForColumnConfig}/GetClassDefinitionForColumnConfigHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => GetClassDefinitionForColumnConfig}/GetClassDefinitionForColumnConfigPayload.php (94%) rename src/Handler/DataObject/ClassDef/{ => GetClassDefinitionForColumnConfig}/GetClassDefinitionForColumnConfigResult.php (95%) rename src/Handler/DataObject/ClassDef/{ => GetClassIcons}/GetClassIconsHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => GetClassIcons}/GetClassIconsPayload.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetClassIcons}/GetClassIconsResult.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetClassTree}/GetClassTreeHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => GetClassTree}/GetClassTreePayload.php (98%) rename src/Handler/DataObject/ClassDef/{ => GetClassTree}/GetClassTreeResult.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetDocumentTypes}/GetDocumentTypesHandler.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetDocumentTypes}/GetDocumentTypesResult.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptions}/GetSelectOptionsHandler.php (98%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptions}/GetSelectOptionsPayload.php (96%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptions}/GetSelectOptionsResult.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptionsTree}/GetSelectOptionsTreeHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptionsTree}/GetSelectOptionsTreePayload.php (95%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptionsTree}/GetSelectOptionsTreeResult.php (96%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptionsUsages}/GetSelectOptionsUsagesHandler.php (98%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptionsUsages}/GetSelectOptionsUsagesPayload.php (96%) rename src/Handler/DataObject/ClassDef/{ => GetSelectOptionsUsages}/GetSelectOptionsUsagesResult.php (96%) rename src/Handler/DataObject/ClassDef/{ => GetTextLayoutPreview}/GetTextLayoutPreviewHandler.php (98%) rename src/Handler/DataObject/ClassDef/{ => GetTextLayoutPreview}/GetTextLayoutPreviewPayload.php (97%) rename src/Handler/DataObject/ClassDef/{ => GetTextLayoutPreview}/GetTextLayoutPreviewResult.php (96%) rename src/Handler/DataObject/ClassDef/{ => GetVideoAllowedTypes}/GetVideoAllowedTypesHandler.php (98%) rename src/Handler/DataObject/ClassDef/{ => GetVideoAllowedTypes}/GetVideoAllowedTypesResult.php (96%) rename src/Handler/DataObject/ClassDef/{ => ImportClass}/ImportClassHandler.php (98%) rename src/Handler/DataObject/ClassDef/{ => ImportClass}/ImportClassPayload.php (98%) rename src/Handler/DataObject/ClassDef/{ => SaveClassDefinition}/SaveClassDefinitionHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => SaveClassDefinition}/SaveClassDefinitionPayload.php (97%) rename src/Handler/DataObject/ClassDef/{ => SaveClassDefinition}/SaveClassDefinitionResult.php (97%) rename src/Handler/DataObject/ClassDef/{ => SaveSelectOptions}/SaveSelectOptionsHandler.php (99%) rename src/Handler/DataObject/ClassDef/{ => SaveSelectOptions}/SaveSelectOptionsPayload.php (98%) rename src/Handler/DataObject/ClassDef/{ => SaveSelectOptions}/SaveSelectOptionsResult.php (97%) rename src/Handler/DataObject/ClassDef/{ => SuggestClassIdentifier}/SuggestClassIdentifierHandler.php (97%) rename src/Handler/DataObject/ClassDef/{ => SuggestClassIdentifier}/SuggestClassIdentifierResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => AddCollections}/AddCollectionsHandler.php (99%) rename src/Handler/DataObject/Classificationstore/{ => AddCollections}/AddCollectionsPayload.php (96%) rename src/Handler/DataObject/Classificationstore/{ => AddCollections}/AddCollectionsResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => AddGroups}/AddGroupsHandler.php (99%) rename src/Handler/DataObject/Classificationstore/{ => AddGroups}/AddGroupsPayload.php (97%) rename src/Handler/DataObject/Classificationstore/{ => AddGroups}/AddGroupsResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => AddProperty}/AddPropertyHandler.php (98%) rename src/Handler/DataObject/Classificationstore/{ => AddProperty}/AddPropertyPayload.php (96%) rename src/Handler/DataObject/Classificationstore/{ => AddProperty}/AddPropertyResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => CreateCollection}/CreateCollectionHandler.php (97%) rename src/Handler/DataObject/Classificationstore/{ => CreateCollection}/CreateCollectionPayload.php (95%) rename src/Handler/DataObject/Classificationstore/{ => CreateCollection}/CreateCollectionResult.php (95%) rename src/Handler/DataObject/Classificationstore/{ => CreateGroup}/CreateGroupHandler.php (98%) rename src/Handler/DataObject/Classificationstore/{ => CreateGroup}/CreateGroupPayload.php (96%) rename src/Handler/DataObject/Classificationstore/{ => CreateGroup}/CreateGroupResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => CreateStore}/CreateStoreHandler.php (97%) rename src/Handler/DataObject/Classificationstore/{ => CreateStore}/CreateStorePayload.php (96%) rename src/Handler/DataObject/Classificationstore/{ => CreateStore}/CreateStoreResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => DeleteCollection}/DeleteCollectionHandler.php (97%) rename src/Handler/DataObject/Classificationstore/{ => DeleteCollection}/DeleteCollectionPayload.php (94%) rename src/Handler/DataObject/Classificationstore/{ => DeleteCollectionRelation}/DeleteCollectionRelationHandler.php (96%) rename src/Handler/DataObject/Classificationstore/{ => DeleteCollectionRelation}/DeleteCollectionRelationPayload.php (94%) rename src/Handler/DataObject/Classificationstore/{ => DeleteGroup}/DeleteGroupHandler.php (97%) rename src/Handler/DataObject/Classificationstore/{ => DeleteGroup}/DeleteGroupPayload.php (95%) rename src/Handler/DataObject/Classificationstore/{ => DeleteProperty}/DeletePropertyHandler.php (96%) rename src/Handler/DataObject/Classificationstore/{ => DeleteProperty}/DeletePropertyPayload.php (94%) rename src/Handler/DataObject/Classificationstore/{ => DeleteRelation}/DeleteRelationHandler.php (97%) rename src/Handler/DataObject/Classificationstore/{ => DeleteRelation}/DeleteRelationPayload.php (95%) rename src/Handler/DataObject/Classificationstore/{ => EditStore}/EditStoreHandler.php (98%) rename src/Handler/DataObject/Classificationstore/{ => EditStore}/EditStorePayload.php (97%) rename src/Handler/DataObject/Classificationstore/{ => GetCollectionRelations}/GetCollectionRelationsHandler.php (98%) rename src/Handler/DataObject/Classificationstore/{ => GetCollectionRelations}/GetCollectionRelationsPayload.php (96%) rename src/Handler/DataObject/Classificationstore/{ => GetCollectionRelations}/GetCollectionRelationsResult.php (95%) rename src/Handler/DataObject/Classificationstore/{ => GetCollections}/GetCollectionsHandler.php (99%) rename src/Handler/DataObject/Classificationstore/{ => GetCollections}/GetCollectionsPayload.php (98%) rename src/Handler/DataObject/Classificationstore/{ => GetCollections}/GetCollectionsResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => GetGroups}/GetGroupsHandler.php (99%) rename src/Handler/DataObject/Classificationstore/{ => GetGroups}/GetGroupsPayload.php (98%) rename src/Handler/DataObject/Classificationstore/{ => GetGroups}/GetGroupsResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => GetPage}/GetPageHandler.php (99%) rename src/Handler/DataObject/Classificationstore/{ => GetPage}/GetPagePayload.php (97%) rename src/Handler/DataObject/Classificationstore/{ => GetPage}/GetPageResult.php (97%) rename src/Handler/DataObject/Classificationstore/{ => GetProperties}/GetPropertiesHandler.php (99%) rename src/Handler/DataObject/Classificationstore/{ => GetProperties}/GetPropertiesPayload.php (98%) rename src/Handler/DataObject/Classificationstore/{ => GetProperties}/GetPropertiesResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => GetRelations}/GetRelationsHandler.php (99%) rename src/Handler/DataObject/Classificationstore/{ => GetRelations}/GetRelationsPayload.php (98%) rename src/Handler/DataObject/Classificationstore/{ => GetRelations}/GetRelationsResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => GetStoreTree}/GetStoreTreeHandler.php (98%) rename src/Handler/DataObject/Classificationstore/{ => GetStoreTree}/GetStoreTreeResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => ListStores}/ListStoresHandler.php (97%) rename src/Handler/DataObject/Classificationstore/{ => ListStores}/ListStoresResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => SaveCollectionRelations}/SaveCollectionRelationsHandler.php (97%) rename src/Handler/DataObject/Classificationstore/{ => SaveCollectionRelations}/SaveCollectionRelationsPayload.php (94%) rename src/Handler/DataObject/Classificationstore/{ => SaveCollectionRelations}/SaveCollectionRelationsResult.php (94%) rename src/Handler/DataObject/Classificationstore/{ => SaveRelation}/SaveRelationHandler.php (98%) rename src/Handler/DataObject/Classificationstore/{ => SaveRelation}/SaveRelationPayload.php (96%) rename src/Handler/DataObject/Classificationstore/{ => SaveRelation}/SaveRelationResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => SearchRelations}/SearchRelationsHandler.php (99%) rename src/Handler/DataObject/Classificationstore/{ => SearchRelations}/SearchRelationsPayload.php (97%) rename src/Handler/DataObject/Classificationstore/{ => SearchRelations}/SearchRelationsResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => UpdateCollection}/UpdateCollectionHandler.php (98%) rename src/Handler/DataObject/Classificationstore/{ => UpdateCollection}/UpdateCollectionPayload.php (95%) rename src/Handler/DataObject/Classificationstore/{ => UpdateCollection}/UpdateCollectionResult.php (95%) rename src/Handler/DataObject/Classificationstore/{ => UpdateGroup}/UpdateGroupHandler.php (98%) rename src/Handler/DataObject/Classificationstore/{ => UpdateGroup}/UpdateGroupPayload.php (96%) rename src/Handler/DataObject/Classificationstore/{ => UpdateGroup}/UpdateGroupResult.php (96%) rename src/Handler/DataObject/Classificationstore/{ => UpdateProperty}/UpdatePropertyHandler.php (97%) rename src/Handler/DataObject/Classificationstore/{ => UpdateProperty}/UpdatePropertyPayload.php (95%) rename src/Handler/DataObject/Classificationstore/{ => UpdateProperty}/UpdatePropertyResult.php (96%) create mode 100644 src/Handler/DataObject/Copy/CopyInfo/CopyInfoHandler.php create mode 100644 src/Handler/DataObject/Copy/CopyInfo/CopyInfoPayload.php create mode 100644 src/Handler/DataObject/Copy/CopyInfo/CopyInfoResult.php delete mode 100644 src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsHandler.php rename src/Handler/DataObject/CustomLayout/{ => AddCustomLayout}/AddCustomLayoutHandler.php (98%) rename src/Handler/DataObject/CustomLayout/{ => DeleteCustomLayout}/DeleteCustomLayoutHandler.php (98%) rename src/Handler/DataObject/CustomLayout/{ => ExportCustomLayout}/ExportCustomLayoutHandler.php (98%) rename src/Handler/DataObject/CustomLayout/{ => ExportCustomLayout}/ExportCustomLayoutResult.php (96%) rename src/Handler/DataObject/CustomLayout/{ => GetAllLayouts}/AllLayoutsResult.php (97%) rename src/Handler/DataObject/CustomLayout/{ => GetAllLayouts}/GetAllLayoutsHandler.php (99%) rename src/Handler/DataObject/CustomLayout/{ => GetCustomLayout}/GetCustomLayoutHandler.php (99%) rename src/Handler/DataObject/CustomLayout/{ => GetCustomLayout}/GetCustomLayoutResult.php (97%) rename src/Handler/DataObject/CustomLayout/{ => GetCustomLayoutDefinitions}/CustomLayoutDefinitionsResult.php (95%) rename src/Handler/DataObject/CustomLayout/{ => GetCustomLayoutDefinitions}/GetCustomLayoutDefinitionsHandler.php (97%) rename src/Handler/DataObject/CustomLayout/{ => ImportCustomLayout}/ImportCustomLayoutHandler.php (98%) rename src/Handler/DataObject/CustomLayout/{ => SaveCustomLayout}/SaveCustomLayoutHandler.php (98%) rename src/Handler/DataObject/CustomLayout/{ => SuggestCustomLayoutIdentifier}/SuggestCustomLayoutIdentifierHandler.php (97%) rename src/Handler/DataObject/CustomLayout/{ => SuggestCustomLayoutIdentifier}/SuggestCustomLayoutIdentifierResult.php (95%) rename src/Handler/DataObject/{ => DataObjectGridProxy}/DataObjectGridProxyHandler.php (92%) rename src/{Payload/DataObject => Handler/DataObject/DataObjectGridProxy}/DataObjectGridProxyPayload.php (94%) rename src/Handler/DataObject/{ => DataObjectGridProxy}/DataObjectGridProxyResult.php (89%) rename src/Handler/DataObject/{ => DeleteDataObject}/DeleteDataObjectHandler.php (94%) rename src/{Payload/DataObject => Handler/DataObject/DeleteDataObject}/DeleteDataObjectPayload.php (93%) rename src/Handler/DataObject/{ => DeleteDataObject}/DeleteDataObjectResult.php (89%) rename src/Handler/DataObject/FieldCollection/{ => DeleteFieldCollection}/DeleteFieldCollectionHandler.php (96%) rename src/Handler/DataObject/FieldCollection/{ => ExportFieldCollection}/ExportFieldCollectionHandler.php (98%) rename src/Handler/DataObject/FieldCollection/{ => ExportFieldCollection}/ExportFieldCollectionResult.php (95%) rename src/Handler/DataObject/FieldCollection/{ => GetFieldCollection}/GetFieldCollectionHandler.php (97%) rename src/Handler/DataObject/FieldCollection/{ => GetFieldCollection}/GetFieldCollectionResult.php (96%) rename src/Handler/DataObject/FieldCollection/{ => GetFieldCollectionList}/FieldCollectionListResult.php (95%) rename src/Handler/DataObject/FieldCollection/{ => GetFieldCollectionList}/GetFieldCollectionListHandler.php (98%) rename src/Handler/DataObject/FieldCollection/{ => GetFieldCollectionTree}/FieldCollectionTreeResult.php (95%) rename src/Handler/DataObject/FieldCollection/{ => GetFieldCollectionTree}/GetFieldCollectionTreeHandler.php (99%) rename src/Handler/DataObject/FieldCollection/{ => GetFieldCollectionUsages}/GetFieldCollectionUsagesHandler.php (97%) rename src/Handler/DataObject/FieldCollection/{ => ImportFieldCollection}/ImportFieldCollectionHandler.php (97%) rename src/Handler/DataObject/FieldCollection/{ => UpdateFieldCollection}/UpdateFieldCollectionHandler.php (98%) rename src/Handler/DataObject/{ => GetDataObject}/GetDataObjectHandler.php (95%) rename src/{Payload/DataObject => Handler/DataObject/GetDataObject}/GetDataObjectPayload.php (93%) rename src/Handler/DataObject/{ => GetDataObject}/GetDataObjectResult.php (89%) rename src/Handler/DataObject/{ => GetDataObjectChildren}/GetDataObjectChildrenHandler.php (94%) rename src/Handler/DataObject/{ => GetDataObjectFolder}/GetDataObjectFolderHandler.php (92%) rename src/Handler/DataObject/{ => GetDataObjectFolder}/GetDataObjectFolderResult.php (89%) rename src/Handler/DataObject/{ => GetDataObjectPreviewUrl}/GetDataObjectPreviewUrlHandler.php (79%) rename src/Handler/DataObject/{ => GetIdPathPagingInfo}/GetIdPathPagingInfoHandler.php (95%) rename src/Handler/DataObject/{ => GetIdPathPagingInfo}/GetIdPathPagingInfoResult.php (89%) rename src/Handler/DataObject/{ => GetSelectOptions}/GetSelectOptionsHandler.php (94%) rename src/{Payload/DataObject => Handler/DataObject/GetSelectOptions}/GetSelectOptionsPayload.php (94%) rename src/Handler/DataObject/Helper/{ => ApplyGridConfigToAll}/ApplyGridConfigToAllHandler.php (94%) rename src/Handler/DataObject/Helper/{ => ApplyGridConfigToAll}/ApplyGridConfigToAllPayload.php (93%) delete mode 100644 src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php rename src/Handler/DataObject/Helper/{ => DoDataObjectExport}/DoDataObjectExportHandler.php (98%) rename src/Handler/DataObject/Helper/{ => DoDataObjectExport}/DoDataObjectExportPayload.php (96%) rename src/Handler/DataObject/Helper/{ => ExecuteBatch}/ExecuteBatchHandler.php (93%) rename src/Handler/DataObject/Helper/{ => ExecuteBatch}/ExecuteBatchPayload.php (94%) rename src/Handler/DataObject/Helper/{ => GetAvailableVisibleFields}/GetAvailableVisibleFieldsHandler.php (97%) rename src/Handler/DataObject/Helper/{ => GetAvailableVisibleFields}/GetAvailableVisibleFieldsPayload.php (91%) rename src/Handler/DataObject/Helper/{ => GetAvailableVisibleFields}/GetAvailableVisibleFieldsResult.php (87%) rename src/Handler/DataObject/Helper/{ => GetBatchJobs}/GetBatchJobsHandler.php (93%) rename src/Handler/DataObject/Helper/{ => GetBatchJobs}/GetBatchJobsPayload.php (93%) rename src/Handler/DataObject/Helper/{ => GetBatchJobs}/GetBatchJobsResult.php (89%) rename src/Handler/DataObject/Helper/{ => GetExportConfigs}/GetExportConfigsHandler.php (94%) rename src/Handler/DataObject/Helper/{ => GetExportConfigs}/GetExportConfigsPayload.php (92%) rename src/Handler/DataObject/Helper/{ => GetExportJobs}/GetExportJobsHandler.php (96%) rename src/Handler/DataObject/Helper/{ => GetExportJobs}/GetExportJobsPayload.php (93%) rename src/Handler/DataObject/Helper/{ => GetExportJobs}/GetExportJobsResult.php (89%) rename src/Handler/DataObject/Helper/{ => ImportUpload}/ImportUploadHandler.php (93%) rename src/Handler/DataObject/Helper/{ => ImportUpload}/ImportUploadPayload.php (94%) rename src/Handler/DataObject/Helper/{ => LoadObjectData}/LoadObjectDataHandler.php (92%) rename src/Handler/DataObject/Helper/{ => LoadObjectData}/LoadObjectDataPayload.php (92%) rename src/Handler/DataObject/Helper/{ => MarkDataObjectGridConfigFavourite}/MarkDataObjectGridConfigFavouriteHandler.php (96%) rename src/Handler/DataObject/Helper/{ => MarkDataObjectGridConfigFavourite}/MarkDataObjectGridConfigFavouritePayload.php (93%) rename src/Handler/DataObject/Helper/{ => MarkDataObjectGridConfigFavourite}/MarkDataObjectGridConfigFavouriteResult.php (87%) rename src/Handler/DataObject/Helper/{ => PrepareHelperColumnConfigs}/PrepareHelperColumnConfigsHandler.php (86%) rename src/Handler/DataObject/Helper/{ => PrepareHelperColumnConfigs}/PrepareHelperColumnConfigsPayload.php (60%) rename src/Handler/DataObject/Helper/{ => SaveDataObjectGridColumnConfig}/SaveDataObjectGridColumnConfigHandler.php (98%) rename src/Handler/DataObject/Helper/{ => SaveDataObjectGridColumnConfig}/SaveDataObjectGridColumnConfigPayload.php (94%) rename src/Handler/DataObject/Helper/{ => SaveDataObjectGridColumnConfig}/SaveDataObjectGridColumnConfigResult.php (88%) rename src/Handler/DataObject/ObjectBrick/{ => DeleteObjectBrick}/DeleteObjectBrickHandler.php (97%) rename src/Handler/DataObject/ObjectBrick/{ => ExportObjectBrick}/ExportObjectBrickHandler.php (98%) rename src/Handler/DataObject/ObjectBrick/{ => ExportObjectBrick}/ExportObjectBrickResult.php (96%) rename src/Handler/DataObject/ObjectBrick/{ => GetBrickUsages}/BrickUsagesResult.php (97%) rename src/Handler/DataObject/ObjectBrick/{ => GetBrickUsages}/GetBrickUsagesHandler.php (98%) rename src/Handler/DataObject/ObjectBrick/{ => GetObjectBrick}/GetObjectBrickHandler.php (98%) rename src/Handler/DataObject/ObjectBrick/{ => GetObjectBrick}/GetObjectBrickResult.php (97%) rename src/Handler/DataObject/ObjectBrick/{ => GetObjectBrickList}/GetObjectBrickListHandler.php (99%) rename src/Handler/DataObject/ObjectBrick/{ => GetObjectBrickList}/ObjectBrickListResult.php (96%) rename src/Handler/DataObject/ObjectBrick/{ => GetObjectBrickTree}/GetObjectBrickTreeHandler.php (99%) rename src/Handler/DataObject/ObjectBrick/{ => GetObjectBrickTree}/ObjectBrickTreeResult.php (96%) rename src/Handler/DataObject/ObjectBrick/{ => ImportObjectBrick}/ImportObjectBrickHandler.php (98%) rename src/Handler/DataObject/ObjectBrick/{ => UpdateObjectBrick}/UpdateObjectBrickHandler.php (99%) rename src/Handler/DataObject/QuantityValue/{ => ConvertAllQuantityValues}/ConvertAllQuantityValuesHandler.php (98%) rename src/Handler/DataObject/QuantityValue/{ => ConvertAllQuantityValues}/ConvertAllQuantityValuesResult.php (95%) rename src/Handler/DataObject/QuantityValue/{ => ConvertQuantityValue}/ConvertQuantityValueHandler.php (98%) rename src/Handler/DataObject/QuantityValue/{ => ConvertQuantityValue}/ConvertQuantityValueResult.php (96%) rename src/Handler/DataObject/QuantityValue/{ => ExportQuantityValueUnits}/ExportQuantityValueUnitsHandler.php (96%) rename src/Handler/DataObject/QuantityValue/{ => GetQuantityValueUnitList}/GetQuantityValueUnitListHandler.php (98%) rename src/Handler/DataObject/QuantityValue/{ => GetQuantityValueUnitList}/GetQuantityValueUnitListResult.php (95%) rename src/Handler/DataObject/QuantityValue/{ => GetQuantityValueUnits}/GetQuantityValueUnitsHandler.php (98%) rename src/Handler/DataObject/QuantityValue/{ => GetQuantityValueUnits}/GetQuantityValueUnitsResult.php (96%) rename src/Handler/DataObject/QuantityValue/{ => ImportQuantityValueUnits}/ImportQuantityValueUnitsHandler.php (97%) rename src/Handler/DataObject/{ => SaveDataObjectFolder}/SaveDataObjectFolderHandler.php (95%) rename src/{Payload/DataObject => Handler/DataObject/SaveDataObjectFolder}/SaveDataObjectFolderPayload.php (94%) rename src/Handler/DataObject/{ => TreeGetChildrenById}/TreeGetChildrenByIdHandler.php (96%) rename src/{Payload/DataObject => Handler/DataObject/TreeGetChildrenById}/TreeGetChildrenByIdPayload.php (89%) rename src/Handler/DataObject/{ => UpdateDataObject}/UpdateDataObjectHandler.php (98%) rename src/{Payload/DataObject => Handler/DataObject/UpdateDataObject}/UpdateDataObjectPayload.php (94%) rename src/Handler/DataObject/{ => UpdateDataObject}/UpdateDataObjectResult.php (89%) rename src/Handler/DataObject/Variants/{ => GetVariants}/GetVariantsHandler.php (99%) rename src/Handler/DataObject/Variants/{ => GetVariants}/GetVariantsResult.php (98%) rename src/Handler/DataObject/Variants/{ => UpdateObjectKey}/UpdateObjectKeyHandler.php (98%) rename src/Handler/DataObject/Variants/{ => UpdateObjectKey}/UpdateObjectKeyResult.php (97%) create mode 100644 src/Handler/DataObject/Version/PreviewVersion/PreviewVersionPayload.php create mode 100644 src/Handler/Document/Copy/CopyInfo/CopyInfoHandler.php create mode 100644 src/Handler/Document/Copy/CopyInfo/CopyInfoPayload.php create mode 100644 src/Handler/Document/Copy/CopyInfo/CopyInfoResult.php delete mode 100644 src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsHandler.php delete mode 100644 src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsResult.php rename src/Handler/Document/{ => GetDocumentChildren}/GetDocumentChildrenHandler.php (94%) rename src/Handler/Document/{ => GetDocumentChildren}/GetDocumentChildrenResult.php (88%) create mode 100644 src/Handler/Element/AddNote/AddNoteHandler.php create mode 100644 src/Handler/Element/AddNote/AddNotePayload.php delete mode 100644 src/Handler/Element/AddNoteHandler.php create mode 100644 src/Handler/Element/AnalyzePermissions/AnalyzePermissionsHandler.php create mode 100644 src/Handler/Element/AnalyzePermissions/AnalyzePermissionsPayload.php create mode 100644 src/Handler/Element/AnalyzePermissions/AnalyzePermissionsResult.php delete mode 100644 src/Handler/Element/AnalyzePermissionsHandler.php create mode 100644 src/Handler/Element/DeleteAllVersions/DeleteAllVersionsHandler.php create mode 100644 src/Handler/Element/DeleteAllVersions/DeleteAllVersionsPayload.php create mode 100644 src/Handler/Element/DeleteAllVersions/DeleteAllVersionsResult.php delete mode 100644 src/Handler/Element/DeleteAllVersionsHandler.php create mode 100644 src/Handler/Element/DeleteDraft/DeleteDraftHandler.php create mode 100644 src/Handler/Element/DeleteNote/DeleteNoteHandler.php delete mode 100644 src/Handler/Element/DeleteNoteHandler.php create mode 100644 src/Handler/Element/DeleteVersion/DeleteVersionHandler.php rename src/Handler/Element/{ => FindUsages}/FindUsagesHandler.php (54%) create mode 100644 src/Handler/Element/FindUsages/FindUsagesPayload.php create mode 100644 src/Handler/Element/FindUsages/FindUsagesResult.php rename src/Handler/Element/{ => GetDeleteInfo}/GetDeleteInfoHandler.php (96%) create mode 100644 src/Handler/Element/GetDeleteInfo/GetDeleteInfoPayload.php create mode 100644 src/Handler/Element/GetDependencies/GetDependenciesResult.php create mode 100644 src/Handler/Element/GetDependenciesPayload.php rename src/Handler/Element/{ => GetNicePath}/GetNicePathHandler.php (80%) create mode 100644 src/Handler/Element/GetNicePath/GetNicePathPayload.php create mode 100644 src/Handler/Element/GetNicePath/GetNicePathResult.php rename src/Handler/Element/{ => GetNoteList}/GetNoteListHandler.php (88%) create mode 100644 src/Handler/Element/GetNoteList/GetNoteListResult.php create mode 100644 src/Handler/Element/GetNoteTypes/GetNoteTypesHandler.php create mode 100644 src/Handler/Element/GetNoteTypes/GetNoteTypesPayload.php create mode 100644 src/Handler/Element/GetNoteTypes/GetNoteTypesResult.php create mode 100644 src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesHandler.php create mode 100644 src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesPayload.php create mode 100644 src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesResult.php delete mode 100644 src/Handler/Element/GetPredefinedPropertiesHandler.php create mode 100644 src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsHandler.php create mode 100644 src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsPayload.php create mode 100644 src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsResult.php create mode 100644 src/Handler/Element/GetRequiredByDependencies/GetRequiredByDependenciesHandler.php create mode 100644 src/Handler/Element/GetRequiredByDependencies/GetRequiredByDependenciesPayload.php delete mode 100644 src/Handler/Element/GetRequiredByDependenciesHandler.php create mode 100644 src/Handler/Element/GetRequiresDependencies/GetRequiresDependenciesHandler.php create mode 100644 src/Handler/Element/GetRequiresDependencies/GetRequiresDependenciesPayload.php delete mode 100644 src/Handler/Element/GetRequiresDependenciesHandler.php rename src/Handler/Element/{ => GetSubtype}/GetSubtypeHandler.php (65%) create mode 100644 src/Handler/Element/GetSubtype/GetSubtypePayload.php create mode 100644 src/Handler/Element/GetSubtype/GetSubtypeResult.php delete mode 100644 src/Handler/Element/GetSubtypeResult.php rename src/Handler/Element/{ => GetVersions}/GetVersionsHandler.php (69%) create mode 100644 src/Handler/Element/GetVersions/GetVersionsPayload.php create mode 100644 src/Handler/Element/GetVersions/GetVersionsResult.php delete mode 100644 src/Handler/Element/GetVersionsResult.php create mode 100644 src/Handler/Element/LockElement/LockElementHandler.php create mode 100644 src/Handler/Element/LockElement/LockElementPayload.php create mode 100644 src/Handler/Element/LockElement/LockElementResult.php rename src/Handler/Element/{ => ReplaceAssignments}/ReplaceAssignmentsHandler.php (60%) create mode 100644 src/Handler/Element/ReplaceAssignments/ReplaceAssignmentsPayload.php create mode 100644 src/Handler/Element/ReplaceAssignments/ReplaceAssignmentsResult.php create mode 100644 src/Handler/Element/TypePath/TypePathHandler.php create mode 100644 src/Handler/Element/TypePath/TypePathPayload.php create mode 100644 src/Handler/Element/TypePath/TypePathResult.php delete mode 100644 src/Handler/Element/TypePathHandler.php delete mode 100644 src/Handler/Element/TypePathResult.php create mode 100644 src/Handler/Element/UnlockElement/UnlockElementHandler.php create mode 100644 src/Handler/Element/UnlockElement/UnlockElementPayload.php create mode 100644 src/Handler/Element/UnlockElement/UnlockElementResult.php delete mode 100644 src/Handler/Element/UnlockElementHandler.php create mode 100644 src/Handler/Element/UnlockElements/UnlockElementsHandler.php create mode 100644 src/Handler/Element/UnlockElements/UnlockElementsPayload.php create mode 100644 src/Handler/Element/UnlockElements/UnlockElementsResult.php delete mode 100644 src/Handler/Element/UnlockElementsHandler.php create mode 100644 src/Handler/Element/UnlockPropagate/UnlockPropagateHandler.php create mode 100644 src/Handler/Element/UnlockPropagate/UnlockPropagatePayload.php create mode 100644 src/Handler/Element/UnlockPropagate/UnlockPropagateResult.php create mode 100644 src/Handler/Element/VersionUpdate/VersionUpdateHandler.php create mode 100644 src/Handler/Element/VersionUpdate/VersionUpdatePayload.php create mode 100644 src/Handler/Element/VersionUpdate/VersionUpdateResult.php delete mode 100644 src/Handler/Element/VersionUpdateHandler.php create mode 100644 src/Handler/Email/Blocklist/CreateBlocklistEntry/CreateBlocklistEntryHandler.php create mode 100644 src/Handler/Email/Blocklist/DeleteBlocklistEntry/DeleteBlocklistEntryHandler.php create mode 100644 src/Handler/Email/Blocklist/UpdateBlocklistEntry/UpdateBlocklistEntryHandler.php delete mode 100644 src/Handler/Email/CreateBlocklistEntryHandler.php delete mode 100644 src/Handler/Email/DeleteBlocklistEntryHandler.php create mode 100644 src/Handler/Email/DeleteEmailLog/DeleteEmailLogHandler.php delete mode 100644 src/Handler/Email/DeleteEmailLogHandler.php rename src/Handler/Email/{ => GetBlocklist}/GetBlocklistHandler.php (63%) create mode 100644 src/Handler/Email/GetBlocklist/GetBlocklistResult.php delete mode 100644 src/Handler/Email/GetEmailLogHandler.php delete mode 100644 src/Handler/Email/GetEmailLogResult.php rename src/Handler/Email/{ => GetEmailLogs}/GetEmailLogsHandler.php (56%) create mode 100644 src/Handler/Email/GetEmailLogs/GetEmailLogsPayload.php create mode 100644 src/Handler/Email/GetEmailLogs/GetEmailLogsResult.php delete mode 100644 src/Handler/Email/GetEmailLogsResult.php rename src/Handler/Email/{ => ResendEmail}/ResendEmailHandler.php (76%) create mode 100644 src/Handler/Email/ResendEmail/ResendEmailPayload.php create mode 100644 src/Handler/Email/SendTestEmail/SendTestEmailHandler.php create mode 100644 src/Handler/Email/SendTestEmail/SendTestEmailPayload.php delete mode 100644 src/Handler/Email/SendTestEmailHandler.php rename src/Handler/Email/{ => ShowEmailLog/GetEmailLogParams}/GetEmailLogParamsHandler.php (85%) create mode 100644 src/Handler/Email/ShowEmailLog/GetEmailLogResult.php create mode 100644 src/Handler/Email/ShowEmailLog/ShowEmailLogHandler.php create mode 100644 src/Handler/Email/ShowEmailLog/ShowEmailLogPayload.php delete mode 100644 src/Handler/Email/UpdateBlocklistEntryHandler.php create mode 100644 src/Handler/GDPR/Asset/ExportAsset/ExportAssetHandler.php rename src/Handler/{DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsResult.php => GDPR/Asset/ExportAsset/ExportAssetResult.php} (74%) create mode 100644 src/Handler/GDPR/Asset/SearchAssets/SearchAssetsHandler.php rename src/Handler/{Email/GetBlocklistResult.php => GDPR/Asset/SearchAssets/SearchAssetsResult.php} (83%) create mode 100644 src/Handler/GDPR/DataObject/ExportDataObject/ExportDataObjectHandler.php create mode 100644 src/Handler/GDPR/DataObject/ExportDataObject/ExportDataObjectResult.php create mode 100644 src/Handler/GDPR/DataObject/SearchDataObjects/SearchDataObjectsHandler.php create mode 100644 src/Handler/GDPR/DataObject/SearchDataObjects/SearchDataObjectsResult.php create mode 100644 src/Handler/GDPR/GetDataProviders/GetDataProvidersHandler.php create mode 100644 src/Handler/GDPR/GetDataProviders/GetDataProvidersResult.php create mode 100644 src/Handler/GDPR/OpenDxpUsers/ExportUserData/ExportUserDataHandler.php rename src/Handler/{Element/GetNoteListResult.php => GDPR/OpenDxpUsers/ExportUserData/ExportUserDataResult.php} (82%) create mode 100644 src/Handler/GDPR/OpenDxpUsers/SearchUsers/SearchUsersHandler.php create mode 100644 src/Handler/GDPR/OpenDxpUsers/SearchUsers/SearchUsersResult.php create mode 100644 src/Handler/GDPR/SearchDataPayload.php rename src/Handler/{Element/GetReplaceAssignmentsBatchJobsHandler.php => GDPR/SentMail/ExportSentMail/ExportSentMailHandler.php} (52%) create mode 100644 src/Handler/GDPR/SentMail/ExportSentMail/ExportSentMailResult.php create mode 100644 src/Handler/Install/CheckSystem/CheckSystemHandler.php create mode 100644 src/Handler/Install/CheckSystem/CheckSystemPayload.php create mode 100644 src/Handler/Install/CheckSystem/CheckSystemResult.php create mode 100644 src/Handler/Login/Deeplink/DeeplinkHandler.php create mode 100644 src/Handler/Login/Deeplink/DeeplinkPayload.php create mode 100644 src/Handler/Login/Deeplink/DeeplinkResult.php rename src/Handler/Login/{ => GenerateTwoFactorSetup}/GenerateTwoFactorSetupHandler.php (73%) create mode 100644 src/Handler/Login/GenerateTwoFactorSetup/GenerateTwoFactorSetupPayload.php create mode 100644 src/Handler/Login/GenerateTwoFactorSetup/GenerateTwoFactorSetupResult.php rename src/Handler/Login/{ => LostPassword}/LostPasswordHandler.php (76%) create mode 100644 src/Handler/Login/LostPassword/LostPasswordPayload.php create mode 100644 src/Handler/Login/LostPassword/LostPasswordResult.php rename src/Handler/Login/{ => SaveTwoFactorSetup}/SaveTwoFactorSetupHandler.php (54%) create mode 100644 src/Handler/Login/SaveTwoFactorSetup/SaveTwoFactorSetupPayload.php create mode 100644 src/Handler/Misc/AdminCss/AdminCssHandler.php create mode 100644 src/Handler/Misc/AdminCss/AdminCssResult.php create mode 100644 src/Handler/Misc/GetAvailableControllerReferences/GetAvailableControllerReferencesHandler.php rename src/Handler/{Element/FindUsagesResult.php => Misc/GetAvailableControllerReferences/GetAvailableControllerReferencesResult.php} (80%) rename src/Handler/{Element/DeleteVersionHandler.php => Misc/GetAvailableLanguages/GetAvailableLanguagesHandler.php} (59%) rename src/Handler/{Asset/Copy/ChildIdsResult.php => Misc/GetAvailableLanguages/GetAvailableLanguagesResult.php} (78%) create mode 100644 src/Handler/Misc/GetAvailableTemplates/GetAvailableTemplatesHandler.php create mode 100644 src/Handler/Misc/GetAvailableTemplates/GetAvailableTemplatesResult.php create mode 100644 src/Handler/Misc/GetCountryList/GetCountryListHandler.php rename src/Handler/{Element/AnalyzePermissionsResult.php => Misc/GetCountryList/GetCountryListResult.php} (84%) rename src/Handler/Misc/{ => GetIconList}/GetIconListHandler.php (89%) create mode 100644 src/Handler/Misc/GetIconList/GetIconListPayload.php rename src/Handler/Misc/{ => GetIconList}/GetIconListResult.php (92%) rename src/Handler/Misc/{ => GetJsonTranslations}/GetJsonTranslationsHandler.php (57%) create mode 100644 src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsPayload.php rename src/Handler/Misc/{ => GetJsonTranslations}/GetJsonTranslationsResult.php (89%) rename src/Handler/{Element/LockElementHandler.php => Misc/GetLanguageFlag/GetLanguageFlagHandler.php} (59%) create mode 100644 src/Handler/Misc/GetLanguageFlag/GetLanguageFlagPayload.php rename src/Handler/{Element/GetRequiresDependenciesResult.php => Misc/GetLanguageFlag/GetLanguageFlagResult.php} (79%) create mode 100644 src/Handler/Misc/GetLanguageList/GetLanguageListHandler.php create mode 100644 src/Handler/Misc/GetLanguageList/GetLanguageListResult.php rename src/Handler/{Element/UnlockPropagateHandler.php => Misc/GetValidFilename/GetValidFilenameHandler.php} (63%) create mode 100644 src/Handler/Misc/GetValidFilename/GetValidFilenamePayload.php rename src/Handler/{Element/GetPredefinedPropertiesResult.php => Misc/GetValidFilename/GetValidFilenameResult.php} (79%) create mode 100644 src/Handler/Misc/Maintenance/MaintenanceHandler.php create mode 100644 src/Handler/Misc/Maintenance/MaintenancePayload.php create mode 100644 src/Handler/Misc/ScriptProxy/ScriptProxyHandler.php create mode 100644 src/Handler/Misc/ScriptProxy/ScriptProxyPayload.php create mode 100644 src/Handler/Misc/ScriptProxy/ScriptProxyResult.php rename src/Handler/Notification/{ => DeleteAllNotifications}/DeleteAllNotificationsHandler.php (56%) rename src/Handler/Notification/{ => DeleteNotification}/DeleteNotificationHandler.php (58%) rename src/Handler/Notification/{ => FindAllNotifications}/FindAllNotificationsHandler.php (60%) create mode 100644 src/Handler/Notification/FindAllNotifications/FindAllNotificationsPayload.php rename src/Handler/Notification/{ => FindAllNotifications}/FindAllNotificationsResult.php (89%) rename src/Handler/Notification/{ => FindLastUnreadNotifications}/FindLastUnreadNotificationsHandler.php (62%) create mode 100644 src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsPayload.php rename src/Handler/Notification/{ => FindLastUnreadNotifications}/FindLastUnreadNotificationsResult.php (88%) rename src/Handler/Notification/{ => FindNotification}/FindNotificationHandler.php (63%) rename src/Handler/Notification/{ => FindNotification}/FindNotificationResult.php (89%) rename src/Handler/Notification/{ => GetRecipients}/GetRecipientsHandler.php (66%) rename src/Handler/Notification/{ => GetRecipients}/GetRecipientsResult.php (89%) rename src/Handler/Notification/{ => MarkAsReadNotification}/MarkAsReadNotificationHandler.php (58%) create mode 100644 src/Handler/Notification/SendNotification/SendNotificationHandler.php create mode 100644 src/Handler/Notification/SendNotification/SendNotificationPayload.php delete mode 100644 src/Handler/Notification/SendNotificationHandler.php rename src/Handler/Portal/{ => AddWidget}/AddWidgetHandler.php (77%) create mode 100644 src/Handler/Portal/AddWidget/AddWidgetPayload.php rename src/Handler/Portal/{ => AddWidget}/AddWidgetResult.php (83%) rename src/Handler/Portal/{ => CreateDashboard}/CreateDashboardHandler.php (76%) create mode 100644 src/Handler/Portal/CreateDashboard/CreateDashboardPayload.php rename src/Handler/Portal/{ => DeleteDashboard}/DeleteDashboardHandler.php (78%) create mode 100644 src/Handler/Portal/DeleteDashboard/DeleteDashboardPayload.php rename src/Handler/Portal/{ => GetDashboardConfiguration}/GetDashboardConfigurationHandler.php (76%) create mode 100644 src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationPayload.php rename src/Handler/Portal/{ => GetDashboardConfiguration}/GetDashboardConfigurationResult.php (79%) rename src/Handler/Portal/{ => GetDashboardList}/GetDashboardListHandler.php (82%) rename src/Handler/Portal/{ => GetDashboardList}/GetDashboardListResult.php (81%) rename src/Handler/Portal/{ => GetModificationStatistics}/GetModificationStatisticsHandler.php (89%) rename src/Handler/Portal/{ => GetModificationStatistics}/GetModificationStatisticsResult.php (87%) rename src/Handler/Portal/{ => GetModifiedAssets}/GetModifiedAssetsHandler.php (87%) rename src/Handler/Portal/{ => GetModifiedAssets}/GetModifiedAssetsResult.php (87%) rename src/Handler/Portal/{ => GetModifiedDocuments}/GetModifiedDocumentsHandler.php (87%) rename src/Handler/Portal/{ => GetModifiedDocuments}/GetModifiedDocumentsResult.php (87%) rename src/Handler/Portal/{ => GetModifiedObjects}/GetModifiedObjectsHandler.php (87%) rename src/Handler/Portal/{ => GetModifiedObjects}/GetModifiedObjectsResult.php (87%) rename src/Handler/Portal/{ => RemoveWidget}/RemoveWidgetHandler.php (75%) create mode 100644 src/Handler/Portal/RemoveWidget/RemoveWidgetPayload.php rename src/Handler/Portal/{ => ReorderWidget}/ReorderWidgetHandler.php (72%) create mode 100644 src/Handler/Portal/ReorderWidget/ReorderWidgetPayload.php rename src/Handler/Portal/{ => UpdatePortletConfig}/UpdatePortletConfigHandler.php (67%) create mode 100644 src/Handler/Portal/UpdatePortletConfig/UpdatePortletConfigPayload.php rename src/Handler/Recyclebin/{ => AddToRecyclebin}/AddToRecyclebinHandler.php (85%) create mode 100644 src/Handler/Recyclebin/AddToRecyclebin/AddToRecyclebinPayload.php rename src/Handler/Recyclebin/{ => DeleteRecyclebinItem}/DeleteRecyclebinItemHandler.php (84%) rename src/Handler/{Login/LostPasswordResult.php => Recyclebin/FlushRecyclebin/FlushRecyclebinHandler.php} (56%) rename src/Handler/Recyclebin/{ => ListRecyclebin}/ListRecyclebinHandler.php (96%) rename src/Handler/Recyclebin/{ => ListRecyclebin}/ListRecyclebinResult.php (90%) rename src/Handler/Recyclebin/{ => RestoreRecyclebinItem}/RestoreRecyclebinItemHandler.php (79%) rename src/Handler/{DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsPayload.php => Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemPayload.php} (68%) rename src/Handler/Settings/{ => AddThumbnail}/AddThumbnailHandler.php (76%) create mode 100644 src/Handler/Settings/AddThumbnail/AddThumbnailPayload.php rename src/Handler/Settings/{ => AddThumbnail}/AddThumbnailResult.php (90%) rename src/Handler/Settings/{ => AddVideoThumbnail}/AddVideoThumbnailHandler.php (75%) create mode 100644 src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailPayload.php rename src/Handler/Settings/{ => AddVideoThumbnail}/AddVideoThumbnailResult.php (90%) create mode 100644 src/Handler/Settings/ClearCache/ClearCacheHandler.php create mode 100644 src/Handler/Settings/ClearCache/ClearCachePayload.php create mode 100644 src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php rename src/Handler/Settings/{ => ClearOutputCache}/ClearOutputCacheHandler.php (93%) create mode 100644 src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php rename src/Handler/Settings/{ => ClearTemporaryFiles}/ClearTemporaryFilesHandler.php (94%) rename src/Handler/Settings/{ => CreatePredefinedMetadata}/CreatePredefinedMetadataHandler.php (91%) rename src/Handler/Settings/{ => CreatePredefinedMetadata}/CreatePredefinedMetadataResult.php (88%) rename src/Handler/Settings/{ => CreatePredefinedProperty}/CreatePredefinedPropertyHandler.php (88%) rename src/Handler/Settings/{ => CreatePredefinedProperty}/CreatePredefinedPropertyResult.php (88%) rename src/Handler/Settings/{ => CreateWebsiteSetting}/CreateWebsiteSettingHandler.php (86%) rename src/Handler/Settings/{ => CreateWebsiteSetting}/CreateWebsiteSettingResult.php (89%) rename src/Handler/Settings/{ => DeleteCustomLogo}/DeleteCustomLogoHandler.php (92%) rename src/Handler/Settings/{ => DeletePredefinedMetadata}/DeletePredefinedMetadataHandler.php (85%) rename src/Handler/Settings/{ => DeletePredefinedProperty}/DeletePredefinedPropertyHandler.php (85%) rename src/Handler/Settings/{ => DeleteThumbnail}/DeleteThumbnailHandler.php (71%) create mode 100644 src/Handler/Settings/DeleteThumbnail/DeleteThumbnailPayload.php rename src/Handler/Settings/{ => DeleteVideoThumbnail}/DeleteVideoThumbnailHandler.php (70%) create mode 100644 src/Handler/Settings/DeleteVideoThumbnail/DeleteVideoThumbnailPayload.php rename src/Handler/Settings/{ => DeleteWebsiteSetting}/DeleteWebsiteSettingHandler.php (85%) rename src/Handler/Settings/{ => DisplayCustomLogo}/DisplayCustomLogoHandler.php (94%) rename src/Handler/Settings/{ => DisplayCustomLogo}/DisplayCustomLogoResult.php (90%) rename src/Handler/Settings/{ => GetAppearanceSettings}/GetAppearanceSettingsHandler.php (91%) rename src/Handler/Settings/{ => GetAppearanceSettings}/GetAppearanceSettingsResult.php (88%) rename src/Handler/Settings/{ => GetAvailableAdminLanguages}/GetAvailableAdminLanguagesHandler.php (93%) rename src/Handler/Settings/{ => GetAvailableAdminLanguages}/GetAvailableAdminLanguagesResult.php (88%) rename src/Handler/Settings/{ => GetAvailableAlgorithms}/GetAvailableAlgorithmsHandler.php (93%) rename src/Handler/Settings/{ => GetAvailableAlgorithms}/GetAvailableAlgorithmsResult.php (88%) rename src/Handler/Settings/{ => GetAvailableCountries}/GetAvailableCountriesHandler.php (93%) rename src/Handler/Settings/{ => GetAvailableCountries}/GetAvailableCountriesResult.php (88%) rename src/Handler/Settings/{ => GetAvailableSites}/GetAvailableSitesHandler.php (96%) rename src/Handler/Settings/{ => GetAvailableSites}/GetAvailableSitesResult.php (89%) rename src/Handler/Settings/{ => GetDownloadableThumbnails}/GetDownloadableThumbnailsHandler.php (93%) rename src/Handler/Settings/{ => GetDownloadableThumbnails}/GetDownloadableThumbnailsResult.php (88%) rename src/Handler/Settings/{ => GetFilteredPredefinedMetadata}/GetFilteredPredefinedMetadataHandler.php (93%) rename src/Handler/Settings/{ => GetFilteredPredefinedMetadata}/GetFilteredPredefinedMetadataResult.php (88%) rename src/Handler/Settings/{ => GetPredefinedMetadataList}/GetPredefinedMetadataListHandler.php (90%) rename src/Handler/Settings/{ => GetPredefinedMetadataList}/GetPredefinedMetadataListResult.php (89%) rename src/Handler/Settings/{ => GetPredefinedPropertiesList}/GetPredefinedPropertiesListHandler.php (91%) rename src/Handler/Settings/{ => GetPredefinedPropertiesList}/GetPredefinedPropertiesListResult.php (88%) rename src/Handler/Settings/{ => GetSystemSettings}/GetSystemSettingsHandler.php (96%) rename src/Handler/Settings/{ => GetSystemSettings}/GetSystemSettingsResult.php (90%) rename src/Handler/Settings/{ => GetThumbnail}/GetThumbnailHandler.php (92%) rename src/Handler/Settings/{ => GetThumbnail}/GetThumbnailResult.php (90%) rename src/Handler/Settings/{ => GetThumbnailTree}/GetThumbnailTreeHandler.php (97%) rename src/Handler/Settings/{ => GetThumbnailTree}/GetThumbnailTreeResult.php (89%) rename src/Handler/Settings/{ => GetVideoThumbnail}/GetVideoThumbnailHandler.php (92%) rename src/Handler/Settings/{ => GetVideoThumbnail}/GetVideoThumbnailResult.php (89%) rename src/Handler/Settings/{ => GetVideoThumbnailList}/GetVideoThumbnailListHandler.php (93%) rename src/Handler/Settings/{ => GetVideoThumbnailList}/GetVideoThumbnailListResult.php (89%) rename src/Handler/Settings/{ => GetVideoThumbnailTree}/GetVideoThumbnailTreeHandler.php (96%) rename src/Handler/Settings/{ => GetVideoThumbnailTree}/GetVideoThumbnailTreeResult.php (89%) rename src/Handler/Settings/{ => GetWebsiteSettingsList}/GetWebsiteSettingsListHandler.php (94%) rename src/Handler/Settings/{ => GetWebsiteSettingsList}/GetWebsiteSettingsListResult.php (89%) rename src/Handler/Settings/{ => SaveAppearanceSettings}/SaveAppearanceSettingsHandler.php (59%) create mode 100644 src/Handler/Settings/SaveSettingsPayload.php rename src/Handler/Settings/{ => SaveSystemSettings}/SaveSystemSettingsHandler.php (59%) rename src/Handler/Settings/{ => ThumbnailAdapterCheck}/ThumbnailAdapterCheckHandler.php (94%) rename src/Handler/Settings/{ => ThumbnailAdapterCheck}/ThumbnailAdapterCheckResult.php (88%) rename src/Handler/Settings/{ => UpdatePredefinedMetadata}/UpdatePredefinedMetadataHandler.php (91%) rename src/Handler/Settings/{ => UpdatePredefinedMetadata}/UpdatePredefinedMetadataResult.php (88%) rename src/Handler/Settings/{ => UpdatePredefinedProperty}/UpdatePredefinedPropertyHandler.php (89%) rename src/Handler/Settings/{ => UpdatePredefinedProperty}/UpdatePredefinedPropertyResult.php (88%) rename src/Handler/Settings/{ => UpdateThumbnail}/UpdateThumbnailHandler.php (77%) create mode 100644 src/Handler/Settings/UpdateThumbnail/UpdateThumbnailPayload.php rename src/Handler/Settings/{ => UpdateVideoThumbnail}/UpdateVideoThumbnailHandler.php (75%) create mode 100644 src/Handler/Settings/UpdateVideoThumbnail/UpdateVideoThumbnailPayload.php rename src/Handler/Settings/{ => UpdateWebsiteSetting}/UpdateWebsiteSettingHandler.php (94%) rename src/Handler/Settings/{ => UpdateWebsiteSetting}/UpdateWebsiteSettingResult.php (89%) rename src/Handler/Settings/{ => UploadCustomLogo}/UploadCustomLogoHandler.php (93%) rename src/Handler/Tags/{ => AddTag}/AddTagHandler.php (76%) create mode 100644 src/Handler/Tags/AddTag/AddTagPayload.php rename src/Handler/Tags/{ => AddTag}/AddTagResult.php (91%) rename src/Handler/Tags/{ => AddTagToElement}/AddTagToElementHandler.php (66%) create mode 100644 src/Handler/Tags/AddTagToElement/AddTagToElementPayload.php rename src/Handler/Tags/{ => AddTagToElement}/AddTagToElementResult.php (90%) rename src/Handler/Tags/{ => DeleteTag}/DeleteTagHandler.php (73%) rename src/Handler/{Asset/Copy/GetAssetChildIds/GetAssetChildIdsPayload.php => Tags/DeleteTag/DeleteTagPayload.php} (75%) rename src/Handler/Tags/{ => DoBatchAssignment}/DoBatchAssignmentHandler.php (68%) create mode 100644 src/Handler/Tags/DoBatchAssignment/DoBatchAssignmentPayload.php rename src/Handler/Tags/{ => GetBatchAssignmentJobs}/GetBatchAssignmentJobsHandler.php (95%) create mode 100644 src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsPayload.php rename src/Handler/Tags/{ => GetBatchAssignmentJobs}/GetBatchAssignmentJobsResult.php (90%) rename src/Handler/Tags/{ => GetTagTreeChildren}/GetTagTreeChildrenHandler.php (79%) create mode 100644 src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenPayload.php rename src/Handler/Tags/{ => GetTagTreeChildren}/GetTagTreeChildrenResult.php (89%) rename src/Handler/Tags/{ => LoadTagsForElement/GetTagsForElement}/GetTagsForElementHandler.php (82%) rename src/Handler/Tags/{ => LoadTagsForElement/GetTagsForElement}/GetTagsForElementResult.php (87%) create mode 100644 src/Handler/Tags/LoadTagsForElement/LoadTagsForElementPayload.php rename src/Handler/Tags/{ => RemoveTagFromElement}/RemoveTagFromElementHandler.php (65%) create mode 100644 src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementPayload.php rename src/Handler/Tags/{ => RemoveTagFromElement}/RemoveTagFromElementResult.php (89%) rename src/Handler/Tags/{ => UpdateTag}/UpdateTagHandler.php (62%) create mode 100644 src/Handler/Tags/UpdateTag/UpdateTagPayload.php rename src/Handler/Translation/{ => AddAdminTranslationKeys}/AddAdminTranslationKeysHandler.php (87%) create mode 100644 src/Handler/Translation/AddAdminTranslationKeys/AddAdminTranslationKeysPayload.php rename src/Handler/Translation/{ => BuildContentExportJobs}/BuildContentExportJobsHandler.php (88%) create mode 100644 src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsPayload.php rename src/Handler/Translation/{ => BuildContentExportJobs}/BuildContentExportJobsResult.php (89%) rename src/Handler/Translation/{ => CleanupTranslations}/CleanupTranslationsHandler.php (79%) create mode 100644 src/Handler/Translation/CleanupTranslations/CleanupTranslationsPayload.php rename src/Handler/Translation/{ => CreateTranslation}/CreateTranslationHandler.php (96%) rename src/Handler/Translation/{ => CreateTranslation}/CreateTranslationResult.php (90%) rename src/Handler/Translation/{ => DeleteTranslation}/DeleteTranslationHandler.php (91%) rename src/Handler/Translation/{ => ExportTranslations}/ExportTranslationsHandler.php (89%) create mode 100644 src/Handler/Translation/ExportTranslations/ExportTranslationsPayload.php rename src/Handler/Translation/{ => ExportTranslations}/ExportTranslationsResult.php (89%) rename src/Handler/Translation/{ => GetTranslationDomains}/GetTranslationDomainsHandler.php (79%) rename src/Handler/Translation/{ => GetTranslationDomains}/GetTranslationDomainsResult.php (88%) rename src/Handler/Translation/{ => GetTranslations}/GetTranslationsHandler.php (96%) rename src/Handler/Translation/{ => GetTranslations}/GetTranslationsResult.php (89%) rename src/Handler/Translation/{ => GetWebsiteTranslationLanguages}/GetWebsiteTranslationLanguagesHandler.php (79%) rename src/Handler/Translation/{ => GetWebsiteTranslationLanguages}/GetWebsiteTranslationLanguagesResult.php (86%) rename src/Handler/Translation/{ => ImportTranslations}/ImportTranslationsHandler.php (69%) create mode 100644 src/Handler/Translation/ImportTranslations/ImportTranslationsPayload.php rename src/Handler/Translation/{ => ImportTranslations}/ImportTranslationsResult.php (89%) rename src/Handler/Translation/{ => MergeTranslationItems}/MergeTranslationItemsHandler.php (74%) create mode 100644 src/Handler/Translation/MergeTranslationItems/MergeTranslationItemsPayload.php rename src/Handler/Translation/{ => UpdateTranslation}/UpdateTranslationHandler.php (95%) rename src/Handler/Translation/{ => UpdateTranslation}/UpdateTranslationResult.php (90%) rename src/Handler/Translation/{ => UploadTranslationImportFile}/UploadTranslationImportFileHandler.php (83%) create mode 100644 src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFilePayload.php rename src/Handler/Translation/{ => UploadTranslationImportFile}/UploadTranslationImportFileResult.php (88%) rename src/Handler/User/{ => AddUser}/AddUserHandler.php (86%) create mode 100644 src/Handler/User/AddUser/AddUserPayload.php rename src/Handler/User/{ => AddUser}/AddUserResult.php (91%) rename src/Handler/User/{ => DeleteUser}/DeleteUserHandler.php (93%) rename src/Handler/{DataObject/Helper/DeleteDataObjectGridColumnConfigPayload.php => User/DeleteUser/DeleteUserPayload.php} (74%) rename src/Handler/User/{ => DeleteUserImage}/DeleteUserImageHandler.php (87%) create mode 100644 src/Handler/User/DeleteUserImage/DeleteUserImagePayload.php rename src/Handler/User/{ => Disable2Fa}/Disable2FaHandler.php (90%) create mode 100644 src/Handler/User/Disable2Fa/Disable2FaPayload.php rename src/Handler/User/{ => GetCurrentUser}/GetCurrentUserHandler.php (90%) create mode 100644 src/Handler/User/GetCurrentUser/GetCurrentUserPayload.php rename src/Handler/User/{ => GetCurrentUser}/GetCurrentUserResult.php (90%) rename src/Handler/User/{ => GetMinimalUser}/GetMinimalUserHandler.php (85%) create mode 100644 src/Handler/User/GetMinimalUser/GetMinimalUserPayload.php rename src/Handler/User/{ => GetMinimalUser}/GetMinimalUserResult.php (91%) rename src/Handler/User/{ => GetRole}/GetRoleHandler.php (93%) create mode 100644 src/Handler/User/GetRole/GetRolePayload.php rename src/Handler/User/{ => GetRole}/GetRoleResult.php (93%) rename src/Handler/User/{ => GetRoleTreeChildren}/GetRoleTreeChildrenHandler.php (89%) rename src/Handler/{Document/Copy/GetDocumentChildIds/GetDocumentChildIdsPayload.php => User/GetRoleTreeChildren/GetRoleTreeChildrenPayload.php} (68%) rename src/Handler/User/{ => GetRoles}/GetRolesHandler.php (80%) create mode 100644 src/Handler/User/GetRoles/GetRolesPayload.php rename src/Handler/User/{ => GetTokenLoginLink}/GetTokenLoginLinkHandler.php (90%) create mode 100644 src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkPayload.php delete mode 100644 src/Handler/User/GetTokenLoginLinkResult.php rename src/Handler/User/{ => GetUser}/GetUserHandler.php (95%) create mode 100644 src/Handler/User/GetUser/GetUserPayload.php rename src/Handler/User/{ => GetUser}/GetUserResult.php (93%) rename src/Handler/User/{ => GetUserImage}/GetUserImageHandler.php (69%) create mode 100644 src/Handler/User/GetUserImage/GetUserImagePayload.php create mode 100644 src/Handler/User/GetUserImage/GetUserImageResult.php rename src/Handler/User/{ => GetUserTreeChildren}/GetUserTreeChildrenHandler.php (90%) create mode 100644 src/Handler/User/GetUserTreeChildren/GetUserTreeChildrenPayload.php rename src/Handler/User/{ => GetUsers}/GetUsersHandler.php (83%) create mode 100644 src/Handler/User/GetUsers/GetUsersPayload.php rename src/Handler/User/{ => Reset2FaSecret}/Reset2FaSecretHandler.php (83%) create mode 100644 src/Handler/User/Reset2FaSecret/Reset2FaSecretPayload.php rename src/Handler/User/{ => ResetMy2FaSecret}/ResetMy2FaSecretHandler.php (82%) rename src/Handler/User/{ => SearchUsers}/SearchUsersHandler.php (78%) create mode 100644 src/Handler/User/SearchUsers/SearchUsersPayload.php rename src/Handler/User/{ => SendInvitationLink}/SendInvitationLinkHandler.php (83%) create mode 100644 src/Handler/User/SendInvitationLink/SendInvitationLinkPayload.php rename src/Handler/User/{ => SendInvitationLink}/SendInvitationLinkResult.php (86%) rename src/Handler/User/{ => UpdateCurrentUser}/UpdateCurrentUserHandler.php (87%) create mode 100644 src/Handler/User/UpdateCurrentUser/UpdateCurrentUserPayload.php rename src/Handler/User/{ => UpdateUser}/UpdateUserHandler.php (90%) create mode 100644 src/Handler/User/UpdateUser/UpdateUserPayload.php rename src/Handler/User/{ => UploadUserImage}/UploadUserImageHandler.php (81%) create mode 100644 src/Handler/User/UploadUserImage/UploadUserImagePayload.php rename src/Handler/Workflow/{ => GetModalCustomHtml}/GetModalCustomHtmlHandler.php (81%) create mode 100644 src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlPayload.php rename src/Handler/Workflow/{ => GetModalCustomHtml}/GetModalCustomHtmlResult.php (89%) rename src/Handler/Workflow/{ => GetWorkflowDetails}/GetWorkflowDetailsHandler.php (92%) create mode 100644 src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsPayload.php rename src/Handler/Workflow/{ => GetWorkflowDetails}/GetWorkflowDetailsResult.php (89%) rename src/Handler/Workflow/{ => GetWorkflowForm}/GetWorkflowFormHandler.php (82%) create mode 100644 src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormPayload.php rename src/Handler/Workflow/{ => GetWorkflowForm}/GetWorkflowFormResult.php (91%) rename src/Handler/Workflow/{ => ShowGraph/GetWorkflowSvg}/GetWorkflowSvgHandler.php (86%) create mode 100644 src/Handler/Workflow/ShowGraph/ShowGraphPayload.php rename src/Handler/Workflow/{ => SubmitGlobalAction}/SubmitGlobalActionHandler.php (73%) create mode 100644 src/Handler/Workflow/SubmitGlobalAction/SubmitGlobalActionPayload.php rename src/Handler/Workflow/{ => SubmitWorkflowTransition}/SubmitWorkflowTransitionHandler.php (74%) create mode 100644 src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionPayload.php rename src/Handler/Workflow/{ => SubmitWorkflowTransition}/SubmitWorkflowTransitionResult.php (89%) delete mode 100644 src/Normalizer/DataObject/AdminStyleNormalizer.php delete mode 100644 src/Normalizer/DataObject/TreeStyleNormalizer.php delete mode 100644 src/Normalizer/DataObject/UserNamesNormalizer.php delete mode 100644 src/Normalizer/Document/AdminStyleNormalizer.php delete mode 100644 src/Normalizer/Document/DocumentMetaNormalizer.php delete mode 100644 src/Normalizer/Document/DraftNormalizer.php delete mode 100644 src/Normalizer/Document/PropertiesNormalizer.php delete mode 100644 src/Normalizer/Document/TranslationNormalizer.php delete mode 100644 src/Normalizer/Element/UserNamesNormalizer.php delete mode 100644 src/Normalizer/ElementResponseNormalizer.php delete mode 100644 src/Normalizer/ElementResponseNormalizerInterface.php rename src/{Handler/Settings/ClearOpenDxpCacheHandler.php => Service/Cache/OpenDxpCacheClearingService.php} (81%) rename src/{Handler/Settings/ClearSymfonyCacheHandler.php => Service/Cache/SymfonyCacheClearingService.php} (82%) diff --git a/config/services.yaml b/config/services.yaml index 68c81ed4..0da3c783 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -3,10 +3,6 @@ services: autowire: true autoconfigure: true - _instanceof: - OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface: - tags: ['opendxp.admin.normalizer.element_response'] - OpenDxp\Bundle\AdminBundle\Installer: public: true arguments: @@ -108,6 +104,8 @@ services: OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface: class: OpenDxp\Bundle\AdminBundle\Service\ElementService + OpenDxp\Bundle\AdminBundle\Service\CustomLoginUrlGenerator: ~ + OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface: class: OpenDxp\Bundle\AdminBundle\Service\AdminUserContext @@ -117,13 +115,6 @@ services: OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper: ~ - OpenDxp\Bundle\AdminBundle\Normalizer\: - resource: '../src/Normalizer' - - OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer: - arguments: - $normalizers: !tagged_iterator opendxp.admin.normalizer.element_response - OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator: ~ OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPayloadMapper: ~ @@ -161,9 +152,6 @@ services: # OpenDxp\Bundle\AdminBundle\Builder\: resource: '../src/Builder' - bind: - $customAdminRouteName: '%opendxp_admin.custom_admin_route_name%' - $secret: '%secret%' # # HTTP / Value Resolvers @@ -171,11 +159,21 @@ services: OpenDxp\Bundle\AdminBundle\Http\: resource: '../src/Http' + # + # Enrichers + # + OpenDxp\Bundle\AdminBundle\Enricher\: + resource: '../src/Enricher' + # # Handlers # OpenDxp\Bundle\AdminBundle\Handler\: resource: '../src/Handler' + bind: + $documentNoteTypes: '%opendxp_admin.dataObjects.notes_events.types%' + $assetNoteTypes: '%opendxp_admin.assets.notes_events.types%' + $objectNoteTypes: '%opendxp_admin.documents.notes_events.types%' OpenDxp\Bundle\AdminBundle\Handler\Document\AddDocument\AddDocumentHandler: arguments: diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e85a0d1c..44a96e3f 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -18,8 +18,3 @@ parameters: count: 1 path: src/DependencyInjection/OpenDxpAdminExtension.php - - - message: '#^Trait OpenDxp\\Bundle\\AdminBundle\\EventListener\\Traits\\ControllerTypeTrait is used zero times and is not analysed\.$#' - identifier: trait.unused - count: 1 - path: src/EventListener/Traits/ControllerTypeTrait.php diff --git a/src/Builder/AdminSettingsAssembler.php b/src/Builder/AdminSettingsAssembler.php index 263833c0..2805bc9e 100644 --- a/src/Builder/AdminSettingsAssembler.php +++ b/src/Builder/AdminSettingsAssembler.php @@ -42,6 +42,7 @@ use OpenDxp\Version; use OpenDxp\Video; use OpenDxp\Bundle\AdminBundle\Handler\Admin\Settings\SettingsPayload; +use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\HttpKernel\KernelInterface; @@ -58,7 +59,9 @@ public function __construct( private readonly Connection $db, private readonly KernelInterface $kernel, private readonly RequestStack $requestStack, + #[Autowire('%opendxp_admin.custom_admin_route_name%')] private readonly string $customAdminRouteName, + #[Autowire('%secret%')] private readonly string $secret, ) {} diff --git a/src/Controller/Admin/Asset/AssetController.php b/src/Controller/Admin/Asset/AssetController.php index 891541b4..7289441c 100644 --- a/src/Controller/Admin/Asset/AssetController.php +++ b/src/Controller/Admin/Asset/AssetController.php @@ -22,34 +22,33 @@ use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnail\ClearAssetThumbnailPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnailHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfo\GetDeleteInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfo\GetDeleteInfoPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolder\CreateAssetFolderPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolder\CreateAssetFolderHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAsset\DeleteAssetPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAsset\DeleteAssetHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildren\GetAssetChildrenPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildren\GetAssetChildrenHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetData\GetAssetDataPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetData\GetAssetDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GridProxy\GridProxyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GridProxy\GridProxyPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset\SaveAssetHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\UpdateAsset\UpdateAssetPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\UpdateAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\UpdateAsset\UpdateAssetHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\SaveAsset\SaveAssetPayload; use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetGridService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; -use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Controller\Traits\ElementEditLockHelperTrait; use OpenDxp\Model\Element\ElementInterface; use Override; -use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @internal @@ -62,7 +61,6 @@ class AssetController extends ElementControllerBase public function __construct( ElementServiceInterface $elementService, - private readonly AssetGridService $assetGridService, ) { parent::__construct($elementService); } @@ -70,24 +68,15 @@ public function __construct( #[Route('/grid-proxy', name: 'opendxp_admin_asset_gridproxy', methods: ['GET', 'POST', 'PUT'])] public function gridProxyAction( Request $request, - EventDispatcherInterface $eventDispatcher, + GridProxyHandler $handler, + GridProxyPayload $payload, CsrfProtectionHandler $csrfProtection, - #[MapQueryParameter] ?string $language = null, ): JsonResponse { - $allParams = [...$request->request->all(), ...$request->query->all()]; - $effectiveLanguage = $language !== 'default' ? $language : null; - - $filterPrepareEvent = new GenericEvent(null, ['requestParams' => $allParams]); - $eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE); - $allParams = $filterPrepareEvent->getArgument('requestParams'); - - if (isset($allParams['data']) && $allParams['data']) { + if (isset($payload->params['data']) && $payload->params['data']) { $csrfProtection->checkCsrfToken($request); } - return $this->adminJson( - $this->assetGridService->gridProxy($allParams, $effectiveLanguage) - ); + return $this->adminJson($handler($payload)->data); } #[Override] @@ -103,11 +92,9 @@ public function treeGetRootAction( #[Route('/delete-info', name: 'opendxp_admin_asset_deleteinfo', methods: ['GET'])] public function deleteInfoAction( GetDeleteInfoHandler $handler, - Request $request, - #[MapQueryParameter] ?string $id = null, - #[MapQueryParameter] ?string $type = null, + GetDeleteInfoPayload $payload, ): JsonResponse { - return parent::deleteInfoAction($handler, $request, $id, $type); + return parent::deleteInfoAction($handler, $payload); } #[Route('/get-data-by-id', name: 'opendxp_admin_asset_getdatabyid', methods: ['GET'])] @@ -128,12 +115,10 @@ public function getDataByIdAction( public function treeGetChildrenByIdAction( GetAssetChildrenPayload $payload, GetAssetChildrenHandler $getChildren, - Request $request, - #[MapQueryParameter] int $inSearch = 0, ): JsonResponse { $result = $getChildren($payload); - if ($request->query->has('limit')) { + if ($payload->hasLimit) { return $this->adminJson([ 'offset' => $result->offset, 'limit' => $result->limit, @@ -141,7 +126,7 @@ public function treeGetChildrenByIdAction( 'overflow' => $payload->filter !== null && ($result->filteredTotalCount > $result->limit), 'nodes' => $result->assets, 'filter' => $result->filter ?: '', - 'inSearch' => $inSearch, + 'inSearch' => $payload->inSearch, ]); } diff --git a/src/Controller/Admin/Asset/AssetCopyController.php b/src/Controller/Admin/Asset/AssetCopyController.php index 22b4f0c9..ea998df0 100644 --- a/src/Controller/Admin/Asset/AssetCopyController.php +++ b/src/Controller/Admin/Asset/AssetCopyController.php @@ -20,15 +20,14 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAsset\CopyAssetPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAssetHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\GetAssetChildIds\GetAssetChildIdsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\GetAssetChildIdsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAsset\CopyAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyInfo\CopyInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyInfo\CopyInfoPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Tool; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -41,82 +40,32 @@ class AssetCopyController extends AdminAbstractController { #[Route('/copy-info', name: 'opendxp_admin_asset_copyinfo', methods: ['GET'])] public function copyInfoAction( - GetAssetChildIdsPayload $childIdsPayload, - GetAssetChildIdsHandler $getChildIds, + CopyInfoPayload $payload, + CopyInfoHandler $handler, Request $request, - #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter] int $sourceId = 0, - #[MapQueryParameter] ?string $targetId = null, ): JsonResponse { - $transactionId = time(); - $pasteJobs = []; + $result = $handler($payload); - Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { - $session->set((string) $transactionId, []); + Tool\Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($result): void { + $session->set((string) $result->transactionId, []); }, 'opendxp_copy'); - if ($type === 'recursive') { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_asset_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $sourceId, - 'targetId' => $targetId, - 'type' => 'child', - 'transactionId' => $transactionId, - 'saveParentId' => true, - ], - ]]; - - $childIds = $getChildIds($childIdsPayload)->ids; - foreach ($childIds as $id) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_asset_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $id, - 'targetParentId' => $targetId, - 'sourceParentId' => $sourceId, - 'type' => 'child', - 'transactionId' => $transactionId, - ], - ]]; - } - } elseif ($type === 'child' || $type === 'replace') { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_asset_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $sourceId, - 'targetId' => $targetId, - 'type' => $type, - 'transactionId' => $transactionId, - ], - ]]; - } - - return $this->adminJson(['pastejobs' => $pasteJobs]); + return $this->adminJson(['pastejobs' => $result->pasteJobs]); } #[Route('/copy', name: 'opendxp_admin_asset_copy', methods: ['POST'])] - public function copyAction(CopyAssetHandler $copyAsset, Request $request): JsonResponse - { - $sourceId = (int) $request->request->get('sourceId'); - $targetId = (int) $request->request->get('targetId'); - $type = (string) $request->request->get('type'); - - $session = Tool\Session::getSessionBag($request->getSession(), 'opendxp_copy'); - $sessionBag = $session->get($request->request->get('transactionId')); - - $sourceParentId = $request->request->has('targetParentId') ? (int) $request->request->get('sourceParentId') : null; - $targetParentId = $request->request->has('targetParentId') ? (int) $request->request->get('targetParentId') : null; - $sessionParentId = $sessionBag['parentId'] ? (int) $sessionBag['parentId'] : null; - - $result = $copyAsset(new CopyAssetPayload($sourceId, $targetId, $type, $sourceParentId, $targetParentId, $sessionParentId)); + public function copyAction( + CopyAssetPayload $payload, + CopyAssetHandler $copyAsset, + Request $request, + ): JsonResponse { + $result = $copyAsset($payload); - if ($result->newAsset !== null && $request->request->get('saveParentId')) { + if ($result->newAsset !== null && $payload->saveParentId) { + $session = Tool\Session::getSessionBag($request->getSession(), 'opendxp_copy'); + $sessionBag = $session->get($payload->transactionId); $sessionBag['parentId'] = $result->newAsset->getId(); - $session->set($request->request->get('transactionId'), $sessionBag); + $session->set($payload->transactionId, $sessionBag); } return $this->adminJson(ApiResponse::ok()); diff --git a/src/Controller/Admin/Asset/AssetDownloadController.php b/src/Controller/Admin/Asset/AssetDownloadController.php index 712bc60e..f2edfda2 100644 --- a/src/Controller/Admin/Asset/AssetDownloadController.php +++ b/src/Controller/Admin/Asset/AssetDownloadController.php @@ -22,15 +22,15 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\AddFilesToZip\AddFilesToZipPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\AddFilesToZipHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\AddFilesToZip\AddFilesToZipHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAsset\DownloadAssetPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAsset\DownloadAssetHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnail\DownloadImageThumbnailPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnail\DownloadImageThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZip\DownloadZipPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZipHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZip\DownloadZipHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobs\GetDownloadZipJobsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobs\GetDownloadZipJobsHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; diff --git a/src/Controller/Admin/Asset/AssetEditorController.php b/src/Controller/Admin/Asset/AssetEditorController.php index 0b25fe3f..d58f725f 100644 --- a/src/Controller/Admin/Asset/AssetEditorController.php +++ b/src/Controller/Admin/Asset/AssetEditorController.php @@ -20,9 +20,9 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\LoadAssetForEditor\LoadAssetForEditorPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\LoadAssetForEditorHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\LoadAssetForEditor\LoadAssetForEditorHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\SaveImageEditor\SaveImageEditorPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\SaveImageEditorHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\SaveImageEditor\SaveImageEditorHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; diff --git a/src/Controller/Admin/Asset/AssetHelperController.php b/src/Controller/Admin/Asset/AssetHelperController.php index 2e4667a2..6d511a91 100644 --- a/src/Controller/Admin/Asset/AssetHelperController.php +++ b/src/Controller/Admin/Asset/AssetHelperController.php @@ -19,24 +19,25 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DeleteGridColumnConfig\DeleteGridColumnConfigPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DeleteGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DeleteGridColumnConfig\DeleteGridColumnConfigHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExport\DoAssetExportPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExport\DoAssetExportHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatch\ExecuteAssetBatchPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatchHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatch\ExecuteAssetBatchHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobs\GetAssetBatchJobsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetMetadataForColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobs\GetAssetBatchJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\PrepareHelperColumnConfigs\PrepareHelperColumnConfigsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\PrepareHelperColumnConfigs\PrepareHelperColumnConfigsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetMetadataForColumnConfig\GetAssetMetadataForColumnConfigHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobs\GetExportJobsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobs\GetExportJobsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavourite\MarkGridConfigFavouritePayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavouriteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavourite\MarkGridConfigFavouriteHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\SaveGridColumnConfig\SaveGridColumnConfigPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\SaveGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\SaveGridColumnConfig\SaveGridColumnConfigHandler; use OpenDxp\Bundle\AdminBundle\Service\Grid\AssetGridColumnConfigResolver; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; use OpenDxp\Tool\Session; -use stdClass; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -61,15 +62,14 @@ public function gridDeleteColumnConfigAction( DeleteGridColumnConfigPayload $deletePayload, DeleteGridColumnConfigHandler $deleteGridColumnConfig, Request $request, - #[MapQueryParameter(name: 'no_system_columns')] bool $noSystemColumns = false, ): JsonResponse { $params = [ - 'id' => $request->request->get('id'), - 'type' => $request->request->get('type'), - 'types' => $request->request->get('types'), - 'gridConfigId' => $request->request->get('gridConfigId'), - 'searchType' => $request->request->get('searchType'), - 'noSystemColumns' => $noSystemColumns, + 'id' => $request->request->getString('id'), + 'type' => $request->request->getString('type'), + 'types' => $request->request->getString('types'), + 'gridConfigId' => $request->request->getString('gridConfigId'), + 'searchType' => $request->request->getString('searchType'), + 'noSystemColumns' => $deletePayload->noSystemColumns, ]; $deleteGridColumnConfig($deletePayload); @@ -101,32 +101,20 @@ public function gridGetColumnConfigAction( } #[Route('/prepare-helper-column-configs', name: 'opendxp_admin_asset_assethelper_preparehelpercolumnconfigs', methods: ['POST'])] - public function prepareHelperColumnConfigs(Request $request): JsonResponse - { - $helperColumns = []; - $newData = []; - $data = json_decode($request->request->get('columns')); - - /** @var stdClass $item */ - foreach ($data as $item) { - if (!empty($item->isOperator)) { - $itemKey = '#' . uniqid('', false); - - $item->key = $itemKey; - $newData[] = $item; - $helperColumns[$itemKey] = $item; - } else { - $newData[] = $item; - } - } + public function prepareHelperColumnConfigs( + PrepareHelperColumnConfigsPayload $payload, + PrepareHelperColumnConfigsHandler $prepareHelperColumnConfigs, + Request $request, + ): JsonResponse { + $result = $prepareHelperColumnConfigs($payload); - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($helperColumns): void { + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($result): void { $existingColumns = $session->get('helpercolumns', []); - $helperColumns = [...$helperColumns, ...$existingColumns]; + $helperColumns = [...$result->helperColumns, ...$existingColumns]; $session->set('helpercolumns', $helperColumns); }, 'opendxp_gridconfig'); - return $this->adminJson(ApiResponse::ok(['columns' => $newData])); + return $this->adminJson(ApiResponse::ok(['columns' => $result->newData])); } #[Route('/grid-mark-favourite-column-config', name: 'opendxp_admin_asset_assethelper_gridmarkfavouritecolumnconfig', methods: ['POST'])] diff --git a/src/Controller/Admin/Asset/AssetMediaController.php b/src/Controller/Admin/Asset/AssetMediaController.php index 291b6d26..fba9c7ff 100644 --- a/src/Controller/Admin/Asset/AssetMediaController.php +++ b/src/Controller/Admin/Asset/AssetMediaController.php @@ -22,15 +22,15 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAsset\DownloadAssetPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAsset\DownloadAssetHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetAssetText\GetAssetTextPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetAssetTextHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetAssetText\GetAssetTextHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetDocumentPreview\GetDocumentPreviewPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetDocumentPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetDocumentPreview\GetDocumentPreviewHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetVideoPreview\GetVideoPreviewPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetVideoPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\GetVideoPreview\GetVideoPreviewHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\ServeVideoPreview\ServeVideoPreviewPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\ServeVideoPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Media\ServeVideoPreview\ServeVideoPreviewHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Model\Asset\Enum\PdfScanStatus; use Symfony\Component\HttpFoundation\JsonResponse; diff --git a/src/Controller/Admin/Asset/AssetThumbnailController.php b/src/Controller/Admin/Asset/AssetThumbnailController.php index 8d1e7447..e4a0d9c6 100644 --- a/src/Controller/Admin/Asset/AssetThumbnailController.php +++ b/src/Controller/Admin/Asset/AssetThumbnailController.php @@ -22,15 +22,15 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetDocumentThumbnail\GetDocumentThumbnailPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetDocumentThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetDocumentThumbnail\GetDocumentThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderContentPreview\GetFolderContentPreviewPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderContentPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderContentPreview\GetFolderContentPreviewHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderThumbnail\GetFolderThumbnailPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetFolderThumbnail\GetFolderThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetImageThumbnail\GetImageThumbnailPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetImageThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetImageThumbnail\GetImageThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetVideoThumbnail\GetVideoThumbnailPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetVideoThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Thumbnail\GetVideoThumbnail\GetVideoThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; diff --git a/src/Controller/Admin/Asset/AssetUploadController.php b/src/Controller/Admin/Asset/AssetUploadController.php index b9a74481..ba233e6e 100644 --- a/src/Controller/Admin/Asset/AssetUploadController.php +++ b/src/Controller/Admin/Asset/AssetUploadController.php @@ -20,13 +20,13 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\CheckAssetExists\CheckAssetExistsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\CheckAssetExistsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\CheckAssetExists\CheckAssetExistsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZipFiles\ImportZipFilesPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZipFilesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZipFiles\ImportZipFilesHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZip\ImportZipPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZipHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ImportZip\ImportZipHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ReplaceAsset\ReplaceAssetPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ReplaceAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Upload\ReplaceAsset\ReplaceAssetHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetUploadService; use Symfony\Component\HttpFoundation\JsonResponse; diff --git a/src/Controller/Admin/Asset/AssetVersionController.php b/src/Controller/Admin/Asset/AssetVersionController.php index f463b1ff..20bac3be 100644 --- a/src/Controller/Admin/Asset/AssetVersionController.php +++ b/src/Controller/Admin/Asset/AssetVersionController.php @@ -21,15 +21,14 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\PublishVersion\PublishVersionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\PublishVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\PublishVersion\PublishVersionHandler; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\ShowVersion\ShowVersionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\ShowVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\ShowVersion\ShowVersionHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Tool; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; use Twig\Environment; @@ -61,7 +60,6 @@ public function showVersionAction( Environment $twig, ShowVersionPayload $payload, ShowVersionHandler $showVersion, - #[MapQueryParameter] ?string $userTimezone = null, ): Response { $result = $showVersion($payload); @@ -75,7 +73,7 @@ public function showVersionAction( ); } - Tool\UserTimezone::setUserTimezone($userTimezone); + Tool\UserTimezone::setUserTimezone($payload->userTimezone); if ($timezone = Tool\UserTimezone::getUserTimezone()) { $twig->getExtension(CoreExtension::class)->setTimezone($timezone); } diff --git a/src/Controller/Admin/DataObject/ClassController.php b/src/Controller/Admin/DataObject/ClassController.php index 30d4c8b4..ab7ec09a 100644 --- a/src/Controller/Admin/DataObject/ClassController.php +++ b/src/Controller/Admin/DataObject/ClassController.php @@ -18,52 +18,54 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClassHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClassPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommitHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommitPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkExportPrepareHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkExportPreparePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImportHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImportPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClassHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClassPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptionsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DoBulkExportHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClassHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClassPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetAssetTypesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassBulkExportListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfigHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfigPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIconsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIconsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTreeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTreePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetDocumentTypesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTreeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTreePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsagesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsagesPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreviewHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreviewPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetVideoAllowedTypesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClassHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClassPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinitionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinitionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptionsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SuggestClassIdentifierHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClass\AddClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClass\AddClassPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommit\BulkCommitHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommit\BulkCommitPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkExportPrepare\BulkExportPreparePayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImport\BulkImportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImport\BulkImportPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClass\DeleteClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClass\DeleteClassPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptions\DeleteSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptions\DeleteSelectOptionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DoBulkExport\DoBulkExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClass\ExportClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClass\ExportClassPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetAssetTypes\GetAssetTypesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassBulkExportList\GetClassBulkExportListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfig\GetClassDefinitionForColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfig\GetClassDefinitionForColumnConfigPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClass\GetClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIcons\GetClassIconsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIcons\GetClassIconsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClass\GetClassPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTree\GetClassTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTree\GetClassTreePayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetDocumentTypes\GetDocumentTypesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptions\GetSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptions\GetSelectOptionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTree\GetSelectOptionsTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTree\GetSelectOptionsTreePayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsages\GetSelectOptionsUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsages\GetSelectOptionsUsagesPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreview\GetTextLayoutPreviewHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreview\GetTextLayoutPreviewPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetVideoAllowedTypes\GetVideoAllowedTypesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClass\ImportClassHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClass\ImportClassPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinition\SaveClassDefinitionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinition\SaveClassDefinitionPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptions\SaveSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptions\SaveSelectOptionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SuggestClassIdentifier\SuggestClassIdentifierHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Logger; +use OpenDxp\Tool\Session; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -173,9 +175,15 @@ public function getClassDefinitionForColumnConfigAction( */ #[IsGranted(CorePermission::Classes->value)] #[Route('/bulk-import', name: 'bulkimport', methods: ['POST'])] - public function bulkImportAction(BulkImportPayload $payload, BulkImportHandler $handler): JsonResponse + public function bulkImportAction(BulkImportPayload $payload, BulkImportHandler $handler, Request $request): JsonResponse { - $response = $this->adminJson(ApiResponse::ok(['data' => $handler($payload)->items])); + $result = $handler($payload); + + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($result): void { + $session->set('class_bulk_import_file', $result->tmpFile); + }, 'opendxp_objects'); + + $response = $this->adminJson(ApiResponse::ok(['data' => $result->items])); $response->headers->set('Content-Type', 'text/html'); return $response; @@ -197,9 +205,11 @@ public function bulkCommitAction(BulkCommitPayload $payload, BulkCommitHandler $ */ #[IsGranted(CorePermission::Classes->value)] #[Route('/bulk-export-prepare', name: 'bulkexportprepare', methods: ['POST'])] - public function bulkExportPrepareAction(BulkExportPreparePayload $payload, BulkExportPrepareHandler $handler): Response + public function bulkExportPrepareAction(BulkExportPreparePayload $payload, Request $request): Response { - $handler($payload); + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($payload): void { + $session->set('class_bulk_export_settings', $payload->data); + }, 'opendxp_objects'); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/DataObject/ClassificationstoreController.php b/src/Controller/Admin/DataObject/ClassificationstoreController.php index 95ce1a77..5812383f 100644 --- a/src/Controller/Admin/DataObject/ClassificationstoreController.php +++ b/src/Controller/Admin/DataObject/ClassificationstoreController.php @@ -18,56 +18,56 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollectionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollectionsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroupsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroupsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddPropertyHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddPropertyPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollectionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollectionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroupHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroupPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStoreHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStorePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelationPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroupHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroupPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeletePropertyHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeletePropertyPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelationPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStoreHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStorePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelationsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroupsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroupsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPageHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPagePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPropertiesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPropertiesPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelationsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetStoreTreeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\ListStoresHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelationsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelationPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelationsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollectionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollectionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroupHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroupPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdatePropertyHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdatePropertyPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollections\AddCollectionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollections\AddCollectionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroups\AddGroupsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroups\AddGroupsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddProperty\AddPropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddProperty\AddPropertyPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollection\CreateCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollection\CreateCollectionPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroup\CreateGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroup\CreateGroupPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStore\CreateStoreHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStore\CreateStorePayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollection\DeleteCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollection\DeleteCollectionPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelation\DeleteCollectionRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelation\DeleteCollectionRelationPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroup\DeleteGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroup\DeleteGroupPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteProperty\DeletePropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteProperty\DeletePropertyPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelation\DeleteRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelation\DeleteRelationPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStore\EditStoreHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStore\EditStorePayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelations\GetCollectionRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelations\GetCollectionRelationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollections\GetCollectionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollections\GetCollectionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroups\GetGroupsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroups\GetGroupsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPage\GetPageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPage\GetPagePayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetProperties\GetPropertiesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetProperties\GetPropertiesPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelations\GetRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelations\GetRelationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetStoreTree\GetStoreTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\ListStores\ListStoresHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelations\SaveCollectionRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelations\SaveCollectionRelationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelation\SaveRelationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelation\SaveRelationPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelations\SearchRelationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelations\SearchRelationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollection\UpdateCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollection\UpdateCollectionPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroup\UpdateGroupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroup\UpdateGroupPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateProperty\UpdatePropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateProperty\UpdatePropertyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; diff --git a/src/Controller/Admin/DataObject/CustomLayoutController.php b/src/Controller/Admin/DataObject/CustomLayoutController.php index e8a6de91..389a1f1b 100644 --- a/src/Controller/Admin/DataObject/CustomLayoutController.php +++ b/src/Controller/Admin/DataObject/CustomLayoutController.php @@ -19,21 +19,21 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\AddCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\AddCustomLayout\AddCustomLayoutHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\AddCustomLayout\AddCustomLayoutPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\DeleteCustomLayoutHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\DeleteCustomLayout\DeleteCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayout\ExportCustomLayoutHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayout\ExportCustomLayoutPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetAllLayoutsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetAllLayouts\GetAllLayoutsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitions\GetCustomLayoutDefinitionsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitions\GetCustomLayoutDefinitionsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayout\GetCustomLayoutHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayout\GetCustomLayoutPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ImportCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ImportCustomLayout\ImportCustomLayoutHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ImportCustomLayout\ImportCustomLayoutPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SaveCustomLayoutHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SaveCustomLayout\SaveCustomLayoutHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SaveCustomLayout\SaveCustomLayoutPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifierHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier\SuggestCustomLayoutIdentifierHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier\SuggestCustomLayoutIdentifierPayload; use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; diff --git a/src/Controller/Admin/DataObject/DataObjectController.php b/src/Controller/Admin/DataObject/DataObjectController.php index b555f3dd..f05eb46f 100644 --- a/src/Controller/Admin/DataObject/DataObjectController.php +++ b/src/Controller/Admin/DataObject/DataObjectController.php @@ -18,53 +18,49 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; use OpenDxp\Bundle\AdminBundle\Controller\Admin\ElementControllerBase; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObjectFolderHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObjectHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ChangeChildrenSortByHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\DeleteDataObjectHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\DataObjectGridProxyHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenByIdHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectFolderHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObjectFolder\AddObjectFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObjectFolder\AddObjectFolderPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObject\AddObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObject\AddObjectPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ChangeChildrenSortBy\ChangeChildrenSortByHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ChangeChildrenSortBy\ChangeChildrenSortByPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\DataObjectGridProxy\DataObjectGridProxyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\DataObjectGridProxy\DataObjectGridProxyPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\DeleteDataObject\DeleteDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\DeleteDataObject\DeleteDataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObject\GetDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObject\GetDataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetSelectOptions\GetSelectOptionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetSelectOptions\GetSelectOptionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectFolder\SaveDataObjectFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectFolder\SaveDataObjectFolderPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenById\TreeGetChildrenByIdHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenById\TreeGetChildrenByIdPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\UpdateDataObject\UpdateDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\UpdateDataObject\UpdateDataObjectPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectFolder\GetDataObjectFolderHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfo\GetIdPathPagingInfoHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfo\GetIdPathPagingInfoPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetSelectOptionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrlHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrl\GetDataObjectPreviewUrlHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrl\GetDataObjectPreviewUrlPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectFolderHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject\SaveDataObjectHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\UpdateDataObjectHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObject\SaveDataObjectPayload; use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\AddObjectFolderPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\AddObjectPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\ChangeChildrenSortByPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DataObjectGridProxyPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DeleteDataObjectPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\GetDataObjectPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\GetSelectOptionsPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\SaveDataObjectFolderPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\TreeGetChildrenByIdPayload; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\UpdateDataObjectPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; -use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\Security\Http\Attribute\IsGranted; use OpenDxp\Controller\Traits\ElementEditLockHelperTrait; -use OpenDxp\Model\DataObject; use OpenDxp\Model\Element\ElementInterface; use Override; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal @@ -95,7 +91,6 @@ public function __construct( public function treeGetChildrenByIdAction( TreeGetChildrenByIdPayload $payload, TreeGetChildrenByIdHandler $handler, - #[MapQueryParameter] int $inSearch = 0, ): JsonResponse { $result = $handler($payload); @@ -108,7 +103,7 @@ public function treeGetChildrenByIdAction( 'nodes' => $result->objects, 'fromPaging' => $result->fromPaging, 'filter' => $result->filter ?: '', - 'inSearch' => $inSearch, + 'inSearch' => $payload->inSearch, ]); } @@ -295,19 +290,9 @@ public function gridProxyAction( #[Route('/preview', name: 'preview', methods: ['GET'])] public function previewAction( - Request $request, + GetDataObjectPreviewUrlPayload $payload, GetDataObjectPreviewUrlHandler $handler, - #[MapQueryParameter] int $id = 0, - ): RedirectResponse|Response { - $object = $this->sessionService->getObject('object', $id); - - if ($object instanceof DataObject\Concrete) { - $payload = new GetDataObjectPreviewUrlPayload($object, ['context' => $this, ...$request->query->all()]); - $redirectUrl = $handler($payload); - - return $this->redirect($redirectUrl); - } - - throw new NotFoundHttpException(sprintf('Expected an object of type "%s", got "%s"', DataObject\Concrete::class, get_debug_type($object))); + ): RedirectResponse { + return $this->redirect($handler($payload)); } } diff --git a/src/Controller/Admin/DataObject/DataObjectCopyController.php b/src/Controller/Admin/DataObject/DataObjectCopyController.php index c8afcb34..3a75fcb5 100644 --- a/src/Controller/Admin/DataObject/DataObjectCopyController.php +++ b/src/Controller/Admin/DataObject/DataObjectCopyController.php @@ -21,8 +21,8 @@ use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyDataObject\CopyDataObjectHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyDataObject\CopyDataObjectPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIds\GetDataObjectChildIdsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIds\GetDataObjectChildIdsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyInfo\CopyInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\CopyInfo\CopyInfoPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\RewriteDataObjectIds\RewriteDataObjectIdsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\RewriteDataObjectIds\RewriteDataObjectIdsPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; @@ -30,7 +30,6 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -43,76 +42,17 @@ class DataObjectCopyController extends AdminAbstractController { #[Route('/copy-info', name: 'copyinfo', methods: ['GET'])] public function copyInfoAction( - GetDataObjectChildIdsPayload $getChildIdsPayload, - GetDataObjectChildIdsHandler $getChildIds, + CopyInfoPayload $payload, + CopyInfoHandler $handler, Request $request, - #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $targetId = null, ): JsonResponse { - $transactionId = time(); - $pasteJobs = []; + $result = $handler($payload); - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { - $session->set((string) $transactionId, ['idMapping' => []]); + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($result): void { + $session->set((string) $result->transactionId, ['idMapping' => []]); }, 'opendxp_copy'); - $sourceId = $getChildIdsPayload->sourceId; - - if ($type === 'recursive' || $type === 'recursive-update-references') { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $sourceId, - 'targetId' => $targetId, - 'type' => 'child', - 'transactionId' => $transactionId, - 'saveParentId' => true, - ], - ]]; - - $childIds = $getChildIds($getChildIdsPayload)->ids; - - foreach ($childIds as $id) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $id, - 'targetParentId' => $targetId, - 'sourceParentId' => $sourceId, - 'type' => 'child', - 'transactionId' => $transactionId, - ], - ]]; - } - - if ($type === 'recursive-update-references' && count($childIds) > 0) { - for ($i = 0; $i < (count($childIds) + 1); $i++) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copyrewriteids'), - 'method' => 'PUT', - 'params' => [ - 'transactionId' => $transactionId, - '_dc' => uniqid('', false), - ], - ]]; - } - } - } elseif ($type === 'child' || $type === 'replace') { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_dataobject_dataobject_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $sourceId, - 'targetId' => $targetId, - 'type' => $type, - 'transactionId' => $transactionId, - ], - ]]; - } - - return $this->adminJson(['pastejobs' => $pasteJobs]); + return $this->adminJson(['pastejobs' => $result->pasteJobs]); } #[Route('/copy-rewrite-ids', name: 'copyrewriteids', methods: ['PUT'])] diff --git a/src/Controller/Admin/DataObject/DataObjectHelperController.php b/src/Controller/Admin/DataObject/DataObjectHelperController.php index e1ebfa4f..6fe56aef 100644 --- a/src/Controller/Admin/DataObject/DataObjectHelperController.php +++ b/src/Controller/Admin/DataObject/DataObjectHelperController.php @@ -17,42 +17,42 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAllHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAllPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteDataObjectGridColumnConfigHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteDataObjectGridColumnConfigPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAll\ApplyGridConfigToAllHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAll\ApplyGridConfigToAllPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteGridColumnConfig\DeleteGridColumnConfigHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteGridColumnConfig\DeleteGridColumnConfigPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExportHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExportPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatchHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatchPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFieldsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFieldsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExport\DoDataObjectExportHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExport\DoDataObjectExportPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatch\ExecuteBatchHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatch\ExecuteBatchPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFields\GetAvailableVisibleFieldsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFields\GetAvailableVisibleFieldsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobs\GetBatchJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobs\GetBatchJobsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigs\GetExportConfigsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigs\GetExportConfigsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobs\GetExportJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobs\GetExportJobsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetGridColumnConfig\GetGridColumnConfigHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetGridColumnConfig\GetGridColumnConfigPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUploadHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUploadPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectDataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectDataPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavouriteHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavouritePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfigHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfigPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUpload\ImportUploadHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUpload\ImportUploadPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectData\LoadObjectDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectData\LoadObjectDataPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavourite\MarkDataObjectGridConfigFavouriteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavourite\MarkDataObjectGridConfigFavouritePayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigs\PrepareHelperColumnConfigsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigs\PrepareHelperColumnConfigsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfig\SaveDataObjectGridColumnConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfig\SaveDataObjectGridColumnConfigPayload; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Tool\Session; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; @@ -84,10 +84,7 @@ public function getExportConfigsAction( public function gridDeleteColumnConfigAction( DeleteGridColumnConfigPayload $payload, DeleteGridColumnConfigHandler $handler, - DeleteDataObjectGridColumnConfigHandler $deleteGridColumnConfig, ): JsonResponse { - $deleteGridColumnConfig(new DeleteDataObjectGridColumnConfigPayload(gridConfigId: (int) $payload->gridConfigId)); - return $this->adminJson($handler($payload)); } @@ -103,10 +100,14 @@ public function gridGetColumnConfigAction( public function prepareHelperColumnConfigs( PrepareHelperColumnConfigsPayload $payload, PrepareHelperColumnConfigsHandler $prepareHelperColumns, + Request $request, ): JsonResponse { $result = $prepareHelperColumns($payload); - $payload->helperColumnsBag->set('helpercolumns', $result['helperColumns']); + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($result): void { + $existingColumns = $session->get('helpercolumns', []); + $session->set('helpercolumns', [...$result['helperColumns'], ...$existingColumns]); + }, 'opendxp_gridconfig'); return $this->adminJson(ApiResponse::ok(['columns' => $result['newData']])); } diff --git a/src/Controller/Admin/DataObject/DataObjectVersionController.php b/src/Controller/Admin/DataObject/DataObjectVersionController.php index f2e60285..01267faa 100644 --- a/src/Controller/Admin/DataObject/DataObjectVersionController.php +++ b/src/Controller/Admin/DataObject/DataObjectVersionController.php @@ -22,14 +22,13 @@ use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\DiffVersions\DiffVersionsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\DiffVersions\DiffVersionsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PreviewVersion\PreviewVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PreviewVersion\PreviewVersionPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PublishVersion\PublishVersionHandler; use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Tool; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; use Twig\Environment; @@ -57,13 +56,12 @@ public function publishVersionAction(IdBodyPayload $payload, PublishVersionHandl public function previewVersionAction( Environment $twig, PreviewVersionHandler $previewVersion, - IdQueryPayload $payload, - #[MapQueryParameter] ?string $userTimezone = null, + PreviewVersionPayload $payload, ): Response { $result = $previewVersion($payload); - Tool\UserTimezone::setUserTimezone($userTimezone); + Tool\UserTimezone::setUserTimezone($payload->userTimezone); if ($timezone = Tool\UserTimezone::getUserTimezone()) { $twig->getExtension(CoreExtension::class)->setTimezone($timezone); } @@ -80,12 +78,11 @@ public function diffVersionsAction( Environment $twig, DiffVersionsHandler $diffVersions, DiffVersionsPayload $payload, - #[MapQueryParameter] ?string $userTimezone = null, ): Response { $result = $diffVersions($payload); - Tool\UserTimezone::setUserTimezone($userTimezone); + Tool\UserTimezone::setUserTimezone($payload->userTimezone); if ($timezone = Tool\UserTimezone::getUserTimezone()) { $twig->getExtension(CoreExtension::class)->setTimezone($timezone); } diff --git a/src/Controller/Admin/DataObject/FieldCollectionController.php b/src/Controller/Admin/DataObject/FieldCollectionController.php index bde1fd3e..c449b9a4 100644 --- a/src/Controller/Admin/DataObject/FieldCollectionController.php +++ b/src/Controller/Admin/DataObject/FieldCollectionController.php @@ -19,20 +19,20 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\DeleteFieldCollectionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\DeleteFieldCollection\DeleteFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollection\ExportFieldCollectionHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollection\ExportFieldCollectionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollection\GetFieldCollectionHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollection\GetFieldCollectionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionList\GetFieldCollectionListHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionList\GetFieldCollectionListPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTree\GetFieldCollectionTreeHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTree\GetFieldCollectionTreePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionUsages\GetFieldCollectionUsagesHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionUsages\GetFieldCollectionUsagesPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ImportFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ImportFieldCollection\ImportFieldCollectionHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ImportFieldCollection\ImportFieldCollectionPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\UpdateFieldCollectionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\UpdateFieldCollection\UpdateFieldCollectionHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\UpdateFieldCollection\UpdateFieldCollectionPayload; use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; diff --git a/src/Controller/Admin/DataObject/ObjectBrickController.php b/src/Controller/Admin/DataObject/ObjectBrickController.php index 3a569935..cfaa9ce6 100644 --- a/src/Controller/Admin/DataObject/ObjectBrickController.php +++ b/src/Controller/Admin/DataObject/ObjectBrickController.php @@ -19,20 +19,20 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\DeleteObjectBrickHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\DeleteObjectBrick\DeleteObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrick\ExportObjectBrickHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrick\ExportObjectBrickPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsages\GetBrickUsagesHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsages\GetBrickUsagesPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrick\GetObjectBrickHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrick\GetObjectBrickPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickList\GetObjectBrickListHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickList\GetObjectBrickListPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTree\GetObjectBrickTreeHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTree\GetObjectBrickTreePayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ImportObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ImportObjectBrick\ImportObjectBrickHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ImportObjectBrick\ImportObjectBrickPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\UpdateObjectBrickHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\UpdateObjectBrick\UpdateObjectBrickHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\UpdateObjectBrick\UpdateObjectBrickPayload; use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; diff --git a/src/Controller/Admin/DataObject/QuantityValueController.php b/src/Controller/Admin/DataObject/QuantityValueController.php index 0d0b23ee..6183c7f8 100644 --- a/src/Controller/Admin/DataObject/QuantityValueController.php +++ b/src/Controller/Admin/DataObject/QuantityValueController.php @@ -18,18 +18,18 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValuesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValues\ConvertAllQuantityValuesHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValues\ConvertAllQuantityValuesPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValueHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValue\ConvertQuantityValueHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValue\ConvertQuantityValuePayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\CreateQuantityValueUnit\CreateQuantityValueUnitHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\DeleteQuantityValueUnit\DeleteQuantityValueUnitHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ExportQuantityValueUnitsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ExportQuantityValueUnits\ExportQuantityValueUnitsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitList\GetQuantityValueUnitListHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitList\GetQuantityValueUnitListPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnits\GetQuantityValueUnitsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnits\GetQuantityValueUnitsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnitsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnits\ImportQuantityValueUnitsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnits\ImportQuantityValueUnitsPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\QuantityValueUnitPayload; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\UpdateQuantityValueUnit\UpdateQuantityValueUnitHandler; diff --git a/src/Controller/Admin/DataObject/VariantsController.php b/src/Controller/Admin/DataObject/VariantsController.php index 75a93ab3..a4e953c1 100644 --- a/src/Controller/Admin/DataObject/VariantsController.php +++ b/src/Controller/Admin/DataObject/VariantsController.php @@ -18,9 +18,9 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariantsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariants\GetVariantsHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariants\GetVariantsPayload; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKeyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKey\UpdateObjectKeyHandler; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKey\UpdateObjectKeyPayload; use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler; use Symfony\Component\HttpFoundation\JsonResponse; diff --git a/src/Controller/Admin/Document/DocumentController.php b/src/Controller/Admin/Document/DocumentController.php index 5fba39c7..5dc75892 100644 --- a/src/Controller/Admin/Document/DocumentController.php +++ b/src/Controller/Admin/Document/DocumentController.php @@ -60,7 +60,8 @@ use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildren\TreeGetDocumentChildrenPayload; use OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateDocument\UpdateDocumentHandler; use OpenDxp\Bundle\AdminBundle\Handler\Document\UpdateDocument\UpdateDocumentPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfo\GetDeleteInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfo\GetDeleteInfoPayload; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Model\Element\ElementInterface; use Override; @@ -88,7 +89,7 @@ public function __construct(ElementServiceInterface $elementService) #[Route('/tree-get-root', name: 'opendxp_admin_document_document_treegetroot', methods: ['GET'])] public function treeGetRootAction( #[MapQueryParameter] ?string $elementType = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $id = null, + #[MapQueryParameter(flags: FILTER_NULL_ON_FAILURE)] ?int $id = null, ): JsonResponse { return parent::treeGetRootAction($elementType, $id); } @@ -98,11 +99,9 @@ public function treeGetRootAction( #[Route('/delete-info', name: 'opendxp_admin_document_document_deleteinfo', methods: ['GET'])] public function deleteInfoAction( GetDeleteInfoHandler $handler, - Request $request, - #[MapQueryParameter] ?string $id = null, - #[MapQueryParameter] ?string $type = null, + GetDeleteInfoPayload $payload, ): JsonResponse { - return parent::deleteInfoAction($handler, $request, $id, $type); + return parent::deleteInfoAction($handler, $payload); } #[IsGranted(CorePermission::Documents->value)] diff --git a/src/Controller/Admin/Document/DocumentControllerBase.php b/src/Controller/Admin/Document/DocumentControllerBase.php index 41f8d7ef..2b57ddf2 100644 --- a/src/Controller/Admin/Document/DocumentControllerBase.php +++ b/src/Controller/Admin/Document/DocumentControllerBase.php @@ -60,12 +60,13 @@ abstract class DocumentControllerBase extends AdminAbstractController public function __construct( protected ElementServiceInterface $elementService, - ) {} + ) { + } #[Route('/save-to-session', name: 'savetosession', methods: ['POST'])] public function saveToSessionAction( - SaveToSessionPayload $payload, - SaveToSessionHandler $handler, + SaveToSessionPayload $payload, + SaveToSessionHandler $handler, ): JsonResponse { $handler($payload); @@ -74,7 +75,7 @@ public function saveToSessionAction( #[Route('/remove-from-session', name: 'removefromsession', methods: ['DELETE'])] public function removeFromSessionAction( - IdBodyPayload $payload, + IdBodyPayload $payload, RemoveFromSessionHandler $handler, ): JsonResponse { $handler($payload); @@ -106,7 +107,7 @@ public function getTreeNodeConfig(ElementInterface $element): array protected function preSendDataActions(array $data, Model\Document $document): JsonResponse { $event = new GenericEvent($this, [ - 'data' => $data, + 'data' => $data, 'document' => $document, ]); diff --git a/src/Controller/Admin/Document/DocumentCopyController.php b/src/Controller/Admin/Document/DocumentCopyController.php index 40b63be6..f3b6d86f 100644 --- a/src/Controller/Admin/Document/DocumentCopyController.php +++ b/src/Controller/Admin/Document/DocumentCopyController.php @@ -21,8 +21,8 @@ use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyDocument\CopyDocumentHandler; use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyDocument\CopyDocumentPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\GetDocumentChildIds\GetDocumentChildIdsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\GetDocumentChildIds\GetDocumentChildIdsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyInfo\CopyInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\CopyInfo\CopyInfoPayload; use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\RewriteDocumentIds\RewriteDocumentIdsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\RewriteDocumentIds\RewriteDocumentIdsPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; @@ -30,7 +30,6 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -43,87 +42,17 @@ class DocumentCopyController extends AdminAbstractController { #[Route('/copy-info', name: 'opendxp_admin_document_document_copyinfo', methods: ['GET'])] public function copyInfoAction( - GetDocumentChildIdsPayload $getChildIdsPayload, - GetDocumentChildIdsHandler $getChildIds, + CopyInfoPayload $payload, + CopyInfoHandler $handler, Request $request, - #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter] ?string $targetId = null, - #[MapQueryParameter] ?string $language = null, - #[MapQueryParameter] ?string $enableInheritance = null, ): JsonResponse { - $transactionId = time(); - $pasteJobs = []; + $result = $handler($payload); - Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($transactionId): void { - $session->set((string) $transactionId, ['idMapping' => []]); + Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($result): void { + $session->set((string) $result->transactionId, ['idMapping' => []]); }, 'opendxp_copy'); - $sourceId = $getChildIdsPayload->sourceId; - - if ($type === 'recursive' || $type === 'recursive-update-references') { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $sourceId, - 'targetId' => $targetId, - 'type' => 'child', - 'language' => $language, - 'enableInheritance' => $enableInheritance, - 'transactionId' => $transactionId, - 'saveParentId' => true, - 'resetIndex' => true, - ], - ]]; - - $childIds = $getChildIds($getChildIdsPayload)->ids; - - foreach ($childIds as $id) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $id, - 'targetParentId' => $targetId, - 'sourceParentId' => $sourceId, - 'type' => 'child', - 'language' => $language, - 'enableInheritance' => $enableInheritance, - 'transactionId' => $transactionId, - ], - ]]; - } - - if ($type === 'recursive-update-references') { - for ($i = 0; $i < (count($childIds) + 1); $i++) { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_document_document_copyrewriteids'), - 'method' => 'PUT', - 'params' => [ - 'transactionId' => $transactionId, - 'enableInheritance' => $enableInheritance, - '_dc' => uniqid('', false), - ], - ]]; - } - } - } elseif ($type === 'child' || $type === 'replace') { - $pasteJobs[] = [[ - 'url' => $this->generateUrl('opendxp_admin_document_document_copy'), - 'method' => 'POST', - 'params' => [ - 'sourceId' => $sourceId, - 'targetId' => $targetId, - 'type' => $type, - 'language' => $language, - 'enableInheritance' => $enableInheritance, - 'transactionId' => $transactionId, - 'resetIndex' => ($type === 'child'), - ], - ]]; - } - - return $this->adminJson(['pastejobs' => $pasteJobs]); + return $this->adminJson(['pastejobs' => $result->pasteJobs]); } #[Route('/copy-rewrite-ids', name: 'opendxp_admin_document_document_copyrewriteids', methods: ['PUT'])] diff --git a/src/Controller/Admin/ElementController.php b/src/Controller/Admin/ElementController.php index 1e8c337b..fa7ad2e1 100644 --- a/src/Controller/Admin/ElementController.php +++ b/src/Controller/Admin/ElementController.php @@ -17,34 +17,52 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\DependencyInjection\OpenDxpAdminExtension; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Element\AddNoteHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\AnalyzePermissionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteAllVersionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteDraftHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteNoteHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteVersionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\FindUsagesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNicePathHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNoteListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetPredefinedPropertiesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetReplaceAssignmentsBatchJobsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetRequiredByDependenciesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetRequiresDependenciesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetSubtypeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetVersionsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\LockElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\AnalyzePermissions\AnalyzePermissionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\AnalyzePermissions\AnalyzePermissionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteAllVersions\DeleteAllVersionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteAllVersions\DeleteAllVersionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteNote\DeleteNoteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteVersion\DeleteVersionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteDraft\DeleteDraftHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\FindUsages\FindUsagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\FindUsages\FindUsagesPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNicePath\GetNicePathHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNicePath\GetNicePathPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNoteList\GetNoteListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNoteTypes\GetNoteTypesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetNoteTypes\GetNoteTypesPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetPredefinedProperties\GetPredefinedPropertiesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetPredefinedProperties\GetPredefinedPropertiesPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetReplaceAssignmentsBatchJobs\GetReplaceAssignmentsBatchJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetReplaceAssignmentsBatchJobs\GetReplaceAssignmentsBatchJobsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetRequiredByDependencies\GetRequiredByDependenciesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetRequiresDependencies\GetRequiresDependenciesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetSubtype\GetSubtypeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetSubtype\GetSubtypePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetVersions\GetVersionsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetVersions\GetVersionsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\AddNote\AddNoteHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\AddNote\AddNotePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\LockElement\LockElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\LockElement\LockElementPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\ReplaceAssignments\ReplaceAssignmentsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\ReplaceAssignments\ReplaceAssignmentsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\TypePath\TypePathHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\TypePath\TypePathPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElements\UnlockElementsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElements\UnlockElementsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockPropagate\UnlockPropagateHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockPropagate\UnlockPropagatePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElement\UnlockElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElement\UnlockElementPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\VersionUpdate\VersionUpdateHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\VersionUpdate\VersionUpdatePayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDependenciesPayload; use OpenDxp\Bundle\AdminBundle\Handler\Element\NoteListPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Element\ReplaceAssignmentsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\TypePathHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElementHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockElementsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\UnlockPropagateHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Element\VersionUpdateHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -56,70 +74,61 @@ */ class ElementController extends AdminAbstractController { - #[Route('/element/lock-element', name: 'opendxp_admin_element_lockelement', methods: ['PUT'])] - public function lockElementAction(Request $request, LockElementHandler $lockElement): Response - { - $lockElement($request->request->getInt('id'), $request->request->get('type'), $request->getSession()->getId()); + #[Route('/lock-element', name: 'opendxp_admin_element_lockelement', methods: ['PUT'])] + public function lockElementAction( + LockElementHandler $lockElement, + LockElementPayload $payload, + ): Response { + $lockElement($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/unlock-element', name: 'opendxp_admin_element_unlockelement', methods: ['PUT'])] - public function unlockElementAction(Request $request, UnlockElementHandler $unlockElement): Response - { - $unlockElement((int) $request->request->get('id'), $request->request->get('type')); + #[Route('/unlock-element', name: 'opendxp_admin_element_unlockelement', methods: ['PUT'])] + public function unlockElementAction( + UnlockElementHandler $unlockElement, + UnlockElementPayload $payload, + ): Response { + $unlockElement($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/unlock-elements', name: 'opendxp_admin_element_unlockelements', methods: ['POST'])] - public function unlockElementsAction(Request $request, UnlockElementsHandler $unlockElements): Response - { - $body = json_decode($request->getContent(), true) ?? []; - $unlockElements($body['elements'] ?? []); + #[Route('/unlock-elements', name: 'opendxp_admin_element_unlockelements', methods: ['POST'])] + public function unlockElementsAction( + UnlockElementsHandler $unlockElements, + UnlockElementsPayload $payload, + ): Response { + $unlockElements($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/get-subtype', name: 'opendxp_admin_element_getsubtype', methods: ['GET'])] + #[Route('/get-subtype', name: 'opendxp_admin_element_getsubtype', methods: ['GET'])] public function getSubtypeAction( GetSubtypeHandler $getSubtype, - #[MapQueryParameter] string $id = '', - #[MapQueryParameter] ?string $type = null, - ): JsonResponse - { - $result = ($getSubtype)($id, $type); + GetSubtypePayload $payload, + ): JsonResponse { + $result = $getSubtype($payload); - return $this->adminJson(ApiResponse::ok(['subtype' => $result->subtype, 'id' => $result->id, 'type' => $result->type])); + return $this->adminJson(ApiResponse::ok([ + 'subtype' => $result->subtype, + 'id' => $result->id, + 'type' => $result->type, + ])); } - protected function processNoteTypesFromParameters(string $parameterName): JsonResponse - { - $config = $this->getParameter($parameterName); - $result = []; - foreach ($config as $configEntry) { - $result[] = [ - 'name' => $configEntry, - ]; - } - - return $this->adminJson(['noteTypes' => $result]); - } + #[Route('/note-types', name: 'opendxp_admin_element_notetypes', methods: ['GET'])] + public function noteTypesAction( + GetNoteTypesPayload $payload, + GetNoteTypesHandler $getNoteTypes, + ): JsonResponse { + $result = $getNoteTypes($payload); - #[Route('/element/note-types', name: 'opendxp_admin_element_notetypes', methods: ['GET'])] - public function noteTypes( - #[MapQueryParameter] ?string $ctype = null, - ): JsonResponse - { - return match ($ctype) { - 'document' => $this->processNoteTypesFromParameters(OpenDxpAdminExtension::PARAM_DOCUMENTS_NOTES_EVENTS_TYPES), - 'asset' => $this->processNoteTypesFromParameters(OpenDxpAdminExtension::PARAM_ASSETS_NOTES_EVENTS_TYPES), - 'object' => $this->processNoteTypesFromParameters(OpenDxpAdminExtension::PARAM_DATAOBJECTS_NOTES_EVENTS_TYPES), - default => $this->adminJson(['noteTypes' => []]), - }; + return $this->adminJson(ApiResponse::ok(['noteTypes' => $result->noteTypes])); } - #[Route('/element/note-list', name: 'opendxp_admin_element_notelist', methods: ['POST'])] + #[Route('/note-list', name: 'opendxp_admin_element_notelist', methods: ['POST'])] #[IsGranted(CorePermission::NotesEvents->value)] public function noteListAction( NoteListPayload $payload, @@ -129,106 +138,87 @@ public function noteListAction( ): JsonResponse { if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->destroyNote($deleteNote, $payload), - default => throw new BadRequestHttpException(), + 'destroy' => $this->handleDeleteNote($deleteNote, $payload), + default => throw new BadRequestHttpException(), }; } $result = $getNoteList($payload); - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + return $this->adminJson(ApiResponse::ok([ + 'data' => $result->data, + 'total' => $result->total, + ])); } - private function destroyNote(DeleteNoteHandler $handler, NoteListPayload $payload): JsonResponse + private function handleDeleteNote(DeleteNoteHandler $handler, NoteListPayload $payload): JsonResponse { $handler($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); } - #[Route('/element/note-add', name: 'opendxp_admin_element_noteadd', methods: ['POST'])] + #[Route('/note-add', name: 'opendxp_admin_element_noteadd', methods: ['POST'])] #[IsGranted(CorePermission::NotesEvents->value)] - public function noteAddAction(Request $request, AddNoteHandler $addNote): JsonResponse - { - - ($addNote)( - cid: (int) $request->request->get('cid'), - ctype: $request->request->get('ctype'), - title: $request->request->get('title'), - description: $request->request->get('description'), - type: $request->request->get('type'), - ); + public function noteAddAction( + AddNoteHandler $addNote, + AddNotePayload $payload, + ): JsonResponse { + $addNote($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/find-usages', name: 'opendxp_admin_element_findusages', methods: ['GET'])] + #[Route('/find-usages', name: 'opendxp_admin_element_findusages', methods: ['GET'])] public function findUsagesAction( FindUsagesHandler $findUsages, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $id = null, - #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter] ?string $path = null, - #[MapQueryParameter] int $limit = 50, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] ?string $sort = null, - ): JsonResponse - { - $result = ($findUsages)( - id: $id, - type: $type, - path: $path, - limit: $limit, - offset: $start, - sort: $sort, - ); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total, 'hasHidden' => $result->hasHidden])); + FindUsagesPayload $payload, + ): JsonResponse { + $result = $findUsages($payload); + + return $this->adminJson(ApiResponse::ok([ + 'data' => $result->data, + 'total' => $result->total, + 'hasHidden' => $result->hasHidden, + ])); } - #[Route('/element/get-replace-assignments-batch-jobs', name: 'opendxp_admin_element_getreplaceassignmentsbatchjobs', methods: ['GET'])] + #[Route('/get-replace-assignments-batch-jobs', name: 'opendxp_admin_element_getreplaceassignmentsbatchjobs', methods: ['GET'])] public function getReplaceAssignmentsBatchJobsAction( GetReplaceAssignmentsBatchJobsHandler $getReplaceAssignmentsBatchJobs, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $id = null, - #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter] ?string $path = null, - ): JsonResponse - { - $jobs = $getReplaceAssignmentsBatchJobs($id, $type, $path); + GetReplaceAssignmentsBatchJobsPayload $payload, + ): JsonResponse { + $jobs = $getReplaceAssignmentsBatchJobs($payload); - return $this->adminJson(ApiResponse::ok(['jobs' => $jobs])); + return $this->adminJson(ApiResponse::ok(['jobs' => $jobs->jobs])); } - #[Route('/element/replace-assignments', name: 'opendxp_admin_element_replaceassignments', methods: ['POST'])] - public function replaceAssignmentsAction(Request $request, ReplaceAssignmentsHandler $replaceAssignments): JsonResponse - { - ($replaceAssignments)( - type: $request->request->get('type'), - id: $request->request->getInt('id'), - sourceType: $request->request->get('sourceType'), - sourceId: $request->request->getInt('sourceId'), - targetType: $request->request->get('targetType'), - targetId: $request->request->getInt('targetId'), - ); + #[Route('/replace-assignments', name: 'opendxp_admin_element_replaceassignments', methods: ['POST'])] + public function replaceAssignmentsAction( + ReplaceAssignmentsHandler $replaceAssignments, + ReplaceAssignmentsPayload $payload, + ): JsonResponse { + $replaceAssignments($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/unlock-propagate', name: 'opendxp_admin_element_unlockpropagate', methods: ['PUT'])] - public function unlockPropagateAction(Request $request, UnlockPropagateHandler $unlockPropagate): JsonResponse - { - $success = $unlockPropagate($request->request->get('type'), $request->request->getInt('id')); + #[Route('/unlock-propagate', name: 'opendxp_admin_element_unlockpropagate', methods: ['PUT'])] + public function unlockPropagateAction( + UnlockPropagateHandler $unlockPropagate, + UnlockPropagatePayload $payload, + ): JsonResponse { + $result = $unlockPropagate($payload); - return $this->adminJson(ApiResponse::fromBool($success)); + return $this->adminJson(ApiResponse::fromBool($result->success)); } - #[Route('/element/type-path', name: 'opendxp_admin_element_typepath', methods: ['GET'])] + #[Route('/type-path', name: 'opendxp_admin_element_typepath', methods: ['GET'])] public function typePathAction( TypePathHandler $typePath, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $type = null, - ): JsonResponse - { - $result = ($typePath)($id, $type); + TypePathPayload $payload, + ): JsonResponse { + $result = $typePath($payload); $data = [ 'index' => $result->index, @@ -244,135 +234,102 @@ public function typePathAction( return $this->adminJson(ApiResponse::ok($data)); } - #[Route('/element/version-update', name: 'opendxp_admin_element_versionupdate', methods: ['PUT'])] - public function versionUpdateAction(Request $request, VersionUpdateHandler $versionUpdate): JsonResponse - { - $data = $this->decodeJson($request->request->get('data')); - ($versionUpdate)($data); + #[Route('/version-update', name: 'opendxp_admin_element_versionupdate', methods: ['PUT'])] + public function versionUpdateAction( + VersionUpdateHandler $versionUpdate, + VersionUpdatePayload $payload, + ): JsonResponse { + $versionUpdate($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/get-nice-path', name: 'opendxp_admin_element_getnicepath', methods: ['POST'])] - public function getNicePathAction(Request $request, GetNicePathHandler $getNicePath): JsonResponse - { - $source = $this->decodeJson($request->request->get('source')); - $context = $request->request->has('context') ? $this->decodeJson($request->request->get('context')) : []; - $targets = $this->decodeJson($request->request->get('targets')); - - $result = ($getNicePath)( - source: $source, - context: $context, - targets: $targets, - loadEditModeData: $request->request->getBoolean('loadEditModeData'), - idProperty: $request->request->get('idProperty', 'id'), - ); + #[Route('/get-nice-path', name: 'opendxp_admin_element_getnicepath', methods: ['POST'])] + public function getNicePathAction( + GetNicePathHandler $getNicePath, + GetNicePathPayload $payload, + ): JsonResponse { + $result = $getNicePath($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } - #[Route('/element/get-versions', name: 'opendxp_admin_element_getversions', methods: ['GET'])] + #[Route('/get-versions', name: 'opendxp_admin_element_getversions', methods: ['GET'])] public function getVersionsAction( GetVersionsHandler $getVersions, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $elementType = null, - ): JsonResponse - { - $result = ($getVersions)($id, $elementType); + GetVersionsPayload $payload, + ): JsonResponse { + $result = $getVersions($payload); return $this->adminJson(['versions' => $result->versions]); } - #[Route('/element/delete-draft', name: 'opendxp_admin_element_deletedraft', methods: ['DELETE'])] - public function deleteDraftAction(Request $request, DeleteDraftHandler $deleteDraft): JsonResponse - { - $deleteDraft((int) $request->request->get('id')); + #[Route('/delete-draft', name: 'opendxp_admin_element_deletedraft', methods: ['DELETE'])] + public function deleteDraftAction( + DeleteDraftHandler $deleteDraft, + IdBodyPayload $payload, + ): JsonResponse { + $deleteDraft($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/delete-version', name: 'opendxp_admin_element_deleteversion', methods: ['DELETE'])] - public function deleteVersionAction(Request $request, DeleteVersionHandler $deleteVersion): JsonResponse - { - $deleteVersion((int) $request->request->get('id')); + #[Route('/delete-version', name: 'opendxp_admin_element_deleteversion', methods: ['DELETE'])] + public function deleteVersionAction( + DeleteVersionHandler $deleteVersion, + IdBodyPayload $payload, + ): JsonResponse { + $deleteVersion($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/delete-all-versions', name: 'opendxp_admin_element_deleteallversion', methods: ['DELETE'])] - public function deleteAllVersionAction(Request $request, DeleteAllVersionsHandler $deleteAllVersions): JsonResponse - { - ($deleteAllVersions)( - elementId: $request->request->getInt('id'), - elementModificationdate: $request->request->get('date'), - elementType: $request->request->get('type'), - ); + #[Route('/delete-all-versions', name: 'opendxp_admin_element_deleteallversion', methods: ['DELETE'])] + public function deleteAllVersionAction( + DeleteAllVersionsHandler $deleteAllVersions, + DeleteAllVersionsPayload $payload, + ): JsonResponse { + $deleteAllVersions($payload); return $this->adminJson(ApiResponse::ok()); } - #[Route('/element/get-requires-dependencies', name: 'opendxp_admin_element_getrequiresdependencies', methods: ['GET'])] + #[Route('/get-requires-dependencies', name: 'opendxp_admin_element_getrequiresdependencies', methods: ['GET'])] public function getRequiresDependenciesAction( GetRequiresDependenciesHandler $getRequiresDependencies, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $elementType = null, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] int $limit = 25, - #[MapQueryParameter] ?string $filter = null, - ): JsonResponse - { - $result = ($getRequiresDependencies)( - id: $id, - type: $elementType, - offset: $start, - limit: $limit, - filterJson: $filter, - ); + GetDependenciesPayload $payload, + ): JsonResponse { + $result = $getRequiresDependencies($payload); return $this->adminJson($result->data); } - #[Route('/element/get-required-by-dependencies', name: 'opendxp_admin_element_getrequiredbydependencies', methods: ['GET'])] + #[Route('/get-required-by-dependencies', name: 'opendxp_admin_element_getrequiredbydependencies', methods: ['GET'])] public function getRequiredByDependenciesAction( GetRequiredByDependenciesHandler $getRequiredByDependencies, - #[MapQueryParameter] int $id = 0, - #[MapQueryParameter] ?string $elementType = null, - #[MapQueryParameter] int $start = 0, - #[MapQueryParameter] int $limit = 25, - #[MapQueryParameter] ?string $filter = null, - ): JsonResponse - { - $result = ($getRequiredByDependencies)( - id: $id, - type: $elementType, - offset: $start, - limit: $limit, - filterJson: $filter, - ); + GetDependenciesPayload $payload, + ): JsonResponse { + $result = $getRequiredByDependencies($payload); return $this->adminJson($result->data); } - #[Route('/element/get-predefined-properties', name: 'opendxp_admin_element_getpredefinedproperties', methods: ['GET'])] + #[Route('/get-predefined-properties', name: 'opendxp_admin_element_getpredefinedproperties', methods: ['GET'])] public function getPredefinedPropertiesAction( GetPredefinedPropertiesHandler $getPredefinedProperties, - #[MapQueryParameter] ?string $elementType = null, - #[MapQueryParameter] ?string $query = null, - ): JsonResponse - { - $result = ($getPredefinedProperties)($elementType, $query); + GetPredefinedPropertiesPayload $payload, + ): JsonResponse { + $result = $getPredefinedProperties($payload); return $this->adminJson(['properties' => $result->properties]); } - #[Route('/element/analyze-permissions', name: 'opendxp_admin_element_analyzepermissions', methods: ['POST'])] - public function analyzePermissionsAction(Request $request, AnalyzePermissionsHandler $analyzePermissions): Response - { - $result = ($analyzePermissions)( - userId: $request->request->getInt('userId') ?: null, - elementType: $request->request->get('elementType'), - elementId: $request->request->getInt('elementId'), - ); + #[Route('/analyze-permissions', name: 'opendxp_admin_element_analyzepermissions', methods: ['POST'])] + public function analyzePermissionsAction( + AnalyzePermissionsHandler $analyzePermissions, + AnalyzePermissionsPayload $payload, + ): Response { + $result = $analyzePermissions($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } diff --git a/src/Controller/Admin/ElementControllerBase.php b/src/Controller/Admin/ElementControllerBase.php index de46e8b1..a36106b9 100644 --- a/src/Controller/Admin/ElementControllerBase.php +++ b/src/Controller/Admin/ElementControllerBase.php @@ -17,13 +17,13 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfo\GetDeleteInfoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfo\GetDeleteInfoPayload; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Model\Element\ElementInterface; use OpenDxp\Model\Element\Service; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; @@ -47,7 +47,7 @@ protected function getTreeNodeConfig(ElementInterface $element): array #[Route('/tree-get-root', name: 'treegetroot', methods: ['GET'])] public function treeGetRootAction( #[MapQueryParameter] ?string $elementType = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $id = null, + #[MapQueryParameter(flags: FILTER_NULL_ON_FAILURE)] ?int $id = null, ): JsonResponse { $type = $elementType; @@ -70,11 +70,9 @@ public function treeGetRootAction( #[Route('/delete-info', name: 'deleteinfo', methods: ['GET'])] public function deleteInfoAction( GetDeleteInfoHandler $handler, - Request $request, - #[MapQueryParameter] ?string $id = null, - #[MapQueryParameter] ?string $type = null, + GetDeleteInfoPayload $payload, ): JsonResponse { - return $this->adminJson($handler($id, $type, $request->getBaseUrl())); + return $this->adminJson($handler($payload)); } } diff --git a/src/Controller/Admin/EmailController.php b/src/Controller/Admin/EmailController.php index 5dcc864a..98f11c83 100644 --- a/src/Controller/Admin/EmailController.php +++ b/src/Controller/Admin/EmailController.php @@ -1,4 +1,5 @@ request->has('documentId') ? (int)$request->request->get('documentId') : null, - limit: (int)$request->request->get('limit', 50), - offset: (int)$request->request->get('start', 0), - filter: $request->request->has('filter') ? $request->request->get('filter') : null, - ); - - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + public function emailLogsAction( + GetEmailLogsHandler $getEmailLogs, + GetEmailLogsPayload $payload, + ): JsonResponse { + $result = $getEmailLogs($payload); + + return $this->adminJson(ApiResponse::ok([ + 'data' => $result->data, + 'total' => $result->total, + ])); } #[IsGranted(CorePermission::Emails->value)] #[Route('/show-email-log', name: 'opendxp_admin_email_showemaillog', methods: ['GET'])] public function showEmailLogAction( - GetEmailLogHandler $getEmailLog, + ShowEmailLogHandler $showEmailLog, GetEmailLogParamsHandler $getEmailLogParams, + ShowEmailLogPayload $payload, ?Profiler $profiler, - #[MapQueryParameter] ?string $type = null, - #[MapQueryParameter] int $id = 0, ): JsonResponse|Response { if ($profiler) { $profiler->disable(); } - if ($type === 'params') { - return $this->adminJson($getEmailLogParams(id: $id)); - } - - $result = $getEmailLog($id); - - if ($type === 'text') { - return $this->render('@OpenDxpAdmin/admin/email/text.html.twig', ['log' => $result->textLog]); - } - - if ($type === 'html') { - return new Response($result->htmlLog, 200, [ + return match ($payload->type) { + 'params' => $this->adminJson($getEmailLogParams($payload->id)), + 'text' => $this->render('@OpenDxpAdmin/admin/email/text.html.twig', ['log' => $showEmailLog($payload->id)->textLog]), + 'html' => new Response($showEmailLog($payload->id)->htmlLog, 200, [ 'Content-Security-Policy' => "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data:", - ]); - } - - if ($type === 'details') { - return $this->adminJson($result->objectVars); - } - - return new Response('No Type specified'); + ]), + 'details' => $this->adminJson($showEmailLog($payload->id)->objectVars), + default => new Response('No Type specified'), + }; } #[IsGranted(CorePermission::Emails->value)] #[Route('/delete-email-log', name: 'opendxp_admin_email_deleteemaillog', methods: ['DELETE'])] - public function deleteEmailLogAction(Request $request, DeleteEmailLogHandler $deleteEmailLog): JsonResponse - { - $deleteEmailLog(id: (int)$request->request->get('id')); + public function deleteEmailLogAction( + DeleteEmailLogHandler $deleteEmailLog, + IdBodyPayload $payload, + ): JsonResponse { + $deleteEmailLog($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Emails->value)] #[Route('/resend-email', name: 'opendxp_admin_email_resendemail', methods: ['POST'])] - public function resendEmailAction(Request $request, ResendEmailHandler $resendEmail): JsonResponse - { - $resendEmail( - id: (int)$request->request->get('id'), - fieldOverrides: [ - 'from' => $request->request->get('from') ?: null, - 'to' => $request->request->get('to') ?: null, - 'cc' => $request->request->get('cc') ?: null, - 'bcc' => $request->request->get('bcc') ?: null, - 'replyto' => $request->request->get('replyto') ?: null, - ], - ); + public function resendEmailAction( + ResendEmailHandler $resendEmail, + ResendEmailPayload $payload, + ): JsonResponse { + $resendEmail($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Emails->value)] #[Route('/send-test-email', name: 'opendxp_admin_email_sendtestemail', methods: ['POST'])] - public function sendTestEmailAction(Request $request, SendTestEmailHandler $sendTestEmail): JsonResponse - { + public function sendTestEmailAction( + Request $request, + SendTestEmailHandler $sendTestEmail, + SendTestEmailPayload $payload, + ): JsonResponse { // Simulate a frontend request to prefix assets $request->attributes->set(RequestHelper::ATTRIBUTE_FRONTEND_REQUEST, true); - $mailParamsArray = null; - if ($request->request->has('mailParamaters')) { - $mailParamsArray = json_decode($request->request->get('mailParamaters'), true) ?: null; - } - - $sendTestEmail( - emailType: (string)$request->request->get('emailType'), - content: $request->request->get('content'), - documentPath: $request->request->get('documentPath'), - mailParameters: $mailParamsArray, - from: $request->request->get('from'), - to: (string)$request->request->get('to'), - subject: (string)$request->request->get('subject'), - ); + $sendTestEmail($payload); return $this->adminJson(ApiResponse::ok()); } @@ -171,7 +147,10 @@ public function blocklistAction( $result = $getBlocklist($payload); - return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); + return $this->adminJson(ApiResponse::ok([ + 'data' => $result->data, + 'total' => $result->total, + ])); } private function destroyBlocklistEntry(DeleteBlocklistEntryHandler $handler, BlocklistPayload $payload): JsonResponse diff --git a/src/Controller/Admin/IndexController.php b/src/Controller/Admin/IndexController.php index e247d9a8..c0fc048c 100644 --- a/src/Controller/Admin/IndexController.php +++ b/src/Controller/Admin/IndexController.php @@ -40,8 +40,8 @@ class IndexController extends AdminAbstractController implements KernelResponseE #[Route('/', name: 'opendxp_admin_index', methods: ['GET'])] public function indexAction( Request $request, - SettingsHandler $settingsHandler, SettingsPayload $payload, + SettingsHandler $settingsHandler, TranslatorInterface $translator, ): Response { $user = $this->getAdminUser(); @@ -63,8 +63,8 @@ public function indexAction( #[Route('/index/statistics', name: 'opendxp_admin_index_statistics', methods: ['GET'])] public function statisticsAction( Request $request, - StatisticsHandler $statisticsHandler, EmptyPayload $payload, + StatisticsHandler $statisticsHandler, ): JsonResponse { if (!$request->isXmlHttpRequest()) { diff --git a/src/Controller/Admin/InstallController.php b/src/Controller/Admin/InstallController.php index 82cc06ec..45d36045 100644 --- a/src/Controller/Admin/InstallController.php +++ b/src/Controller/Admin/InstallController.php @@ -16,14 +16,12 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; -use Doctrine\DBAL\Connection; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Tool\Requirements; -use Symfony\Component\HttpFoundation\Request; +use OpenDxp\Bundle\AdminBundle\Handler\Install\CheckSystem\CheckSystemHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Install\CheckSystem\CheckSystemPayload; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Profiler\Profiler; use Symfony\Component\Routing\Attribute\Route; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal @@ -33,19 +31,16 @@ class InstallController extends AdminAbstractController { #[Route('/check', name: 'opendxp_admin_install_check', methods: ['GET', 'POST'])] public function checkAction( - Request $request, - Connection $db, + CheckSystemPayload $payload, + CheckSystemHandler $handler, ?Profiler $profiler, - #[MapQueryParameter] bool $headless = false, - ): Response - { + ): Response { if ($profiler) { $profiler->disable(); } - $viewParams = Requirements::checkAll($db); - $viewParams['headless'] = $headless || $request->request->getBoolean('headless'); + $result = $handler($payload); - return $this->render('@OpenDxpAdmin/admin/install/check.html.twig', $viewParams); + return $this->render('@OpenDxpAdmin/admin/install/check.html.twig', $result->viewParams); } } diff --git a/src/Controller/Admin/LoginController.php b/src/Controller/Admin/LoginController.php index 95de0b59..415a4dd7 100644 --- a/src/Controller/Admin/LoginController.php +++ b/src/Controller/Admin/LoginController.php @@ -17,21 +17,22 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; -use OpenDxp; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Event\Login\LoginRedirectEvent; -use OpenDxp\Bundle\AdminBundle\Handler\Login\GenerateTwoFactorSetupHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Login\LostPasswordHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Login\SaveTwoFactorSetupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Login\Deeplink\DeeplinkHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Login\Deeplink\DeeplinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Login\GenerateTwoFactorSetup\GenerateTwoFactorSetupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Login\GenerateTwoFactorSetup\GenerateTwoFactorSetupPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Login\LostPassword\LostPasswordHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Login\LostPassword\LostPasswordPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Login\SaveTwoFactorSetup\SaveTwoFactorSetupHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Login\SaveTwoFactorSetup\SaveTwoFactorSetupPayload; use OpenDxp\Bundle\AdminBundle\Factory\LoginPageFactory; use OpenDxp\Bundle\AdminBundle\Security\CsrfProtectionHandler; use OpenDxp\Controller\KernelControllerEventInterface; use OpenDxp\Controller\KernelResponseEventInterface; -use OpenDxp\Http\Request\Host\GeneralHostResolver; use OpenDxp\Http\ResponseHelper; -use OpenDxp\Logger; -use OpenDxp\Security\SecurityHelper; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -57,7 +58,8 @@ public function __construct( protected TranslatorInterface $translator, protected EventDispatcherInterface $eventDispatcher, private readonly LoginPageFactory $loginPageFactory, - ) {} + ) { + } public function onKernelControllerEvent(ControllerEvent $event): void { @@ -89,7 +91,8 @@ public function loginAction( Request $request, CsrfProtectionHandler $csrfProtection, #[MapQueryParameter(name: 'too_many_attempts')] ?string $tooManyAttempts = null, - ): RedirectResponse|Response { + ): Response { + $queryParams = $request->query->all(); if ($request->attributes->get('_route') === 'opendxp_admin_login_fallback') { @@ -152,26 +155,15 @@ public function loginCheckAction( public function lostpasswordAction( Request $request, CsrfProtectionHandler $csrfProtection, + LostPasswordPayload $payload, LostPasswordHandler $lostPassword, - GeneralHostResolver $generalHostResolver, ): Response { $params = $this->loginPageFactory->create($request)->base(); - if ($request->getMethod() === 'POST' && $request->request->has('username')) { - $result = $lostPassword( - username: (string) $request->request->get('username'), - clientIp: (string) $request->getClientIp(), - domain: $generalHostResolver->resolve(['source' => $request]) ?? '', - ); - - if ($result->eventResponse !== null) { - return $result->eventResponse; - } + $result = $lostPassword($payload); - if ($result->error !== null) { - Logger::error('Lost password service: ' . $result->error); - usleep(random_int(50, 200)); - } + if ($result->eventResponse !== null) { + return $result->eventResponse; } $csrfProtection->regenerateCsrfToken($request->getSession()); @@ -181,36 +173,16 @@ public function lostpasswordAction( #[Route('/login/deeplink', name: 'opendxp_admin_login_deeplink')] public function deeplinkAction( - Request $request, - #[MapQueryParameter] string $perspective = '', + DeeplinkHandler $handler, + DeeplinkPayload $payload, ): Response { - $queryString = $request->server->get('QUERY_STRING'); - - if (preg_match('/(document|asset|object)_(\d+)_([a-z]+)/', $queryString, $deeplink)) { - $deeplink = $deeplink[0]; - $perspective = strip_tags($perspective); - - if (strpos($queryString, 'token')) { - $url = $this->dispatchLoginRedirect([ - 'deeplink' => $deeplink, - 'perspective' => $perspective, - ]); + $result = $handler($payload); - return $this->redirect($url . '&' . $queryString); - } - - if ($queryString) { - return $this->render('@OpenDxpAdmin/admin/login/deeplink.html.twig', [ - 'tab' => $deeplink, - 'redirect' => $this->dispatchLoginRedirect([ - 'deeplink' => 'true', - 'perspective' => $perspective, - ]), - ]); - } + if ($result->redirectUrl) { + return $this->redirect($result->redirectUrl); } - throw $this->createNotFoundException(); + return $this->render($result->template, $result->params); } #[Route('/login/2fa', name: 'opendxp_admin_2fa')] @@ -234,36 +206,48 @@ public function twoFactorAuthenticationAction(Request $request): Response } #[Route('/login/2fa-setup', name: 'opendxp_admin_2fa_setup')] - public function twoFactorSetupAuthenticationAction( + public function twoFactorSetupAuthenticationAction(Request $request): Response|RedirectResponse + { + if ($request->isMethod('post')) { + return $this->forward(self::class . '::twoFactorSetupSaveAction'); + } + + return $this->forward(self::class . '::twoFactorSetupGenerateAction', [], $request->query->all()); + } + + #[Route('/login/2fa-setup-save', name: 'opendxp_admin_2fa_setup_save')] + public function twoFactorSetupSaveAction( + SaveTwoFactorSetupPayload $payload, + SaveTwoFactorSetupHandler $saveSetup, + ): RedirectResponse { + try { + $saveSetup($payload); + } catch (\Throwable) { + return new RedirectResponse($this->generateUrl('opendxp_admin_2fa_setup', ['error' => '2fa_wrong'])); + } + + return new RedirectResponse($this->generateUrl('opendxp_admin_login')); + } + + #[Route('/login/2fa-setup-generate', name: 'opendxp_admin_2fa_setup_generate')] + public function twoFactorSetupGenerateAction( Request $request, + GenerateTwoFactorSetupPayload $payload, GenerateTwoFactorSetupHandler $generateSetup, - SaveTwoFactorSetupHandler $saveSetup, - #[MapQueryParameter] ?string $error = null, ): Response { + $params = $this->loginPageFactory->create($request)->base(); $params['setup'] = true; - if ($error) { - $params['error'] = $error; - } - - if ($request->isMethod('post')) { - try { - $saveSetup( - secret: (string) $request->getSession()->get('2fa_secret'), - authCode: (string) $request->request->get('_auth_code'), - ); - } catch (\Throwable) { - return new RedirectResponse($this->generateUrl('opendxp_admin_2fa_setup', ['error' => '2fa_wrong'])); - } - - return new RedirectResponse($this->generateUrl('opendxp_admin_login')); + if ($payload->error) { + $params['error'] = $payload->error; } $result = $generateSetup(); - $request->getSession()->set('2fa_secret', $result->secret); $params['image'] = $result->qrDataUri; + $request->getSession()->set('2fa_secret', $result->secret); + return $this->render('@OpenDxpAdmin/admin/login/two_factor_setup.html.twig', $params); } diff --git a/src/Controller/Admin/MiscController.php b/src/Controller/Admin/MiscController.php index a2b674dd..0970d4f4 100644 --- a/src/Controller/Admin/MiscController.php +++ b/src/Controller/Admin/MiscController.php @@ -17,26 +17,32 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\Admin; -use InvalidArgumentException; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetIconListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetJsonTranslationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\AdminCss\AdminCssHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetAvailableControllerReferences\GetAvailableControllerReferencesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetAvailableLanguages\GetAvailableLanguagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetAvailableTemplates\GetAvailableTemplatesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetCountryList\GetCountryListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetIconList\GetIconListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetIconList\GetIconListPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetJsonTranslations\GetJsonTranslationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetJsonTranslations\GetJsonTranslationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetLanguageFlag\GetLanguageFlagHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetLanguageFlag\GetLanguageFlagPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetLanguageList\GetLanguageListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetValidFilename\GetValidFilenameHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\GetValidFilename\GetValidFilenamePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\Maintenance\MaintenanceHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\Maintenance\MaintenancePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\ScriptProxy\ScriptProxyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Misc\ScriptProxy\ScriptProxyPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use OpenDxp\Bundle\AdminBundle\System\AdminConfig; -use OpenDxp\Bundle\AdminBundle\Tool as AdminTool; -use OpenDxp\Config; -use OpenDxp\Controller\Config\ControllerDataProvider; -use OpenDxp\Localization\LocaleServiceInterface; -use OpenDxp\Tool; -use OpenDxp\Tool\Storage; -use OpenDxp\Translation\Translator; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Profiler\Profiler; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -47,40 +53,31 @@ class MiscController extends AdminAbstractController { #[Route('/get-available-controller-references', name: 'opendxp_admin_misc_getavailablecontroller_references', methods: ['GET'])] - public function getAvailableControllerReferencesAction(ControllerDataProvider $provider): JsonResponse - { - $controllerReferences = $provider->getControllerReferences(); - - $result = array_map(fn ($controller) => [ - 'name' => $controller, - ], $controllerReferences); + public function getAvailableControllerReferencesAction( + EmptyPayload $payload, + GetAvailableControllerReferencesHandler $handler, + ): JsonResponse { + $result = $handler($payload); - return $this->adminJson(ApiResponse::ok(['data' => $result, 'total' => count($result)])); + return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } #[Route('/get-available-templates', name: 'opendxp_admin_misc_getavailabletemplates', methods: ['GET'])] - public function getAvailableTemplatesAction(ControllerDataProvider $provider): JsonResponse - { - $templates = $provider->getTemplates(); - - sort($templates, SORT_NATURAL | SORT_FLAG_CASE); - - $result = array_map(static fn ($template) => [ - 'path' => $template, - ], $templates); + public function getAvailableTemplatesAction( + EmptyPayload $payload, + GetAvailableTemplatesHandler $handler, + ): JsonResponse { + $result = $handler($payload); - return $this->adminJson([ - 'data' => $result, - ]); + return $this->adminJson(['data' => $result->data]); } #[Route('/json-translations-system', name: 'opendxp_admin_misc_jsontranslationssystem', methods: ['GET'])] public function jsonTranslationsSystemAction( - GetJsonTranslationsHandler $getJsonTranslations, - Translator $translator, - #[MapQueryParameter] ?string $language = null, + GetJsonTranslationsPayload $payload, + GetJsonTranslationsHandler $handler, ): Response { - $result = $getJsonTranslations($translator, $language); + $result = $handler($payload); $response = new Response('opendxp.system_i18n = ' . $this->encodeJson($result->translations) . ';'); $response->headers->set('Content-Type', 'text/javascript'); @@ -93,68 +90,48 @@ public function jsonTranslationsSystemAction( */ #[Route('/script-proxy', name: 'opendxp_admin_misc_scriptproxy', methods: ['GET'])] public function scriptProxyAction( - #[MapQueryParameter] ?string $storageFile = null, + ScriptProxyPayload $payload, + ScriptProxyHandler $handler, ): Response { - if (!$storageFile) { - throw new InvalidArgumentException('The parameter storageFile is required'); - } + $result = $handler($payload); - $fileExtension = pathinfo($storageFile, PATHINFO_EXTENSION); - $storage = Storage::get('admin'); - $scriptsContent = $storage->read($storageFile); + $lifetime = 86400; - if (!empty($scriptsContent)) { - $contentType = 'text/javascript'; - if ($fileExtension === 'css') { - $contentType = 'text/css'; - } + $response = new Response($result->content); + $response->headers->set('Cache-Control', 'max-age=' . $lifetime); + $response->headers->set('Pragma', ''); + $response->headers->set('Content-Type', $result->contentType); + $response->headers->set('Expires', gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT'); - $lifetime = 86400; - - $response = new Response($scriptsContent); - $response->headers->set('Cache-Control', 'max-age=' . $lifetime); - $response->headers->set('Pragma', ''); - $response->headers->set('Content-Type', $contentType); - $response->headers->set('Expires', gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT'); - - return $response; - } - - throw $this->createNotFoundException('Scripts not found'); + return $response; } #[Route('/admin-css', name: 'opendxp_admin_misc_admincss', methods: ['GET'])] - public function adminCssAction(Config $config): Response - { - // customviews config - $cvData = \OpenDxp\Bundle\AdminBundle\CustomView\Config::get(); - - // languages - $languages = \OpenDxp\Tool::getValidLanguages(); - $adminLanguages = \OpenDxp\Tool\Admin::getLanguages(); - $languages = array_unique([...$languages, ...$adminLanguages]); + public function adminCssAction( + EmptyPayload $payload, + AdminCssHandler $handler, + ): Response { + $result = $handler($payload); $response = $this->render('@OpenDxpAdmin/admin/misc/admin_css.html.twig', [ - 'customviews' => $cvData, - 'adminSettings' => AdminConfig::get(), - 'languages' => $languages, + 'customviews' => $result->customviews, + 'adminSettings' => $result->adminSettings, + 'languages' => $result->languages, ]); + $response->headers->set('Content-Type', 'text/css; charset=UTF-8'); return $response; } - #[Route('/ping', name: 'opendxp_admin_misc_ping', methods: ['GET'])] - public function pingAction(): JsonResponse - { - return $this->adminJson(ApiResponse::ok()); - } - #[Route('/available-languages', name: 'opendxp_admin_misc_availablelanguages', methods: ['GET'])] - public function availableLanguagesAction(): Response - { - $locales = Tool::getSupportedLocales(); - $response = new Response('opendxp.available_languages = ' . $this->encodeJson($locales) . ';'); + public function availableLanguagesAction( + EmptyPayload $payload, + GetAvailableLanguagesHandler $handler, + ): Response { + $result = $handler($payload); + + $response = new Response('opendxp.available_languages = ' . $this->encodeJson($result->locales) . ';'); $response->headers->set('Content-Type', 'text/javascript'); return $response; @@ -162,76 +139,53 @@ public function availableLanguagesAction(): Response #[Route('/get-valid-filename', name: 'opendxp_admin_misc_getvalidfilename', methods: ['GET'])] public function getValidFilenameAction( - #[MapQueryParameter] ?string $value = null, - #[MapQueryParameter] ?string $type = null, + GetValidFilenamePayload $payload, + GetValidFilenameHandler $handler, ): JsonResponse { - return $this->adminJson([ - 'filename' => \OpenDxp\Model\Element\Service::getValidKey($value, $type), - ]); + $result = $handler($payload); + + return $this->adminJson(['filename' => $result->filename]); } #[IsGranted(CorePermission::MaintenanceMode->value)] #[Route('/maintenance', name: 'opendxp_admin_misc_maintenance', methods: ['POST'])] public function maintenanceAction( - Request $request, - Tool\MaintenanceModeHelperInterface $maintenanceModeHelper, - #[MapQueryParameter] ?string $activate = null, - #[MapQueryParameter] ?string $deactivate = null, + MaintenancePayload $payload, + MaintenanceHandler $handler, ): JsonResponse { - - if ($activate) { - $maintenanceModeHelper->activate($request->getSession()->getId()); - } - - if ($deactivate) { - $maintenanceModeHelper->deactivate(); - } + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/country-list', name: 'opendxp_admin_misc_countrylist', methods: ['GET'])] - public function countryListAction(LocaleServiceInterface $localeService): JsonResponse - { - $countries = $localeService->getDisplayRegions(); - asort($countries); - $options = []; - - foreach ($countries as $short => $translation) { - if (strlen($short) === 2) { - $options[] = [ - 'name' => $translation, - 'code' => $short, - ]; - } - } + public function countryListAction( + EmptyPayload $payload, + GetCountryListHandler $handler, + ): JsonResponse { + $result = $handler($payload); - return $this->adminJson(['data' => $options]); + return $this->adminJson(['data' => $result->data]); } #[Route('/language-list', name: 'opendxp_admin_misc_languagelist', methods: ['GET'])] - public function languageListAction(): JsonResponse - { - $locales = Tool::getSupportedLocales(); - $options = []; - - foreach ($locales as $short => $translation) { - $options[] = [ - 'name' => $translation, - 'code' => $short, - ]; - } + public function languageListAction( + EmptyPayload $payload, + GetLanguageListHandler $handler, + ): JsonResponse { + $result = $handler($payload); - return $this->adminJson(['data' => $options]); + return $this->adminJson(['data' => $result->data]); } #[Route('/get-language-flag', name: 'opendxp_admin_misc_getlanguageflag', methods: ['GET'])] public function getLanguageFlagAction( - #[MapQueryParameter] ?string $language = null, + GetLanguageFlagPayload $payload, + GetLanguageFlagHandler $handler, ): BinaryFileResponse { - $iconPath = AdminTool::getLanguageFlagFile($language); + $result = $handler($payload); - $response = new BinaryFileResponse($iconPath); + $response = new BinaryFileResponse($result->iconPath); $response->headers->set('Content-Type', 'image/svg+xml'); return $response; @@ -239,29 +193,35 @@ public function getLanguageFlagAction( #[Route('/icon-list', name: 'opendxp_admin_misc_iconlist', methods: ['GET'])] public function iconListAction( - GetIconListHandler $getIconList, + GetIconListPayload $payload, + GetIconListHandler $handler, ?Profiler $profiler, - #[MapQueryParameter] ?string $type = null, ): Response { if ($profiler) { $profiler->disable(); } - if ($type === null) { + if ($payload->type === null) { return $this->render('@OpenDxpAdmin/admin/misc/icon_library_reload.html.twig'); } - $result = $getIconList($type); + $result = $handler($payload); return $this->render('@OpenDxpAdmin/admin/misc/icon_list.html.twig', [ - 'icons' => $result->icons, - 'iconsCss' => $result->iconsCss, - 'type' => $result->type, + 'icons' => $result->icons, + 'iconsCss' => $result->iconsCss, + 'type' => $result->type, 'extraInfo' => $result->extraInfo, - 'source' => $result->source, + 'source' => $result->source, ]); } + #[Route('/ping', name: 'opendxp_admin_misc_ping', methods: ['GET'])] + public function pingAction(): JsonResponse + { + return $this->adminJson(ApiResponse::ok()); + } + #[Route('/test', name: 'opendxp_admin_misc_test')] public function testAction(): Response { diff --git a/src/Controller/Admin/NotificationController.php b/src/Controller/Admin/NotificationController.php index ff5e9e72..7ca757b3 100644 --- a/src/Controller/Admin/NotificationController.php +++ b/src/Controller/Admin/NotificationController.php @@ -19,24 +19,23 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Notification\DeleteAllNotificationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Notification\DeleteNotificationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindAllNotificationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindLastUnreadNotificationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindNotificationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Notification\GetRecipientsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Notification\MarkAsReadNotificationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Notification\SendNotificationHandler; -use OpenDxp\Model\Element\Service; -use OpenDxp\Model\Notification\Service\NotificationService; -use OpenDxp\Model\Notification\Service\UserService; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\DeleteAllNotifications\DeleteAllNotificationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\DeleteNotification\DeleteNotificationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindAllNotifications\FindAllNotificationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindAllNotifications\FindAllNotificationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindLastUnreadNotifications\FindLastUnreadNotificationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindLastUnreadNotifications\FindLastUnreadNotificationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\FindNotification\FindNotificationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\GetRecipients\GetRecipientsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\MarkAsReadNotification\MarkAsReadNotificationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\SendNotification\SendNotificationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Notification\SendNotification\SendNotificationPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; -use Symfony\Contracts\Translation\TranslatorInterface; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal @@ -48,10 +47,9 @@ class NotificationController extends AdminAbstractController #[Route('/recipients', name: 'opendxp_admin_notification_recipients', methods: ['GET'])] public function recipientsAction( GetRecipientsHandler $getRecipients, - UserService $service, - TranslatorInterface $translator, + EmptyPayload $payload, ): JsonResponse { - $result = $getRecipients($service, $translator); + $result = $getRecipients($payload); return $this->adminJson($result->data); } @@ -60,24 +58,9 @@ public function recipientsAction( #[Route('/send', name: 'opendxp_admin_notification_send', methods: ['POST'])] public function sendAction( SendNotificationHandler $sendNotification, - Request $request, - NotificationService $service, + SendNotificationPayload $payload, ): JsonResponse { - $elementId = (int) $request->request->get('elementId', 0); - $elementType = $request->request->get('elementType'); - $element = null; - - if ($elementId && $elementType) { - $element = Service::getElementById($elementType, $elementId); - } - - $sendNotification( - service: $service, - recipientId: (int) $request->request->get('recipientId', 0), - title: (string) $request->request->get('title', ''), - message: (string) $request->request->get('message', ''), - element: $element, - ); + $sendNotification($payload); return $this->adminJson(ApiResponse::ok()); } @@ -86,11 +69,10 @@ public function sendAction( #[Route('/find', name: 'opendxp_admin_notification_find', methods: ['GET'])] public function findAction( FindNotificationHandler $findNotification, - NotificationService $service, - #[MapQueryParameter] int $id = 0, + IdQueryPayload $payload, ): JsonResponse { try { - $result = $findNotification($service, $id); + $result = $findNotification($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data])); } catch (\Throwable) { @@ -102,15 +84,9 @@ public function findAction( #[Route('/find-all', name: 'opendxp_admin_notification_findall', methods: ['POST'])] public function findAllAction( FindAllNotificationsHandler $findAllNotifications, - Request $request, - NotificationService $service, + FindAllNotificationsPayload $payload, ): JsonResponse { - $result = $findAllNotifications( - service: $service, - request: $request, - offset: $request->request->getInt('start'), - limit: $request->request->getInt('limit', 40), - ); + $result = $findAllNotifications($payload); return $this->adminJson(ApiResponse::ok(['total' => $result->total, 'data' => $result->data])); } @@ -119,13 +95,9 @@ public function findAllAction( #[Route('/find-last-unread', name: 'opendxp_admin_notification_findlastunread', methods: ['GET'])] public function findLastUnreadAction( FindLastUnreadNotificationsHandler $findLastUnread, - NotificationService $service, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $lastUpdate = null, + FindLastUnreadNotificationsPayload $payload, ): JsonResponse { - $result = $findLastUnread( - service: $service, - lastUpdate: $lastUpdate ?? time(), - ); + $result = $findLastUnread($payload); return $this->adminJson(ApiResponse::ok(['total' => $result->total, 'data' => $result->data, 'unread' => $result->unread])); } @@ -134,10 +106,9 @@ public function findLastUnreadAction( #[Route('/mark-as-read', name: 'opendxp_admin_notification_markasread', methods: ['PUT'])] public function markAsReadAction( MarkAsReadNotificationHandler $handler, - NotificationService $service, - #[MapQueryParameter] int $id = 0, + IdQueryPayload $payload, ): JsonResponse { - $handler($service, $id); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -146,19 +117,20 @@ public function markAsReadAction( #[Route('/delete', name: 'opendxp_admin_notification_delete', methods: ['DELETE'])] public function deleteAction( DeleteNotificationHandler $handler, - NotificationService $service, - #[MapQueryParameter] int $id = 0, + IdQueryPayload $payload, ): JsonResponse { - $handler($service, $id); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Notifications->value)] #[Route('/delete-all', name: 'opendxp_admin_notification_deleteall', methods: ['DELETE'])] - public function deleteAllAction(DeleteAllNotificationsHandler $deleteAllNotifications, NotificationService $service): JsonResponse - { - $deleteAllNotifications($service); + public function deleteAllAction( + DeleteAllNotificationsHandler $deleteAllNotifications, + EmptyPayload $payload, + ): JsonResponse { + $deleteAllNotifications($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/PortalController.php b/src/Controller/Admin/PortalController.php index 92343f14..6874f875 100644 --- a/src/Controller/Admin/PortalController.php +++ b/src/Controller/Admin/PortalController.php @@ -19,21 +19,27 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\AddWidgetHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\CreateDashboardHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\DeleteDashboardHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardConfigurationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModificationStatisticsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedAssetsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedDocumentsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedObjectsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\RemoveWidgetHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\ReorderWidgetHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Portal\UpdatePortletConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\AddWidget\AddWidgetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\AddWidget\AddWidgetPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\CreateDashboard\CreateDashboardHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\CreateDashboard\CreateDashboardPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\DeleteDashboard\DeleteDashboardHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\DeleteDashboard\DeleteDashboardPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardConfiguration\GetDashboardConfigurationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardConfiguration\GetDashboardConfigurationPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardList\GetDashboardListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModificationStatistics\GetModificationStatisticsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedAssets\GetModifiedAssetsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedDocuments\GetModifiedDocumentsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedObjects\GetModifiedObjectsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\RemoveWidget\RemoveWidgetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\RemoveWidget\RemoveWidgetPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\ReorderWidget\ReorderWidgetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\ReorderWidget\ReorderWidgetPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\UpdatePortletConfig\UpdatePortletConfigHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Portal\UpdatePortletConfig\UpdatePortletConfigPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; /** @@ -43,20 +49,22 @@ class PortalController extends AdminAbstractController { #[Route('/dashboard-list', name: 'opendxp_admin_portal_dashboardlist', methods: ['GET'])] - public function dashboardListAction(GetDashboardListHandler $getDashboardList): JsonResponse - { - $result = $getDashboardList(); + public function dashboardListAction( + GetDashboardListHandler $getDashboardList, + EmptyPayload $payload, + ): JsonResponse { + $result = $getDashboardList($payload); return $this->adminJson($result->dashboards); } #[Route('/create-dashboard', name: 'opendxp_admin_portal_createdashboard', methods: ['POST'])] - public function createDashboardAction(Request $request, CreateDashboardHandler $createDashboard): JsonResponse - { - $key = trim($request->request->get('key', '')); - + public function createDashboardAction( + CreateDashboardHandler $createDashboard, + CreateDashboardPayload $payload, + ): JsonResponse { try { - $createDashboard($key); + $createDashboard($payload); } catch (\InvalidArgumentException $e) { return $this->adminJson(ApiResponse::error($e->getMessage())); } @@ -65,9 +73,11 @@ public function createDashboardAction(Request $request, CreateDashboardHandler $ } #[Route('/delete-dashboard', name: 'opendxp_admin_portal_deletedashboard', methods: ['DELETE'])] - public function deleteDashboardAction(Request $request, DeleteDashboardHandler $deleteDashboard): JsonResponse - { - $deleteDashboard((string) $request->request->get('key')); + public function deleteDashboardAction( + DeleteDashboardHandler $deleteDashboard, + DeleteDashboardPayload $payload, + ): JsonResponse { + $deleteDashboard($payload); return $this->adminJson(ApiResponse::ok()); } @@ -75,91 +85,89 @@ public function deleteDashboardAction(Request $request, DeleteDashboardHandler $ #[Route('/get-configuration', name: 'opendxp_admin_portal_getconfiguration', methods: ['GET'])] public function getConfigurationAction( GetDashboardConfigurationHandler $getDashboardConfiguration, - #[MapQueryParameter] ?string $key = null, + GetDashboardConfigurationPayload $payload, ): JsonResponse { - $result = $getDashboardConfiguration($key); + $result = $getDashboardConfiguration($payload); return $this->adminJson($result->config); } #[Route('/remove-widget', name: 'opendxp_admin_portal_removewidget', methods: ['DELETE'])] - public function removeWidgetAction(Request $request, RemoveWidgetHandler $removeWidget): JsonResponse - { - $widgetId = $request->request->has('id') ? (int) $request->request->get('id') : null; - $removeWidget( - dashboardId: (string) $request->request->get('key'), - widgetId: $widgetId, - ); + public function removeWidgetAction( + RemoveWidgetHandler $removeWidget, + RemoveWidgetPayload $payload, + ): JsonResponse { + $removeWidget($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/add-widget', name: 'opendxp_admin_portal_addwidget', methods: ['POST'])] - public function addWidgetAction(Request $request, AddWidgetHandler $addWidget): JsonResponse - { - $result = $addWidget( - dashboardId: (string) $request->request->get('key'), - type: (string) $request->request->get('type'), - ); + public function addWidgetAction( + AddWidgetHandler $addWidget, + AddWidgetPayload $payload, + ): JsonResponse { + $result = $addWidget($payload); return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } #[Route('/reorder-widget', name: 'opendxp_admin_portal_reorderwidget', methods: ['PUT'])] - public function reorderWidgetAction(Request $request, ReorderWidgetHandler $reorderWidget): JsonResponse - { - $widgetId = $request->request->has('id') ? (int) $request->request->get('id') : null; - $reorderWidget( - dashboardId: (string) $request->request->get('key'), - widgetId: $widgetId, - column: $request->request->getInt('column'), - row: $request->request->getInt('row'), - ); + public function reorderWidgetAction( + ReorderWidgetHandler $reorderWidget, + ReorderWidgetPayload $payload, + ): JsonResponse { + $reorderWidget($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/update-portlet-config', name: 'opendxp_admin_portal_updateportletconfig', methods: ['PUT'])] - public function updatePortletConfigAction(Request $request, UpdatePortletConfigHandler $updatePortletConfig): JsonResponse - { - $portletId = $request->request->has('id') ? (int) $request->request->get('id') : null; - $updatePortletConfig( - dashboardKey: (string) $request->request->get('key'), - portletId: $portletId, - configuration: $request->request->get('config'), - ); + public function updatePortletConfigAction( + UpdatePortletConfigHandler $updatePortletConfig, + UpdatePortletConfigPayload $payload, + ): JsonResponse { + $updatePortletConfig($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/portlet-modified-documents', name: 'opendxp_admin_portal_portletmodifieddocuments', methods: ['GET'])] - public function portletModifiedDocumentsAction(GetModifiedDocumentsHandler $getModifiedDocuments): JsonResponse - { - $result = $getModifiedDocuments(); + public function portletModifiedDocumentsAction( + GetModifiedDocumentsHandler $getModifiedDocuments, + EmptyPayload $payload, + ): JsonResponse { + $result = $getModifiedDocuments($payload); return $this->adminJson(['documents' => $result->documents]); } #[Route('/portlet-modified-assets', name: 'opendxp_admin_portal_portletmodifiedassets', methods: ['GET'])] - public function portletModifiedAssetsAction(GetModifiedAssetsHandler $getModifiedAssets): JsonResponse - { - $result = $getModifiedAssets(); + public function portletModifiedAssetsAction( + GetModifiedAssetsHandler $getModifiedAssets, + EmptyPayload $payload, + ): JsonResponse { + $result = $getModifiedAssets($payload); return $this->adminJson(['assets' => $result->assets]); } #[Route('/portlet-modified-objects', name: 'opendxp_admin_portal_portletmodifiedobjects', methods: ['GET'])] - public function portletModifiedObjectsAction(GetModifiedObjectsHandler $getModifiedObjects): JsonResponse - { - $result = $getModifiedObjects(); + public function portletModifiedObjectsAction( + GetModifiedObjectsHandler $getModifiedObjects, + EmptyPayload $payload, + ): JsonResponse { + $result = $getModifiedObjects($payload); return $this->adminJson(['objects' => $result->objects]); } #[Route('/portlet-modification-statistics', name: 'opendxp_admin_portal_portletmodificationstatistics', methods: ['GET'])] - public function portletModificationStatisticsAction(GetModificationStatisticsHandler $getModificationStatistics): JsonResponse - { - $result = $getModificationStatistics(); + public function portletModificationStatisticsAction( + GetModificationStatisticsHandler $getModificationStatistics, + EmptyPayload $payload, + ): JsonResponse { + $result = $getModificationStatistics($payload); return $this->adminJson(['data' => $result->data]); } diff --git a/src/Controller/Admin/RecyclebinController.php b/src/Controller/Admin/RecyclebinController.php index a493904f..2101f1db 100644 --- a/src/Controller/Admin/RecyclebinController.php +++ b/src/Controller/Admin/RecyclebinController.php @@ -19,16 +19,18 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\AddToRecyclebinHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\DeleteRecyclebinItemHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\ListRecyclebinHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\AddToRecyclebin\AddToRecyclebinHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\AddToRecyclebin\AddToRecyclebinPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\DeleteRecyclebinItem\DeleteRecyclebinItemHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\FlushRecyclebin\FlushRecyclebinHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\ListRecyclebin\ListRecyclebinHandler; use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RecyclebinPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RestoreRecyclebinItemHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RestoreRecyclebinItem\RestoreRecyclebinItemHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RestoreRecyclebinItem\RestoreRecyclebinItemPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Controller\KernelControllerEventInterface; -use OpenDxp\Model\Element; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -70,33 +72,31 @@ private function destroyRecyclebinItem(DeleteRecyclebinItemHandler $handler, Rec #[IsGranted(CorePermission::Recyclebin->value)] #[Route('/recyclebin/restore', name: 'opendxp_admin_recyclebin_restore', methods: ['POST'])] public function restoreAction( + RestoreRecyclebinItemPayload $payload, RestoreRecyclebinItemHandler $restoreRecyclebinItem, - Request $request, ): JsonResponse { - $restoreRecyclebinItem((int) $request->request->get('id')); + $restoreRecyclebinItem($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Recyclebin->value)] #[Route('/recyclebin/flush', name: 'opendxp_admin_recyclebin_flush', methods: ['DELETE'])] - public function flushAction(): JsonResponse - { - $bin = new Element\Recyclebin(); - $bin->flush(); + public function flushAction( + EmptyPayload $payload, + FlushRecyclebinHandler $flushRecyclebin, + ): JsonResponse { + $flushRecyclebin($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/recyclebin/add', name: 'opendxp_admin_recyclebin_add', methods: ['POST'])] public function addAction( + AddToRecyclebinPayload $payload, AddToRecyclebinHandler $addToRecyclebin, - Request $request, ): JsonResponse { - $addToRecyclebin( - type: $request->request->get('type'), - id: $request->request->getInt('id'), - ); + $addToRecyclebin($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/Settings/ThumbnailController.php b/src/Controller/Admin/Settings/ThumbnailController.php index ea2afa00..fde54d7c 100644 --- a/src/Controller/Admin/Settings/ThumbnailController.php +++ b/src/Controller/Admin/Settings/ThumbnailController.php @@ -18,15 +18,17 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\AddThumbnailHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteThumbnailHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetDownloadableThumbnailsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetThumbnailHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetThumbnailTreeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\AddThumbnail\AddThumbnailPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\AddThumbnail\AddThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteThumbnail\DeleteThumbnailPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteThumbnail\DeleteThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetDownloadableThumbnails\GetDownloadableThumbnailsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetThumbnail\GetThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetThumbnailTree\GetThumbnailTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateThumbnail\UpdateThumbnailPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateThumbnail\UpdateThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -54,17 +56,17 @@ public function thumbnailDownloadableAction(GetDownloadableThumbnailsHandler $ge } #[Route('/settings/thumbnail-add', name: 'opendxp_admin_settings_thumbnailadd', methods: ['POST'])] - public function thumbnailAddAction(Request $request, AddThumbnailHandler $addThumbnail): JsonResponse + public function thumbnailAddAction(AddThumbnailPayload $payload, AddThumbnailHandler $addThumbnail): JsonResponse { - $result = $addThumbnail($request->request->get('name')); + $result = $addThumbnail($payload); return $this->adminJson(ApiResponse::fromBool($result->created, ['id' => $result->id])); } #[Route('/settings/thumbnail-delete', name: 'opendxp_admin_settings_thumbnaildelete', methods: ['DELETE'])] - public function thumbnailDeleteAction(Request $request, DeleteThumbnailHandler $deleteThumbnail): JsonResponse + public function thumbnailDeleteAction(DeleteThumbnailPayload $payload, DeleteThumbnailHandler $deleteThumbnail): JsonResponse { - $deleteThumbnail($request->request->get('name')); + $deleteThumbnail($payload); return $this->adminJson(ApiResponse::ok()); } @@ -80,14 +82,9 @@ public function thumbnailGetAction( } #[Route('/settings/thumbnail-update', name: 'opendxp_admin_settings_thumbnailupdate', methods: ['PUT'])] - public function thumbnailUpdateAction(Request $request, UpdateThumbnailHandler $updateThumbnail): JsonResponse + public function thumbnailUpdateAction(UpdateThumbnailPayload $payload, UpdateThumbnailHandler $updateThumbnail): JsonResponse { - $updateThumbnail( - name: $request->request->get('name'), - settingsData: $this->decodeJson($request->request->get('settings')), - mediaData: $this->decodeJson($request->request->get('medias')), - mediaOrder: $this->decodeJson($request->request->get('mediaOrder')), - ); + $updateThumbnail($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/Settings/VideoThumbnailController.php b/src/Controller/Admin/Settings/VideoThumbnailController.php index e2fcbab6..1baf77e7 100644 --- a/src/Controller/Admin/Settings/VideoThumbnailController.php +++ b/src/Controller/Admin/Settings/VideoThumbnailController.php @@ -18,15 +18,17 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\AddVideoThumbnailHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteVideoThumbnailHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailTreeHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateVideoThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\AddVideoThumbnail\AddVideoThumbnailPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\AddVideoThumbnail\AddVideoThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteVideoThumbnail\DeleteVideoThumbnailPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteVideoThumbnail\DeleteVideoThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnail\GetVideoThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailList\GetVideoThumbnailListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailTree\GetVideoThumbnailTreeHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateVideoThumbnail\UpdateVideoThumbnailPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateVideoThumbnail\UpdateVideoThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; @@ -70,17 +72,17 @@ public function videoThumbnailListAction(GetVideoThumbnailListHandler $getVideoT } #[Route('/settings/video-thumbnail-add', name: 'opendxp_admin_settings_videothumbnailadd', methods: ['POST'])] - public function videoThumbnailAddAction(Request $request, AddVideoThumbnailHandler $addVideoThumbnail): JsonResponse + public function videoThumbnailAddAction(AddVideoThumbnailPayload $payload, AddVideoThumbnailHandler $addVideoThumbnail): JsonResponse { - $result = $addVideoThumbnail($request->request->get('name')); + $result = $addVideoThumbnail($payload); return $this->adminJson(ApiResponse::fromBool($result->created, ['id' => $result->id])); } #[Route('/settings/video-thumbnail-delete', name: 'opendxp_admin_settings_videothumbnaildelete', methods: ['DELETE'])] - public function videoThumbnailDeleteAction(Request $request, DeleteVideoThumbnailHandler $deleteVideoThumbnail): JsonResponse + public function videoThumbnailDeleteAction(DeleteVideoThumbnailPayload $payload, DeleteVideoThumbnailHandler $deleteVideoThumbnail): JsonResponse { - $deleteVideoThumbnail($request->request->get('name')); + $deleteVideoThumbnail($payload); return $this->adminJson(ApiResponse::ok()); } @@ -96,14 +98,9 @@ public function videoThumbnailGetAction( } #[Route('/settings/video-thumbnail-update', name: 'opendxp_admin_settings_videothumbnailupdate', methods: ['PUT'])] - public function videoThumbnailUpdateAction(Request $request, UpdateVideoThumbnailHandler $updateVideoThumbnail): JsonResponse + public function videoThumbnailUpdateAction(UpdateVideoThumbnailPayload $payload, UpdateVideoThumbnailHandler $updateVideoThumbnail): JsonResponse { - $updateVideoThumbnail( - name: $request->request->get('name'), - settingsData: $this->decodeJson($request->request->get('settings')), - mediaData: $this->decodeJson($request->request->get('medias')), - mediaOrder: $this->decodeJson($request->request->get('mediaOrder')), - ); + $updateVideoThumbnail($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/SettingsController.php b/src/Controller/Admin/SettingsController.php index 31464e8f..df03cce3 100644 --- a/src/Controller/Admin/SettingsController.php +++ b/src/Controller/Admin/SettingsController.php @@ -19,38 +19,39 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearOpenDxpCacheHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearOutputCacheHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearSymfonyCacheHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearTemporaryFilesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\CreatePredefinedMetadataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\CreatePredefinedPropertyHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\CreateWebsiteSettingHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteCustomLogoHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeletePredefinedMetadataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeletePredefinedPropertyHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteWebsiteSettingHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearCache\ClearCachePayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearCache\ClearCacheHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearOutputCache\ClearOutputCacheHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearTemporaryFiles\ClearTemporaryFilesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\CreatePredefinedMetadata\CreatePredefinedMetadataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\CreatePredefinedProperty\CreatePredefinedPropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\CreateWebsiteSetting\CreateWebsiteSettingHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteCustomLogo\DeleteCustomLogoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeletePredefinedMetadata\DeletePredefinedMetadataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeletePredefinedProperty\DeletePredefinedPropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteWebsiteSetting\DeleteWebsiteSettingHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedMetadataPayload; use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedPropertyPayload; use OpenDxp\Bundle\AdminBundle\Handler\Settings\WebsiteSettingPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\DisplayCustomLogoHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAppearanceSettingsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAdminLanguagesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAlgorithmsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableCountriesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableSitesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetFilteredPredefinedMetadataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetPredefinedMetadataListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetPredefinedPropertiesListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetSystemSettingsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetWebsiteSettingsListHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveAppearanceSettingsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSystemSettingsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\ThumbnailAdapterCheckHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedMetadataHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedPropertyHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateWebsiteSettingHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Settings\UploadCustomLogoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DisplayCustomLogo\DisplayCustomLogoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAppearanceSettings\GetAppearanceSettingsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAdminLanguages\GetAvailableAdminLanguagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAlgorithms\GetAvailableAlgorithmsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableCountries\GetAvailableCountriesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableSites\GetAvailableSitesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetFilteredPredefinedMetadata\GetFilteredPredefinedMetadataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetPredefinedMetadataList\GetPredefinedMetadataListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetPredefinedPropertiesList\GetPredefinedPropertiesListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetSystemSettings\GetSystemSettingsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetWebsiteSettingsList\GetWebsiteSettingsListHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveAppearanceSettings\SaveAppearanceSettingsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSettingsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSystemSettings\SaveSystemSettingsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\ThumbnailAdapterCheck\ThumbnailAdapterCheckHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedMetadata\UpdatePredefinedMetadataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedProperty\UpdatePredefinedPropertyHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateWebsiteSetting\UpdateWebsiteSettingHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UploadCustomLogo\UploadCustomLogoHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Logger; @@ -63,7 +64,6 @@ use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -81,7 +81,7 @@ public function displayCustomLogoAction(Request $request, DisplayCustomLogoHandl return new StreamedResponse(static function () use ($result): void { fpassthru($result->stream); }, 200, [ - 'Content-Type' => $result->mime, + 'Content-Type' => $result->mime, 'Content-Security-Policy' => "script-src 'none'", ]); } @@ -125,9 +125,9 @@ public function metadataAction( if ($payload->hasData) { return match ($xaction) { 'destroy' => $this->destroyPredefinedMetadata($deletePredefinedMetadata, $payload), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedMetadata($payload)->data])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $createPredefinedMetadata($payload)->data])), - default => throw new BadRequestHttpException(), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedMetadata($payload)->data])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $createPredefinedMetadata($payload)->data])), + default => throw new BadRequestHttpException(), }; } @@ -165,10 +165,10 @@ public function propertiesAction( ): JsonResponse { if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->destroyPredefinedProperty($deletePredefinedProperty, $payload), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedProperty($payload)->data])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $createPredefinedProperty($payload)->data])), - default => throw new BadRequestHttpException(), + 'destroy' => $this->destroyPredefinedProperty($payload, $deletePredefinedProperty), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedProperty($payload)->data])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $createPredefinedProperty($payload)->data])), + default => throw new BadRequestHttpException(), }; } @@ -177,8 +177,11 @@ public function propertiesAction( return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - private function destroyPredefinedProperty(DeletePredefinedPropertyHandler $handler, PredefinedPropertyPayload $payload): JsonResponse - { + private function destroyPredefinedProperty( + PredefinedPropertyPayload $payload, + DeletePredefinedPropertyHandler $handler, + ): JsonResponse { + $handler($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); @@ -206,14 +209,10 @@ public function getSystemAction(GetSystemSettingsHandler $handler): JsonResponse #[IsGranted(AdminPermission::SystemAppearance->value)] #[Route('/set-appearance', name: 'opendxp_admin_settings_appearance_set', methods: ['PUT'])] public function setAppearanceSystemAction( - Request $request, - KernelInterface $kernel, + SaveSettingsPayload $payload, SaveAppearanceSettingsHandler $handler, ): JsonResponse { - $handler( - values: $this->decodeJson($request->request->get('data')), - env: $request->request->get('env', $kernel->getEnvironment()), - ); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -221,14 +220,10 @@ public function setAppearanceSystemAction( #[IsGranted(CorePermission::SystemSettings->value)] #[Route('/set-system', name: 'opendxp_admin_settings_setsystem', methods: ['PUT'])] public function setSystemAction( - Request $request, - KernelInterface $kernel, + SaveSettingsPayload $payload, SaveSystemSettingsHandler $handler, ): JsonResponse { - $handler( - values: $this->decodeJson($request->request->get('data')), - env: $request->request->get('env', $kernel->getEnvironment()), - ); + $handler($payload); return $this->adminJson(ApiResponse::ok()); } @@ -236,25 +231,14 @@ public function setSystemAction( #[IsGranted(new Expression('is_granted("clear_cache") or is_granted("system_settings")'))] #[Route('/clear-cache', name: 'opendxp_admin_settings_clearcache', methods: ['DELETE'])] public function clearCacheAction( - Request $request, - KernelInterface $kernel, - ClearOpenDxpCacheHandler $clearOpenDxpCache, - ClearSymfonyCacheHandler $clearSymfonyCache, + ClearCachePayload $payload, + ClearCacheHandler $clearCache, ): JsonResponse { - $shouldClearOpenDxp = !(bool) $request->request->get('only_symfony_cache'); - $shouldClearSymfony = !(bool) $request->request->get('only_opendxp_cache'); - - if ($shouldClearOpenDxp) { - $clearOpenDxpCache(); - } - - if ($shouldClearSymfony) { - $clearSymfonyCache($request->request->get('env', $kernel->getEnvironment())); - } + $clearCache($payload); $response = new JsonResponse(ApiResponse::ok()); - if ($shouldClearSymfony) { + if (!$payload->onlyOpendxpCache) { // send response before exit so the client gets a reply before the process terminates $response->sendHeaders(); $response->sendContent(); @@ -331,9 +315,9 @@ public function websiteSettingsAction( if ($payload->hasData) { return match ($xaction) { 'destroy' => $this->destroyWebsiteSetting($deleteWebsiteSetting, $payload), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $updateWebsiteSetting($payload)->data])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $createWebsiteSetting($payload)->data])), - default => throw new BadRequestHttpException(), + 'update' => $this->adminJson(ApiResponse::ok(['data' => $updateWebsiteSetting($payload)->data])), + 'create' => $this->adminJson(ApiResponse::ok(['data' => $createWebsiteSetting($payload)->data])), + default => throw new BadRequestHttpException(), }; } diff --git a/src/Controller/Admin/TagsController.php b/src/Controller/Admin/TagsController.php index 857c31d6..4a080c1d 100644 --- a/src/Controller/Admin/TagsController.php +++ b/src/Controller/Admin/TagsController.php @@ -19,21 +19,28 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTag\AddTagHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTag\AddTagPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTagToElement\AddTagToElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTagToElement\AddTagToElementPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\DeleteTag\DeleteTagHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\DeleteTag\DeleteTagPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\DoBatchAssignment\DoBatchAssignmentHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\DoBatchAssignment\DoBatchAssignmentPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\GetBatchAssignmentJobs\GetBatchAssignmentJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\GetBatchAssignmentJobs\GetBatchAssignmentJobsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\GetTagTreeChildren\GetTagTreeChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\GetTagTreeChildren\GetTagTreeChildrenPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\LoadTagsForElement\GetTagsForElement\GetTagsForElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\LoadTagsForElement\LoadTagsForElementPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\RemoveTagFromElement\RemoveTagFromElementHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\RemoveTagFromElement\RemoveTagFromElementPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\UpdateTag\UpdateTagHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\UpdateTag\UpdateTagPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTagHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTagToElementHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\DeleteTagHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\DoBatchAssignmentHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\GetBatchAssignmentJobsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\GetTagsForElementHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\GetTagTreeChildrenHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\RemoveTagFromElementHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Tags\UpdateTagHandler; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; /** * @internal @@ -43,124 +50,97 @@ class TagsController extends AdminAbstractController { #[IsGranted(CorePermission::TagsConfiguration->value)] #[Route('/add', name: 'opendxp_admin_tags_add', methods: ['POST'])] - public function addAction(Request $request, AddTagHandler $addTag): JsonResponse - { - $result = $addTag( - name: strip_tags($request->request->get('text', '')), - parentId: (int)$request->request->get('parentId'), - ); + public function addAction( + AddTagPayload $payload, + AddTagHandler $addTag, + ): JsonResponse { + $result = $addTag($payload); return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } #[IsGranted(CorePermission::TagsConfiguration->value)] #[Route('/delete', name: 'opendxp_admin_tags_delete', methods: ['DELETE'])] - public function deleteAction(Request $request, DeleteTagHandler $deleteTag): JsonResponse - { - $deleteTag(id: (int)$request->request->get('id')); + public function deleteAction( + DeleteTagPayload $payload, + DeleteTagHandler $deleteTag, + ): JsonResponse { + $deleteTag($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::TagsConfiguration->value)] #[Route('/update', name: 'opendxp_admin_tags_update', methods: ['PUT'])] - public function updateAction(Request $request, UpdateTagHandler $updateTag): JsonResponse - { - $parentId = $request->request->get('parentId'); - $updateTag( - id: (int)$request->request->get('id'), - parentId: ($parentId || $parentId === '0') ? (int)$parentId : null, - name: $request->request->has('text') ? strip_tags($request->request->get('text', '')) : null, - ); + public function updateAction( + UpdateTagPayload $payload, + UpdateTagHandler $updateTag, + ): JsonResponse { + $updateTag($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/tree-get-children-by-id', name: 'opendxp_admin_tags_treegetchildrenbyid', methods: ['GET'])] public function treeGetChildrenByIdAction( + GetTagTreeChildrenPayload $payload, GetTagTreeChildrenHandler $getTagTreeChildren, - #[MapQueryParameter] ?string $showSelection = null, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $assignmentCId = null, - #[MapQueryParameter] string $assignmentCType = '', - #[MapQueryParameter] ?string $node = null, - #[MapQueryParameter] ?string $filter = null, ): JsonResponse { - $result = $getTagTreeChildren( - showSelection: $showSelection === 'true', - assignmentCId: $assignmentCId, - assignmentCType: strip_tags($assignmentCType), - node: $node, - filter: $filter, - ); + $result = $getTagTreeChildren($payload); return $this->adminJson($result->tags); } #[Route('/load-tags-for-element', name: 'opendxp_admin_tags_loadtagsforelement', methods: ['GET'])] public function loadTagsForElementAction( + LoadTagsForElementPayload $payload, GetTagsForElementHandler $getTagsForElement, - #[MapQueryParameter(flags: \FILTER_NULL_ON_FAILURE)] ?int $assignmentCId = null, - #[MapQueryParameter] string $assignmentCType = '', ): JsonResponse { - if (!$assignmentCId || !$assignmentCType) { + if (!$payload->assignmentCId || !$payload->assignmentCType) { return $this->adminJson([]); } - $result = $getTagsForElement( - assignmentId: $assignmentCId, - assignmentType: strip_tags($assignmentCType), - ); + $result = $getTagsForElement($payload); return $this->adminJson($result->tags); } #[Route('/add-tag-to-element', name: 'opendxp_admin_tags_addtagtoelement', methods: ['PUT'])] - public function addTagToElementAction(Request $request, AddTagToElementHandler $addTagToElement): JsonResponse - { - $result = $addTagToElement( - tagId: (int)$request->request->get('tagId'), - elementType: strip_tags($request->request->get('assignmentElementType', '')), - elementId: (int)$request->request->get('assignmentElementId'), - ); + public function addTagToElementAction( + AddTagToElementPayload $payload, + AddTagToElementHandler $addTagToElement, + ): JsonResponse { + $result = $addTagToElement($payload); return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } #[Route('/remove-tag-from-element', name: 'opendxp_admin_tags_removetagfromelement', methods: ['DELETE'])] - public function removeTagFromElementAction(Request $request, RemoveTagFromElementHandler $removeTagFromElement): JsonResponse - { - $result = $removeTagFromElement( - tagId: (int)$request->request->get('tagId'), - elementType: strip_tags($request->request->get('assignmentElementType', '')), - elementId: (int)$request->request->get('assignmentElementId'), - ); + public function removeTagFromElementAction( + RemoveTagFromElementPayload $payload, + RemoveTagFromElementHandler $removeTagFromElement, + ): JsonResponse { + $result = $removeTagFromElement($payload); return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } #[Route('/get-batch-assignment-jobs', name: 'opendxp_admin_tags_getbatchassignmentjobs', methods: ['GET'])] public function getBatchAssignmentJobsAction( + GetBatchAssignmentJobsPayload $payload, GetBatchAssignmentJobsHandler $getBatchAssignmentJobs, - #[MapQueryParameter] int $elementId = 0, - #[MapQueryParameter] string $elementType = '', ): JsonResponse { - $result = $getBatchAssignmentJobs( - elementType: strip_tags($elementType), - elementId: $elementId, - ); + $result = $getBatchAssignmentJobs($payload); return $this->adminJson(ApiResponse::ok(['idLists' => $result->idListParts, 'totalCount' => $result->totalCount])); } #[Route('/do-batch-assignment', name: 'opendxp_admin_tags_dobatchassignment', methods: ['PUT'])] - public function doBatchAssignmentAction(Request $request, DoBatchAssignmentHandler $doBatchAssignment): JsonResponse - { - $doBatchAssignment( - elementType: strip_tags($request->request->get('elementType', '')), - elementIds: json_decode($request->request->get('childrenIds'), true) ?? [], - assignedTags: json_decode($request->request->get('assignedTags'), true) ?? [], - doCleanupTags: $request->request->get('removeAndApply') === 'true', - ); + public function doBatchAssignmentAction( + DoBatchAssignmentPayload $payload, + DoBatchAssignmentHandler $doBatchAssignment, + ): JsonResponse { + $doBatchAssignment($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/TranslationController.php b/src/Controller/Admin/TranslationController.php index e1f91fa6..988565f3 100644 --- a/src/Controller/Admin/TranslationController.php +++ b/src/Controller/Admin/TranslationController.php @@ -19,20 +19,28 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\AddAdminTranslationKeysHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\BuildContentExportJobsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\CleanupTranslationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\CreateTranslationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\DeleteTranslationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\ExportTranslationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslationDomainsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\GetWebsiteTranslationLanguagesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\ImportTranslationsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\MergeTranslationItemsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\AddAdminTranslationKeys\AddAdminTranslationKeysHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\AddAdminTranslationKeys\AddAdminTranslationKeysPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\BuildContentExportJobs\BuildContentExportJobsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\BuildContentExportJobs\BuildContentExportJobsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\CleanupTranslations\CleanupTranslationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\CleanupTranslations\CleanupTranslationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\CreateTranslation\CreateTranslationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\DeleteTranslation\DeleteTranslationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\ExportTranslations\ExportTranslationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\ExportTranslations\ExportTranslationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslationDomains\GetTranslationDomainsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslations\GetTranslationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\GetWebsiteTranslationLanguages\GetWebsiteTranslationLanguagesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\ImportTranslations\ImportTranslationsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\ImportTranslations\ImportTranslationsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\MergeTranslationItems\MergeTranslationItemsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\MergeTranslationItems\MergeTranslationItemsPayload; use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\UpdateTranslationHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Translation\UploadTranslationImportFileHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\UpdateTranslation\UpdateTranslationHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\UploadTranslationImportFile\UploadTranslationImportFileHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\UploadTranslationImportFile\UploadTranslationImportFilePayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Model\Translation; use OpenDxp\Tool\Session; use Symfony\Component\HttpFoundation\JsonResponse; @@ -51,39 +59,15 @@ class TranslationController extends AdminAbstractController { #[Route('/import', name: 'opendxp_admin_translation_import', methods: ['POST'])] public function importAction( - Request $request, ImportTranslationsHandler $importTranslations, - #[MapQueryParameter] ?string $merge = null, + ImportTranslationsPayload $payload, ): JsonResponse { - $domain = $request->request->get('domain', Translation::DOMAIN_DEFAULT); - $admin = $domain === Translation::DOMAIN_ADMIN; - - $dialect = $request->request->get('csvSettings'); - $session = Session::getSessionBag($request->getSession(), 'opendxp_importconfig'); - $tmpFile = $session->get('translation_import_file'); - - if ($dialect) { - $dialect = json_decode($dialect); - } - - $this->checkPermission(($admin ? 'admin_' : '') . 'translations'); - - $overwrite = !$merge; - - $flagUrlTemplate = $this->generateUrl('opendxp_admin_misc_getlanguageflag', ['language' => '{language}']); - $flagUrlTemplate = str_replace('%7Blanguage%7D', '{language}', $flagUrlTemplate); + $this->checkPermission(($payload->domain === Translation::DOMAIN_ADMIN ? 'admin_' : '') . 'translations'); - $result = $importTranslations( - tmpFile: $tmpFile, - domain: $domain, - overwrite: $overwrite, - dialect: $dialect, - enrichDelta: (bool) $merge, - flagUrlTemplate: $flagUrlTemplate, - ); + $result = $importTranslations($payload); $extra = []; - if ($merge) { + if ($payload->enrichDelta) { $extra['delta'] = base64_encode(json_encode($result->delta)); } @@ -99,10 +83,9 @@ public function importAction( public function uploadImportFileAction( Request $request, UploadTranslationImportFileHandler $uploadTranslationImportFile, + UploadTranslationImportFilePayload $payload, ): JsonResponse { - $file = $request->files->get('Filedata'); - - $result = $uploadTranslationImportFile($file); + $result = $uploadTranslationImportFile($payload); Session::useBag($request->getSession(), static function (AttributeBagInterface $session) use ($result): void { $session->set('translation_import_file', $result->importFile); @@ -116,20 +99,11 @@ public function uploadImportFileAction( #[Route('/export', name: 'opendxp_admin_translation_export', methods: ['GET'])] public function exportAction( ExportTranslationsHandler $exportTranslations, - #[MapQueryParameter] ?string $domain = null, - #[MapQueryParameter] ?string $filter = null, - #[MapQueryParameter] ?string $searchString = null, + ExportTranslationsPayload $payload, ): Response { - $admin = $domain === Translation::DOMAIN_ADMIN; - - $this->checkPermission(($admin ? 'admin_' : '') . 'translations'); + $this->checkPermission(($payload->domain === Translation::DOMAIN_ADMIN ? 'admin_' : '') . 'translations'); - $result = $exportTranslations( - domain: $domain, - filter: $filter, - searchString: $searchString, - admin: $admin, - ); + $result = $exportTranslations($payload); $response = new Response("\xEF\xBB\xBF" . $result->csv); $response->headers->set('Content-Encoding', 'UTF-8'); @@ -142,15 +116,10 @@ public function exportAction( #[Route('/add-admin-translation-keys', name: 'opendxp_admin_translation_addadmintranslationkeys', methods: ['POST'])] public function addAdminTranslationKeysAction( - Request $request, AddAdminTranslationKeysHandler $addAdminTranslationKeys, + AddAdminTranslationKeysPayload $payload, ): JsonResponse { - $keys = $request->request->get('keys'); - - if ($keys) { - $data = $this->decodeJson($keys); - $addAdminTranslationKeys($data); - } + $addAdminTranslationKeys($payload); return $this->adminJson(ApiResponse::ok()); } @@ -225,12 +194,10 @@ protected function prefixTranslations(array $translations): array #[Route('/cleanup', name: 'opendxp_admin_translation_cleanup', methods: ['DELETE'])] public function cleanupAction( - Request $request, CleanupTranslationsHandler $cleanupTranslations, + CleanupTranslationsPayload $payload, ): JsonResponse { - $domain = $request->request->get('domain', Translation::DOMAIN_DEFAULT); - - $cleanupTranslations($domain); + $cleanupTranslations($payload); return $this->adminJson(ApiResponse::ok()); } @@ -243,45 +210,30 @@ public function cleanupAction( */ #[Route('/content-export-jobs', name: 'opendxp_admin_translation_contentexportjobs', methods: ['POST'])] public function contentExportJobsAction( - Request $request, BuildContentExportJobsHandler $buildContentExportJobs, + BuildContentExportJobsPayload $payload, ): JsonResponse { - $data = $this->decodeJson($request->request->get('data')); - $source = str_replace('_', '-', $request->request->get('source', '')); - $target = str_replace('_', '-', $request->request->get('target', '')); - $type = $request->request->get('type'); - $jobUrl = $request->request->get('job_url', $request->getBaseUrl() . '/admin/translation/' . $type . '-export'); - - $elementsPerJob = max(1, (int) $request->request->get('elements_per_job', 10)); - - $result = $buildContentExportJobs( - data: $data && is_array($data) ? $data : [], - source: $source, - target: $target, - jobUrl: $jobUrl, - elementsPerJob: $elementsPerJob, - ); + $result = $buildContentExportJobs($payload); return $this->adminJson(ApiResponse::ok(['jobs' => $result->jobs, 'id' => $result->exportId])); } #[Route('/merge-item', name: 'opendxp_admin_translation_mergeitem', methods: ['PUT'])] public function mergeItemAction( - Request $request, MergeTranslationItemsHandler $mergeTranslationItems, + MergeTranslationItemsPayload $payload, ): JsonResponse { - $domain = $request->request->get('domain', Translation::DOMAIN_DEFAULT); - $dataList = json_decode($request->request->get('data'), true); - - $mergeTranslationItems($dataList, $domain); + $mergeTranslationItems($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/get-website-translation-languages', name: 'opendxp_admin_translation_getwebsitetranslationlanguages', methods: ['GET'])] - public function getWebsiteTranslationLanguagesAction(GetWebsiteTranslationLanguagesHandler $getWebsiteTranslationLanguages): JsonResponse - { - $result = $getWebsiteTranslationLanguages(); + public function getWebsiteTranslationLanguagesAction( + GetWebsiteTranslationLanguagesHandler $getWebsiteTranslationLanguages, + EmptyPayload $payload, + ): JsonResponse { + $result = $getWebsiteTranslationLanguages($payload); return $this->adminJson([ 'view' => $result->view, @@ -292,9 +244,11 @@ public function getWebsiteTranslationLanguagesAction(GetWebsiteTranslationLangua } #[Route('/get-translation-domains', name: 'opendxp_admin_translation_gettranslationdomains', methods: ['GET'])] - public function getTranslationDomainsAction(GetTranslationDomainsHandler $getTranslationDomains): JsonResponse - { - $result = $getTranslationDomains(); + public function getTranslationDomainsAction( + GetTranslationDomainsHandler $getTranslationDomains, + EmptyPayload $payload, + ): JsonResponse { + $result = $getTranslationDomains($payload); return $this->adminJson(['domains' => $result->domains]); } diff --git a/src/Controller/Admin/User/RoleController.php b/src/Controller/Admin/User/RoleController.php index 9ffda71a..9c7f3276 100644 --- a/src/Controller/Admin/User/RoleController.php +++ b/src/Controller/Admin/User/RoleController.php @@ -19,12 +19,14 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoleHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetRolesHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoleTreeChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetRole\GetRoleHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoles\GetRolesHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoles\GetRolesPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoleTreeChildren\GetRoleTreeChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoleTreeChildren\GetRoleTreeChildrenPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -36,19 +38,19 @@ class RoleController extends AdminAbstractController #[IsGranted(CorePermission::Users->value)] #[Route('/user/role-tree-get-children-by-id', name: 'opendxp_admin_user_roletreegetchildrenbyid', methods: ['GET'])] public function roleTreeGetChildrenByIdAction( + GetRoleTreeChildrenPayload $payload, GetRoleTreeChildrenHandler $getRoleTreeChildren, - #[MapQueryParameter] int $node, ): JsonResponse { - return $this->adminJson($getRoleTreeChildren($node)); + return $this->adminJson($getRoleTreeChildren($payload)); } #[IsGranted(CorePermission::Users->value)] #[Route('/user/role-get', name: 'opendxp_admin_user_roleget', methods: ['GET'])] public function roleGetAction( + IdQueryPayload $payload, GetRoleHandler $getRole, - #[MapQueryParameter] int $id, ): JsonResponse { - $result = $getRole($id); + $result = $getRole($payload); return $this->adminJson(ApiResponse::ok([ 'role' => $result->role, @@ -64,10 +66,10 @@ public function roleGetAction( #[IsGranted(CorePermission::Users->value)] #[Route('/user/get-roles', name: 'opendxp_admin_user_getroles', methods: ['GET'])] public function getRolesAction( + GetRolesPayload $payload, GetRolesHandler $getRoles, - #[MapQueryParameter] ?string $permission = null, ): JsonResponse { - $roles = $getRoles($permission); + $roles = $getRoles($payload); return $this->adminJson(ApiResponse::ok(['total' => count($roles), 'data' => $roles])); } @@ -75,10 +77,10 @@ public function getRolesAction( #[IsGranted(CorePermission::ShareConfigurations->value)] #[Route('/user/get-roles-for-sharing', name: 'opendxp_admin_user_getrolesforsharing', methods: ['GET'])] public function getRolesForSharingAction( + GetRolesPayload $payload, GetRolesHandler $getRoles, - #[MapQueryParameter] ?string $permission = null, ): JsonResponse { - $roles = $getRoles($permission); + $roles = $getRoles($payload); return $this->adminJson(ApiResponse::ok(['total' => count($roles), 'data' => $roles])); } diff --git a/src/Controller/Admin/User/UserProfileController.php b/src/Controller/Admin/User/UserProfileController.php index 8c818817..c13559c2 100644 --- a/src/Controller/Admin/User/UserProfileController.php +++ b/src/Controller/Admin/User/UserProfileController.php @@ -19,17 +19,18 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetCurrentUserHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\ResetMy2FaSecretHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\UpdateCurrentUserHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\UploadUserImageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetCurrentUser\GetCurrentUserHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetCurrentUser\GetCurrentUserPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\ResetMy2FaSecret\ResetMy2FaSecretHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\UpdateCurrentUser\UpdateCurrentUserHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\UpdateCurrentUser\UpdateCurrentUserPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\UploadUserImage\UploadUserImageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\UploadUserImage\UploadUserImagePayload; use OpenDxp\Bundle\AdminBundle\Helper\User as UserHelper; -use Symfony\Component\HttpFoundation\File\UploadedFile; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; /** @@ -39,14 +40,10 @@ class UserProfileController extends AdminAbstractController { #[Route('/user/upload-current-user-image', name: 'opendxp_admin_user_uploadcurrentuserimage', methods: ['POST'])] public function uploadCurrentUserImageAction( - Request $request, + UploadUserImagePayload $payload, UploadUserImageHandler $uploadUserImage, - #[MapQueryParameter] int $id, ): JsonResponse { - /** @var UploadedFile $avatarFile */ - $avatarFile = $request->files->get('Filedata'); - - $uploadUserImage($id, $avatarFile); + $uploadUserImage($payload); $response = $this->adminJson(ApiResponse::ok()); $response->headers->set('Content-Type', 'text/html'); @@ -55,36 +52,26 @@ public function uploadCurrentUserImageAction( } #[Route('/user/update-current-user', name: 'opendxp_admin_user_updatecurrentuser', methods: ['PUT'])] - public function updateCurrentUserAction(Request $request, UpdateCurrentUserHandler $updateCurrentUser): JsonResponse - { + public function updateCurrentUserAction( + Request $request, + UpdateCurrentUserPayload $payload, + UpdateCurrentUserHandler $updateCurrentUser, + ): JsonResponse { if (!$request->request->has('id')) { return $this->adminJson(false); } - $isPasswordReset = \OpenDxp\Tool\Session::useBag($request->getSession(), static fn (AttributeBagInterface $adminSession) => (bool) $adminSession->get('password_reset')); - - $values = $this->decodeJson($request->request->get('data'), true); - - $keyBindingsJson = $request->request->has('keyBindings') - ? $request->request->get('keyBindings') - : null; - - $updateCurrentUser( - requestedUserId: (int) $request->request->get('id'), - values: $values, - isPasswordReset: $isPasswordReset, - keyBindingsJson: $keyBindingsJson, - ); + $updateCurrentUser($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/user/get-current-user', name: 'opendxp_admin_user_getcurrentuser', methods: ['GET'])] - public function getCurrentUserAction(Request $request, GetCurrentUserHandler $getCurrentUser): Response - { - $isPasswordReset = (bool) \OpenDxp\Tool\Session::useBag($request->getSession(), fn (AttributeBagInterface $adminSession) => $adminSession->get('password_reset')); - - $result = $getCurrentUser($isPasswordReset); + public function getCurrentUserAction( + GetCurrentUserPayload $payload, + GetCurrentUserHandler $getCurrentUser, + ): Response { + $result = $getCurrentUser($payload); $response = new Response('opendxp.currentuser = ' . $this->encodeJson($result->userData)); $response->headers->set('Content-Type', 'text/javascript'); @@ -93,9 +80,12 @@ public function getCurrentUserAction(Request $request, GetCurrentUserHandler $ge } #[Route('/user/reset-my-2fa-secret', name: 'opendxp_admin_user_reset_my_2fa_secret', methods: ['PUT'])] - public function resetMy2FaSecretAction(ResetMy2FaSecretHandler $resetMy2FaSecret): JsonResponse - { - $resetMy2FaSecret(); + public function resetMy2FaSecretAction( + EmptyPayload $payload, + ResetMy2FaSecretHandler $resetMy2FaSecret, + ): JsonResponse { + + $resetMy2FaSecret($payload); return $this->adminJson(ApiResponse::ok()); } diff --git a/src/Controller/Admin/UserController.php b/src/Controller/Admin/UserController.php index 1997452e..963ff5a5 100644 --- a/src/Controller/Admin/UserController.php +++ b/src/Controller/Admin/UserController.php @@ -19,28 +19,39 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\User\AddUserHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUserHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUserImageHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\Disable2FaHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetMinimalUserHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetTokenLoginLinkHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetUserHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetUserImageHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetUserTreeChildrenHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\GetUsersHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\Reset2FaSecretHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\SearchUsersHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\SendInvitationLinkHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\UpdateUserHandler; -use OpenDxp\Bundle\AdminBundle\Handler\User\UploadUserImageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\AddUser\AddUserHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\AddUser\AddUserPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUser\DeleteUserHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUser\DeleteUserPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUserImage\DeleteUserImageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUserImage\DeleteUserImagePayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\Disable2Fa\Disable2FaHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\Disable2Fa\Disable2FaPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetMinimalUser\GetMinimalUserHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetMinimalUser\GetMinimalUserPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetTokenLoginLink\GetTokenLoginLinkHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetTokenLoginLink\GetTokenLoginLinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetUser\GetUserHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetUser\GetUserPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetUserImage\GetUserImageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetUserImage\GetUserImagePayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetUserTreeChildren\GetUserTreeChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetUserTreeChildren\GetUserTreeChildrenPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetUsers\GetUsersHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetUsers\GetUsersPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\Reset2FaSecret\Reset2FaSecretHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\Reset2FaSecret\Reset2FaSecretPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\SearchUsers\SearchUsersHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\SearchUsers\SearchUsersPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\SendInvitationLink\SendInvitationLinkHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\SendInvitationLink\SendInvitationLinkPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\UpdateUser\UpdateUserHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\UpdateUser\UpdateUserPayload; +use OpenDxp\Bundle\AdminBundle\Handler\User\UploadUserImage\UploadUserImageHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\UploadUserImage\UploadUserImagePayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; -use OpenDxp\Http\Request\Host\GeneralHostResolver; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -53,54 +64,40 @@ class UserController extends AdminAbstractController #[Route('/user/tree-get-children-by-id', name: 'opendxp_admin_user_treegetchildrenbyid', methods: ['GET'])] public function treeGetChildrenByIdAction( GetUserTreeChildrenHandler $getUserTreeChildren, - #[MapQueryParameter] int $node = 0, + GetUserTreeChildrenPayload $payload, ): JsonResponse { - return $this->adminJson($getUserTreeChildren($node)); + return $this->adminJson($getUserTreeChildren($payload)); } #[IsGranted(CorePermission::Users->value)] #[Route('/user/add', name: 'opendxp_admin_user_add', methods: ['POST'])] - public function addAction(Request $request, AddUserHandler $addUser): JsonResponse - { - $referenceId = $request->request->has('rid') ? (int) $request->request->get('rid') : null; - - $result = $addUser( - type: $request->request->get('type'), - parentId: $request->request->getInt('parentId'), - name: trim($request->request->get('name', '')), - active: $request->request->getBoolean('active'), - referenceId: $referenceId, - ); + public function addAction( + AddUserHandler $addUser, + AddUserPayload $payload, + ): JsonResponse { + $result = $addUser($payload); return $this->adminJson(ApiResponse::ok(['id' => $result->id])); } #[IsGranted(CorePermission::Users->value)] #[Route('/user/delete', name: 'opendxp_admin_user_delete', methods: ['DELETE'])] - public function deleteAction(Request $request, DeleteUserHandler $deleteUser): JsonResponse - { - $deleteUser($request->request->getInt('id')); + public function deleteAction( + DeleteUserHandler $deleteUser, + DeleteUserPayload $payload, + ): JsonResponse { + $deleteUser($payload); return $this->adminJson(ApiResponse::ok()); } #[IsGranted(CorePermission::Users->value)] #[Route('/user/update', name: 'opendxp_admin_user_update', methods: ['PUT'])] - public function updateAction(Request $request, UpdateUserHandler $updateUser): JsonResponse - { - $values = $request->request->has('data') - ? $this->decodeJson($request->request->get('data'), true) - : null; - - $workspaces = $request->request->has('workspaces') - ? $this->decodeJson($request->request->get('workspaces'), true) - : null; - - $keyBindingsJson = $request->request->has('keyBindings') - ? $request->request->get('keyBindings') - : null; - - $updateUser($request->request->getInt('id'), $values, $workspaces, $keyBindingsJson); + public function updateAction( + UpdateUserHandler $updateUser, + UpdateUserPayload $payload, + ): JsonResponse { + $updateUser($payload); return $this->adminJson(ApiResponse::ok()); } @@ -109,9 +106,9 @@ public function updateAction(Request $request, UpdateUserHandler $updateUser): J #[Route('/user/get', name: 'opendxp_admin_user_get', methods: ['GET'])] public function getAction( GetUserHandler $getUser, - #[MapQueryParameter] int $id = 0, + GetUserPayload $payload, ): JsonResponse { - $result = $getUser($id); + $result = $getUser($payload); return $this->adminJson(ApiResponse::ok([ 'user' => $result->userData, @@ -128,9 +125,9 @@ public function getAction( #[Route('/user/get-minimal', name: 'opendxp_admin_user_getminimal', methods: ['GET'])] public function getMinimalAction( GetMinimalUserHandler $getMinimalUser, - #[MapQueryParameter] int $id = 0, + GetMinimalUserPayload $payload, ): JsonResponse { - $result = $getMinimalUser($id); + $result = $getMinimalUser($payload); return $this->adminJson([ 'id' => $result->id, @@ -143,16 +140,10 @@ public function getMinimalAction( #[IsGranted(CorePermission::Users->value)] #[Route('/user/upload-image', name: 'opendxp_admin_user_uploadimage', methods: ['POST'])] public function uploadImageAction( - Request $request, UploadUserImageHandler $uploadUserImage, - #[MapQueryParameter] int $id = 0, + UploadUserImagePayload $payload, ): JsonResponse { - $targetUserId = $request->query->has('id') ? $id : null; - - /** @var UploadedFile $avatarFile */ - $avatarFile = $request->files->get('Filedata'); - - $uploadUserImage($targetUserId, $avatarFile); + $uploadUserImage($payload); // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in // Ext.form.Action.Submit and mark the submission as failed @@ -164,21 +155,21 @@ public function uploadImageAction( #[Route('/user/delete-image', name: 'opendxp_admin_user_deleteimage', methods: ['DELETE'])] public function deleteImageAction( - Request $request, DeleteUserImageHandler $deleteUserImage, - #[MapQueryParameter] int $id = 0, + DeleteUserImagePayload $payload, ): JsonResponse { - $targetUserId = $request->query->has('id') ? $id : null; - $deleteUserImage($targetUserId); + $deleteUserImage($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/user/disable-2fa', name: 'opendxp_admin_user_disable2fasecret', methods: ['DELETE'])] - public function disable2FaSecretAction(Disable2FaHandler $disable2Fa): JsonResponse - { + public function disable2FaSecretAction( + Disable2FaHandler $disable2Fa, + Disable2FaPayload $payload, + ): JsonResponse { try { - $disable2Fa(); + $disable2Fa($payload); } catch (\Throwable $e) { return $this->adminJson(ApiResponse::error($e->getMessage())); } @@ -188,24 +179,24 @@ public function disable2FaSecretAction(Disable2FaHandler $disable2Fa): JsonRespo #[IsGranted(CorePermission::Users->value)] #[Route('/user/reset-2fa-secret', name: 'opendxp_admin_user_reset2fasecret', methods: ['PUT'])] - public function reset2FaSecretAction(Request $request, Reset2FaSecretHandler $reset2FaSecret): JsonResponse - { - $reset2FaSecret($request->request->getInt('id')); + public function reset2FaSecretAction( + Reset2FaSecretHandler $reset2FaSecret, + Reset2FaSecretPayload $payload, + ): JsonResponse { + $reset2FaSecret($payload); return $this->adminJson(ApiResponse::ok()); } #[Route('/user/get-image', name: 'opendxp_admin_user_getimage', methods: ['GET'])] public function getImageAction( - Request $request, - GetUserImageHandler $getUserImage, - #[MapQueryParameter] int $id = 0, + GetUserImageHandler $handler, + GetUserImagePayload $payload, ): StreamedResponse { - $targetUserId = $request->query->has('id') ? $id : null; - $stream = $getUserImage($targetUserId); + $result = $handler($payload); - return new StreamedResponse(function () use ($stream): void { - fpassthru($stream); + return new StreamedResponse(function () use ($result): void { + fpassthru($result->image); }, 200, [ 'Content-Type' => 'image/png', ]); @@ -215,10 +206,10 @@ public function getImageAction( #[Route('/user/get-token-login-link', name: 'opendxp_admin_user_gettokenloginlink', methods: ['GET'])] public function getTokenLoginLinkAction( GetTokenLoginLinkHandler $getTokenLoginLink, - #[MapQueryParameter] int $id = 0, + GetTokenLoginLinkPayload $payload, ): JsonResponse { try { - $result = $getTokenLoginLink($id); + $result = $getTokenLoginLink($payload); } catch (\Throwable $e) { return $this->adminJson(ApiResponse::error($e->getMessage())); } @@ -230,32 +221,27 @@ public function getTokenLoginLinkAction( #[Route('/user/search', name: 'opendxp_admin_user_search', methods: ['GET'])] public function searchAction( SearchUsersHandler $searchUsers, - #[MapQueryParameter] ?string $query = null, + SearchUsersPayload $payload, ): JsonResponse { - return $this->adminJson(ApiResponse::ok(['users' => $searchUsers($query)])); + return $this->adminJson(ApiResponse::ok(['users' => $searchUsers($payload)])); } #[IsGranted(CorePermission::ShareConfigurations->value)] #[Route('/user/get-users-for-sharing', name: 'opendxp_admin_user_getusersforsharing', methods: ['GET'])] public function getUsersForSharingAction( GetUsersHandler $getUsers, - #[MapQueryParameter(name: 'include_current_user')] ?string $includeCurrentUser = null, - #[MapQueryParameter] ?string $permission = null, + GetUsersPayload $payload, ): JsonResponse { - return $this->getUsersAction($getUsers, $includeCurrentUser, $permission); + return $this->getUsersAction($getUsers, $payload); } #[IsGranted(CorePermission::Users->value)] #[Route('/user/get-users', name: 'opendxp_admin_user_getusers', methods: ['GET'])] public function getUsersAction( GetUsersHandler $getUsers, - #[MapQueryParameter(name: 'include_current_user')] ?string $includeCurrentUser = null, - #[MapQueryParameter] ?string $permission = null, + GetUsersPayload $payload, ): JsonResponse { - $users = $getUsers( - includeCurrentUser: (bool) $includeCurrentUser, - permission: $permission, - ); + $users = $getUsers($payload); return $this->adminJson(ApiResponse::ok(['total' => count($users), 'data' => $users])); } @@ -263,13 +249,10 @@ public function getUsersAction( #[IsGranted(CorePermission::Users->value)] #[Route('/user/invitationlink', name: 'opendxp_admin_user_invitationlink', methods: ['POST'])] public function invitationLinkAction( - Request $request, SendInvitationLinkHandler $sendInvitationLink, - GeneralHostResolver $generalHostResolver, + SendInvitationLinkPayload $payload, ): JsonResponse { - $username = (string) $request->request->get('username', ''); - $domain = $generalHostResolver->resolve(['source' => $request]) ?? ''; - $result = $sendInvitationLink($username, $domain); + $result = $sendInvitationLink($payload); return $this->adminJson(ApiResponse::fromBool($result->success, ['message' => $result->message])); } diff --git a/src/Controller/Admin/WorkflowController.php b/src/Controller/Admin/WorkflowController.php index 04dbf003..14c9195d 100644 --- a/src/Controller/Admin/WorkflowController.php +++ b/src/Controller/Admin/WorkflowController.php @@ -20,17 +20,21 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; -use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetModalCustomHtmlHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowDetailsHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowFormHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowSvgHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitGlobalActionHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitWorkflowTransitionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetModalCustomHtml\GetModalCustomHtmlHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetModalCustomHtml\GetModalCustomHtmlPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowDetails\GetWorkflowDetailsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowDetails\GetWorkflowDetailsPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowForm\GetWorkflowFormHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowForm\GetWorkflowFormPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\ShowGraph\GetWorkflowSvg\GetWorkflowSvgHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\ShowGraph\ShowGraphPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitGlobalAction\SubmitGlobalActionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitGlobalAction\SubmitGlobalActionPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitWorkflowTransition\SubmitWorkflowTransitionHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitWorkflowTransition\SubmitWorkflowTransitionPayload; use OpenDxp\Model\Element\ValidationException; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; /** @@ -40,16 +44,13 @@ class WorkflowController extends AdminAbstractController { #[Route('/get-workflow-form', name: 'opendxp_admin_workflow_getworkflowform', methods: ['POST'])] - public function getWorkflowFormAction(Request $request, GetWorkflowFormHandler $getWorkflowForm): JsonResponse + public function getWorkflowFormAction( + GetWorkflowFormPayload $payload, + GetWorkflowFormHandler $getWorkflowForm, + ): JsonResponse { try { - [$ctype, $cid] = $this->resolveCtypeAndCid($request); - $result = $getWorkflowForm( - ctype: $ctype, - cid: $cid, - workflowName: $request->request->getString('workflowName'), - transitionName: $request->request->getString('transitionName'), - ); + $result = $getWorkflowForm($payload); $wfConfig = [ 'message' => $result->message, @@ -66,18 +67,11 @@ public function getWorkflowFormAction(Request $request, GetWorkflowFormHandler $ #[Route('/submit-workflow-transition', name: 'opendxp_admin_workflow_submitworkflowtransition', methods: ['POST'])] public function submitWorkflowTransitionAction( - Request $request, + SubmitWorkflowTransitionPayload $payload, SubmitWorkflowTransitionHandler $submitWorkflowTransition, ): JsonResponse { try { - [$ctype, $cid] = $this->resolveCtypeAndCid($request); - $result = $submitWorkflowTransition( - ctype: $ctype, - cid: $cid, - workflowName: $request->request->getString('workflowName'), - transition: $request->request->getString('transition'), - workflowOptions: $request->request->all('workflow'), - ); + $result = $submitWorkflowTransition($payload); if ($result->blocked) { return $this->adminJson(ApiResponse::error('transition failed', ['reasons' => $result->blockerReasons])); @@ -98,18 +92,11 @@ public function submitWorkflowTransitionAction( #[Route('/submit-global-action', name: 'opendxp_admin_workflow_submitglobal', methods: ['POST'])] public function submitGlobalAction( - Request $request, + SubmitGlobalActionPayload $payload, SubmitGlobalActionHandler $submitGlobalAction, ): JsonResponse { try { - [$ctype, $cid] = $this->resolveCtypeAndCid($request); - $submitGlobalAction( - ctype: $ctype, - cid: $cid, - workflowName: $request->request->getString('workflowName'), - transition: $request->request->getString('transition'), - workflowOptions: $request->request->all('workflow'), - ); + $submitGlobalAction($payload); return $this->adminJson(ApiResponse::ok(['callback' => 'reloadObject'])); } catch (ValidationException $e) { @@ -126,23 +113,20 @@ public function submitGlobalAction( #[Route('/get-workflow-details', name: 'opendxp_admin_workflow_getworkflowdetailsstore')] public function getWorkflowDetailsStore( + GetWorkflowDetailsPayload $payload, GetWorkflowDetailsHandler $getWorkflowDetails, - #[MapQueryParameter] string $ctype, - #[MapQueryParameter] int $cid, ): JsonResponse { - $result = $getWorkflowDetails(ctype: $ctype, cid: $cid); + $result = $getWorkflowDetails($payload); return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => count($result->data)])); } #[Route('/show-graph', name: 'opendxp_admin_workflow_show_graph', methods: ['GET'])] public function showGraph( + ShowGraphPayload $payload, GetWorkflowSvgHandler $getWorkflowSvg, - #[MapQueryParameter] string $ctype, - #[MapQueryParameter] int $cid, - #[MapQueryParameter] ?string $workflowName = null, ): Response { - $svg = $getWorkflowSvg(ctype: $ctype, cid: $cid, workflowName: $workflowName); + $svg = $getWorkflowSvg($payload); $response = new Response($svg); $response->headers->set('Content-Type', 'image/svg+xml'); @@ -151,30 +135,17 @@ public function showGraph( } #[Route('/modal-custom-html', name: 'opendxp_admin_workflow_modal_custom_html', methods: ['POST'])] - public function getModalCustomHtml(Request $request, GetModalCustomHtmlHandler $getModalCustomHtml): JsonResponse + public function getModalCustomHtml( + GetModalCustomHtmlPayload $payload, + GetModalCustomHtmlHandler $getModalCustomHtml, + ): JsonResponse { try { - [$ctype, $cid] = $this->resolveCtypeAndCid($request); - $result = $getModalCustomHtml( - ctype: $ctype, - cid: $cid, - workflowName: $request->request->getString('workflowName'), - transition: $request->request->getString('transition'), - isGlobalAction: $request->request->getString('isGlobalAction') === 'true', - ); + $result = $getModalCustomHtml($payload); return $this->adminJson(ApiResponse::ok(['customHtml' => $result->customHtml])); } catch (Exception $e) { return $this->adminJson(ApiResponse::error($e->getMessage())); } } - - /** @return array{string, int} */ - private function resolveCtypeAndCid(Request $request): array - { - $ctype = $request->request->get('ctype') ?? $request->query->get('ctype'); - $cid = (int) ($request->request->get('cid') ?? $request->query->get('cid')); - - return [$ctype, $cid]; - } } diff --git a/src/Controller/GDPR/AdminController.php b/src/Controller/GDPR/AdminController.php index 1a5d750b..bdaf2839 100644 --- a/src/Controller/GDPR/AdminController.php +++ b/src/Controller/GDPR/AdminController.php @@ -18,7 +18,8 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\GDPR; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\GDPR\DataProvider\Manager; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\GetDataProviders\GetDataProvidersHandler; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; @@ -31,16 +32,8 @@ class AdminController extends AdminAbstractController { #[Route('/get-data-providers', name: 'opendxp_admin_gdpr_admin_getdataproviders', methods: ['GET'])] - public function getDataProvidersAction(Manager $manager): JsonResponse + public function getDataProvidersAction(GetDataProvidersHandler $handler, EmptyPayload $payload): JsonResponse { - $response = []; - foreach ($manager->getServices() as $service) { - $response[] = [ - 'name' => $service->getName(), - 'jsClass' => $service->getJsClassName(), - ]; - } - - return $this->adminJson($response); + return $this->adminJson($handler($payload)->providers); } } diff --git a/src/Controller/GDPR/AssetController.php b/src/Controller/GDPR/AssetController.php index a4b28be1..7bcbfffe 100644 --- a/src/Controller/GDPR/AssetController.php +++ b/src/Controller/GDPR/AssetController.php @@ -16,15 +16,14 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\GDPR; -use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\GDPR\DataProvider\Assets; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\Asset\ExportAsset\ExportAssetHandler; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\Asset\SearchAssets\SearchAssetsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\SearchDataPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; -use OpenDxp\Model\Asset; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -36,40 +35,14 @@ class AssetController extends AdminAbstractController { #[Route('/search-assets', name: 'opendxp_admin_gdpr_asset_searchasset', methods: ['GET'])] - public function searchAssetAction(Request $request, Assets $service): JsonResponse + public function searchAssetAction(SearchAssetsHandler $handler, SearchDataPayload $payload): JsonResponse { - $allParams = $request->query->all(); - - $result = $service->searchData( - (int)$allParams['id'], - strip_tags($allParams['firstname']), - strip_tags($allParams['lastname']), - strip_tags($allParams['email']), - (int)$allParams['start'], - (int)$allParams['limit'], - $allParams['sort'] ?? null - ); - - return $this->adminJson($result); + return $this->adminJson($handler($payload)->data); } - /** - * @throws Exception - */ #[Route('/export', name: 'opendxp_admin_gdpr_asset_exportassets', methods: ['GET'])] - public function exportAssetsAction( - Assets $service, - #[MapQueryParameter] int $id = 0, - ): Response + public function exportAssetsAction(ExportAssetHandler $handler, IdQueryPayload $payload): Response { - $asset = Asset::getById($id); - if (!$asset) { - throw $this->createNotFoundException('Asset not found'); - } - if (!$asset->isAllowed('view')) { - throw $this->createAccessDeniedException('Export denied'); - } - - return $service->doExportData($asset); + return $handler($payload)->response; } } diff --git a/src/Controller/GDPR/DataObjectController.php b/src/Controller/GDPR/DataObjectController.php index aba03054..f94374c8 100644 --- a/src/Controller/GDPR/DataObjectController.php +++ b/src/Controller/GDPR/DataObjectController.php @@ -16,14 +16,13 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\GDPR; -use Exception; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\GDPR\DataProvider\DataObjects; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\DataObject\ExportDataObject\ExportDataObjectHandler; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\DataObject\SearchDataObjects\SearchDataObjectsHandler; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\SearchDataPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; -use OpenDxp\Model\DataObject; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -35,48 +34,20 @@ class DataObjectController extends AdminAbstractController { #[Route('/search-data-objects', name: 'opendxp_admin_gdpr_dataobject_searchdataobjects', methods: ['GET'])] - public function searchDataObjectsAction(Request $request, DataObjects $service): JsonResponse + public function searchDataObjectsAction(SearchDataObjectsHandler $handler, SearchDataPayload $payload): JsonResponse { - $allParams = $request->query->all(); - - $result = $service->searchData( - (int)$allParams['id'], - strip_tags($allParams['firstname']), - strip_tags($allParams['lastname']), - strip_tags($allParams['email']), - (int)$allParams['start'], - (int)$allParams['limit'], - $allParams['sort'] ?? null - ); - - return $this->adminJson($result); + return $this->adminJson($handler($payload)->data); } - /** - * @throws Exception - */ #[Route('/export', name: 'opendxp_admin_gdpr_dataobject_exportdataobject', methods: ['GET'])] - public function exportDataObjectAction( - DataObjects $service, - #[MapQueryParameter] int $id = 0, - ): JsonResponse + public function exportDataObjectAction(ExportDataObjectHandler $handler, IdQueryPayload $payload): JsonResponse { - $object = DataObject::getById($id); - - if (!$object) { - throw $this->createNotFoundException('Object not found'); - } - - if (!$object->isAllowed('view')) { - throw $this->createAccessDeniedException('Export denied'); - } - - $exportResult = $service->doExportData($object); + $result = $handler($payload); - $json = $this->encodeJson($exportResult, [], JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_PRETTY_PRINT); + $json = $this->encodeJson($result->data, [], JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_PRETTY_PRINT); return new JsonResponse($json, 200, [ - 'Content-Disposition' => 'attachment; filename="export-data-object-' . $object->getId() . '.json"', + 'Content-Disposition' => 'attachment; filename="export-data-object-' . $result->objectId . '.json"', ], true); } } diff --git a/src/Controller/GDPR/OpenDxpUsersController.php b/src/Controller/GDPR/OpenDxpUsersController.php index 90748c95..f283e62c 100644 --- a/src/Controller/GDPR/OpenDxpUsersController.php +++ b/src/Controller/GDPR/OpenDxpUsersController.php @@ -17,11 +17,12 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\GDPR; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; -use OpenDxp\Bundle\AdminBundle\GDPR\DataProvider\OpenDxpUsers; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\OpenDxpUsers\ExportUserData\ExportUserDataHandler; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\OpenDxpUsers\SearchUsers\SearchUsersHandler; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\SearchDataPayload; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -35,36 +36,21 @@ class OpenDxpUsersController extends AdminAbstractController { #[Route('/search-users', name: 'opendxp_admin_gdpr_opendxpusers_searchusers', methods: ['GET'])] - public function searchUsersAction(Request $request, OpenDxpUsers $openDxpUsers): JsonResponse + public function searchUsersAction(SearchUsersHandler $handler, SearchDataPayload $payload): JsonResponse { - $allParams = $request->query->all(); - - $result = $openDxpUsers->searchData( - (int)$allParams['id'], - strip_tags($allParams['firstname']), - strip_tags($allParams['lastname']), - strip_tags($allParams['email']), - (int)$allParams['start'], - (int)$allParams['limit'], - $allParams['sort'] ?? null - ); - - return $this->adminJson($result); + return $this->adminJson($handler($payload)->data); } #[Route('/export-user-data', name: 'opendxp_admin_gdpr_opendxpusers_exportuserdata', methods: ['GET'])] - public function exportUserDataAction( - OpenDxpUsers $openDxpUsers, - #[MapQueryParameter] int $id = 0, - ): JsonResponse + public function exportUserDataAction(ExportUserDataHandler $handler, IdQueryPayload $payload): JsonResponse { $this->checkPermission('users'); - $userData = $openDxpUsers->getExportData($id); + $result = $handler($payload); - $json = $this->encodeJson($userData, [], JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_PRETTY_PRINT); + $json = $this->encodeJson($result->data, [], JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_PRETTY_PRINT); return new JsonResponse($json, 200, [ - 'Content-Disposition' => 'attachment; filename="export-userdata-' . $userData['id'] . '.json"', + 'Content-Disposition' => 'attachment; filename="export-userdata-' . $result->data['id'] . '.json"', ], true); } } diff --git a/src/Controller/GDPR/SentMailController.php b/src/Controller/GDPR/SentMailController.php index 4c6cb1c8..3e6e3a0a 100644 --- a/src/Controller/GDPR/SentMailController.php +++ b/src/Controller/GDPR/SentMailController.php @@ -17,10 +17,10 @@ namespace OpenDxp\Bundle\AdminBundle\Controller\GDPR; use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; +use OpenDxp\Bundle\AdminBundle\Handler\GDPR\SentMail\ExportSentMail\ExportSentMailHandler; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\AdminPermission; -use OpenDxp\Model\Tool\Email\Log; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -32,25 +32,15 @@ class SentMailController extends AdminAbstractController { #[Route('/export', name: 'opendxp_admin_gdpr_sentmail_exportdataobject', methods: ['GET'])] - public function exportDataObjectAction( - #[MapQueryParameter] int $id = 0, - ): JsonResponse + public function exportDataObjectAction(ExportSentMailHandler $handler, IdQueryPayload $payload): JsonResponse { $this->checkPermission('emails'); + $result = $handler($payload); - $sentMail = Log::getById($id); - if (!$sentMail) { - throw $this->createNotFoundException(); - } - - $sentMailArray = (array)$sentMail; - $sentMailArray['htmlBody'] = $sentMail->getHtmlLog(); - $sentMailArray['textBody'] = $sentMail->getTextLog(); - - $json = $this->encodeJson($sentMailArray, [], JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_PRETTY_PRINT); + $json = $this->encodeJson($result->data, [], JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_PRETTY_PRINT); return new JsonResponse($json, 200, [ - 'Content-Disposition' => 'attachment; filename="export-mail-' . $sentMail->getId() . '.json"', + 'Content-Disposition' => 'attachment; filename="export-mail-' . $result->mailId . '.json"', ], true); } } diff --git a/src/Normalizer/DataObject/CustomLayoutNormalizer.php b/src/Enricher/DataObject/CustomLayoutEnricher.php similarity index 67% rename from src/Normalizer/DataObject/CustomLayoutNormalizer.php rename to src/Enricher/DataObject/CustomLayoutEnricher.php index 83b0a780..5b8d1522 100644 --- a/src/Normalizer/DataObject/CustomLayoutNormalizer.php +++ b/src/Enricher/DataObject/CustomLayoutEnricher.php @@ -15,29 +15,20 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Normalizer\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Enricher\DataObject; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectHandler; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface; use OpenDxp\Model\DataObject\Concrete; -use OpenDxp\Model\Element\ElementInterface; -final class CustomLayoutNormalizer implements ElementResponseNormalizerInterface +final class CustomLayoutEnricher { - public function supports(ElementInterface $element, string $handlerClass): bool - { - return $element instanceof Concrete - && $handlerClass === GetDataObjectHandler::class; - } - - public function normalize(ElementInterface $element, array &$data, array $context = []): void + public function enrich(Concrete $object, array &$data): void { if (!($data['layout'] ?? false)) { return; } $layoutArray = json_decode(json_encode($data['layout']), true); - $classFieldDefinitions = json_decode(json_encode($element->getClass()->getFieldDefinitions()), true); + $classFieldDefinitions = json_decode(json_encode($object->getClass()->getFieldDefinitions()), true); if (is_array($layoutArray)) { $this->injectValuesForCustomLayout($layoutArray, $classFieldDefinitions); diff --git a/src/Normalizer/DataObject/DraftNormalizer.php b/src/Enricher/DataObject/DraftEnricher.php similarity index 53% rename from src/Normalizer/DataObject/DraftNormalizer.php rename to src/Enricher/DataObject/DraftEnricher.php index 91bad9dc..0af4d05e 100644 --- a/src/Normalizer/DataObject/DraftNormalizer.php +++ b/src/Enricher/DataObject/DraftEnricher.php @@ -15,30 +15,20 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Normalizer\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Enricher\DataObject; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectHandler; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface; -use OpenDxp\Model\DataObject; -use OpenDxp\Model\Element\ElementInterface; +use OpenDxp\Model\DataObject\Concrete; use OpenDxp\Model\Version; -final class DraftNormalizer implements ElementResponseNormalizerInterface +final class DraftEnricher { - public function supports(ElementInterface $element, string $handlerClass): bool + public function enrich(Concrete $object, array &$data, ?Version $draftVersion): void { - return $element instanceof DataObject\Concrete - && $handlerClass === GetDataObjectHandler::class; - } - - public function normalize(ElementInterface $element, array &$data, array $context = []): void - { - $draftVersion = $context['draftVersion'] ?? null; if (!$draftVersion instanceof Version) { return; } - $fresh = DataObject\Concrete::getById($element->getId(), ['force' => true]); + $fresh = Concrete::getById($object->getId(), ['force' => true]); if ($fresh->getModificationDate() < $draftVersion->getDate()) { $data['draft'] = [ 'id' => $draftVersion->getId(), diff --git a/src/Enricher/Document/DocumentMetaEnricher.php b/src/Enricher/Document/DocumentMetaEnricher.php new file mode 100644 index 00000000..791707b2 --- /dev/null +++ b/src/Enricher/Document/DocumentMetaEnricher.php @@ -0,0 +1,36 @@ +getId(), ['force' => true]); + $data['versionDate'] = $fresh->getModificationDate(); + $data['userPermissions'] = $document->getUserPermissions(); + $data['idPath'] = ElementService::getIdPath($document); + $data['php'] = [ + 'classes' => [$document::class, ...array_values(class_parents($document))], + 'interfaces' => array_values(class_implements($document)), + ]; + } +} diff --git a/src/Enricher/Document/DraftEnricher.php b/src/Enricher/Document/DraftEnricher.php new file mode 100644 index 00000000..45a1ae4c --- /dev/null +++ b/src/Enricher/Document/DraftEnricher.php @@ -0,0 +1,40 @@ +getId(), ['force' => true]); + if ($fresh->getModificationDate() < $draftVersion->getDate()) { + $data['draft'] = [ + 'id' => $draftVersion->getId(), + 'modificationDate' => $draftVersion->getDate(), + 'isAutoSave' => $draftVersion->isAutoSave(), + ]; + } + } +} diff --git a/src/Handler/Element/DeleteDraftHandler.php b/src/Enricher/Document/PropertiesEnricher.php similarity index 60% rename from src/Handler/Element/DeleteDraftHandler.php rename to src/Enricher/Document/PropertiesEnricher.php index f3b4e151..b01d5399 100644 --- a/src/Handler/Element/DeleteDraftHandler.php +++ b/src/Enricher/Document/PropertiesEnricher.php @@ -15,18 +15,15 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Enricher\Document; -use OpenDxp\Model\Version; +use OpenDxp\Model\Document; +use OpenDxp\Model\Element\Service as ElementService; -final class DeleteDraftHandler +final class PropertiesEnricher { - public function __invoke(int $id): void + public function enrich(Document $document, array &$data): void { - $version = Version::getById($id); - - if ($version) { - $version->delete(); - } + $data['properties'] = ElementService::minimizePropertiesForEditmode($document->getProperties()); } } diff --git a/src/Enricher/Document/TranslationEnricher.php b/src/Enricher/Document/TranslationEnricher.php new file mode 100644 index 00000000..e01bbd4c --- /dev/null +++ b/src/Enricher/Document/TranslationEnricher.php @@ -0,0 +1,34 @@ +getTranslations($document); + $unlinkTranslations = $service->getTranslations($document, 'unlink'); + $language = $document->getProperty('language'); + unset($translations[$language], $unlinkTranslations[$language]); + $data['translations'] = $translations; + $data['unlinkTranslations'] = $unlinkTranslations; + } +} diff --git a/src/Enricher/Element/AdminStyleEnricher.php b/src/Enricher/Element/AdminStyleEnricher.php new file mode 100644 index 00000000..4df29305 --- /dev/null +++ b/src/Enricher/Element/AdminStyleEnricher.php @@ -0,0 +1,66 @@ +dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); + $this->applyStyle($event->getAdminStyle(), $data); + } + + public function forTree(ElementInterface $element, array &$data): void + { + $event = new ElementAdminStyleEvent($element, new AdminStyle($element), ElementAdminStyleEvent::CONTEXT_TREE); + OpenDxp::getEventDispatcher()->dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); + $this->applyStyle($event->getAdminStyle(), $data); + } + + private function applyStyle(AdminStyle $adminStyle, array &$data): void + { + $iconClass = $adminStyle->getElementIconClass(); + $data['iconCls'] = $iconClass !== false ? $iconClass : null; + + if (!$data['iconCls']) { + $icon = $adminStyle->getElementIcon(); + $data['icon'] = $icon !== false ? $icon : null; + } else { + $data['icon'] = null; + } + + $cssClass = $adminStyle->getElementCssClass(); + if ($cssClass !== false) { + $data['cls'] = ($data['cls'] ?? '') . $cssClass . ' '; + } + + $data['qtipCfg'] = $adminStyle->getElementQtipConfig(); + + $text = $adminStyle->getElementText(); + if ($text !== null) { + $data['text'] = $text; + } + } +} diff --git a/src/Normalizer/Element/AbstractUserNamesNormalizer.php b/src/Enricher/Element/UserNamesEnricher.php similarity index 54% rename from src/Normalizer/Element/AbstractUserNamesNormalizer.php rename to src/Enricher/Element/UserNamesEnricher.php index 3a868f65..582ec63b 100644 --- a/src/Normalizer/Element/AbstractUserNamesNormalizer.php +++ b/src/Enricher/Element/UserNamesEnricher.php @@ -15,17 +15,30 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Normalizer\Element; +namespace OpenDxp\Bundle\AdminBundle\Enricher\Element; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizerInterface; +use OpenDxp\Model\Element\ElementInterface; use OpenDxp\Model\User; use Symfony\Contracts\Translation\TranslatorInterface; -abstract class AbstractUserNamesNormalizer implements ElementResponseNormalizerInterface +final class UserNamesEnricher { - public function __construct(protected readonly TranslatorInterface $translator) {} + public function __construct(private readonly TranslatorInterface $translator) {} - protected function resolveUserName(?int $userId): array + public function enrich(ElementInterface $element, array &$data): void + { + $ownerName = $this->resolveUserName($element->getUserOwner()); + $modificationName = $element->getUserOwner() === $element->getUserModification() + ? $ownerName + : $this->resolveUserName($element->getUserModification()); + + $data['userOwnerUsername'] = $ownerName['userName']; + $data['userOwnerFullname'] = $ownerName['fullName']; + $data['userModificationUsername'] = $modificationName['userName']; + $data['userModificationFullname'] = $modificationName['fullName']; + } + + private function resolveUserName(?int $userId): array { $unknown = ['userName' => '', 'fullName' => $this->translator->trans('user_unknown', [], 'admin')]; diff --git a/src/EventListener/Traits/ControllerTypeTrait.php b/src/EventListener/Traits/ControllerTypeTrait.php deleted file mode 100644 index caf1843b..00000000 --- a/src/EventListener/Traits/ControllerTypeTrait.php +++ /dev/null @@ -1,54 +0,0 @@ -getController(); - - if (!is_array($callable) || count($callable) === 0) { - return null; - } - - $controller = $callable[0]; - if ($controller instanceof $type) { - return $controller; - } - - return null; - } - - /** - * Test if event controller is of the given type - */ - protected function isControllerType(ControllerEvent $event, string $type): bool - { - $controller = $this->getControllerType($event, $type); - - return $controller && $controller instanceof $type; - } -} diff --git a/src/Handler/Asset/ClearAssetThumbnailHandler.php b/src/Handler/Asset/ClearAssetThumbnail/ClearAssetThumbnailHandler.php similarity index 94% rename from src/Handler/Asset/ClearAssetThumbnailHandler.php rename to src/Handler/Asset/ClearAssetThumbnail/ClearAssetThumbnailHandler.php index 9d8a5dbc..cadeee02 100644 --- a/src/Handler/Asset/ClearAssetThumbnailHandler.php +++ b/src/Handler/Asset/ClearAssetThumbnail/ClearAssetThumbnailHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnail; use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnail\ClearAssetThumbnailPayload; use OpenDxp\Model\Asset; diff --git a/src/Handler/Asset/Copy/CopyAssetHandler.php b/src/Handler/Asset/Copy/CopyAsset/CopyAssetHandler.php similarity index 97% rename from src/Handler/Asset/Copy/CopyAssetHandler.php rename to src/Handler/Asset/Copy/CopyAsset/CopyAssetHandler.php index 9919ca42..6f9d30a5 100644 --- a/src/Handler/Asset/Copy/CopyAssetHandler.php +++ b/src/Handler/Asset/Copy/CopyAsset/CopyAssetHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAsset; use OpenDxp\Bundle\AdminBundle\Factory\ElementServiceFactory; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAsset\CopyAssetPayload; diff --git a/src/Handler/Asset/Copy/CopyAsset/CopyAssetPayload.php b/src/Handler/Asset/Copy/CopyAsset/CopyAssetPayload.php index 2391aa2c..5125a34e 100644 --- a/src/Handler/Asset/Copy/CopyAsset/CopyAssetPayload.php +++ b/src/Handler/Asset/Copy/CopyAsset/CopyAssetPayload.php @@ -17,7 +17,11 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAsset; -final readonly class CopyAssetPayload +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; +use OpenDxp\Tool; +use Symfony\Component\HttpFoundation\Request; + +final readonly class CopyAssetPayload implements ExtJsPayloadInterface { public function __construct( public readonly int $sourceId, @@ -26,5 +30,24 @@ public function __construct( public readonly ?int $sourceParentId, public readonly ?int $targetParentId, public readonly ?int $sessionParentId, + public readonly string $transactionId, + public readonly string $saveParentId, ) {} + + public static function fromRequest(Request $request): static + { + $session = Tool\Session::getSessionBag($request->getSession(), 'opendxp_copy'); + $sessionBag = $session->get($request->request->getString('transactionId')); + + return new static( + sourceId: (int) $request->request->getString('sourceId'), + targetId: (int) $request->request->getString('targetId'), + type: $request->request->getString('type'), + sourceParentId: $request->request->has('targetParentId') ? (int) $request->request->getString('sourceParentId') : null, + targetParentId: $request->request->has('targetParentId') ? (int) $request->request->getString('targetParentId') : null, + sessionParentId: isset($sessionBag['parentId']) ? (int) $sessionBag['parentId'] : null, + transactionId: $request->request->getString('transactionId'), + saveParentId: $request->request->getString('saveParentId'), + ); + } } diff --git a/src/Handler/Asset/Copy/CopyAssetResult.php b/src/Handler/Asset/Copy/CopyAsset/CopyAssetResult.php similarity index 90% rename from src/Handler/Asset/Copy/CopyAssetResult.php rename to src/Handler/Asset/Copy/CopyAsset/CopyAssetResult.php index 7023ab69..7aa12a76 100644 --- a/src/Handler/Asset/Copy/CopyAssetResult.php +++ b/src/Handler/Asset/Copy/CopyAsset/CopyAssetResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy\CopyAsset; use OpenDxp\Model\Asset; diff --git a/src/Handler/Asset/Copy/CopyInfo/CopyInfoHandler.php b/src/Handler/Asset/Copy/CopyInfo/CopyInfoHandler.php new file mode 100644 index 00000000..c7ce2225 --- /dev/null +++ b/src/Handler/Asset/Copy/CopyInfo/CopyInfoHandler.php @@ -0,0 +1,83 @@ +type === 'recursive') { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_asset_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $payload->sourceId, + 'targetId' => $payload->targetId, + 'type' => 'child', + 'transactionId' => $transactionId, + 'saveParentId' => true, + ], + ]]; + + $asset = Asset::getById($payload->sourceId) ?? throw new AssetNotFoundException($payload->sourceId); + + if ($asset->hasChildren()) { + $list = new Asset\Listing(); + $list->setCondition('`path` LIKE ?', [$list->escapeLike($asset->getRealFullPath()) . '/%']); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('ASC'); + + foreach ($list->loadIdList() as $id) { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_asset_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $id, + 'targetParentId' => $payload->targetId, + 'sourceParentId' => $payload->sourceId, + 'type' => 'child', + 'transactionId' => $transactionId, + ], + ]]; + } + } + } elseif ($payload->type === 'child' || $payload->type === 'replace') { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_asset_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $payload->sourceId, + 'targetId' => $payload->targetId, + 'type' => $payload->type, + 'transactionId' => $transactionId, + ], + ]]; + } + + return new CopyInfoResult($transactionId, $pasteJobs); + } +} diff --git a/src/Handler/Asset/Copy/CopyInfo/CopyInfoPayload.php b/src/Handler/Asset/Copy/CopyInfo/CopyInfoPayload.php new file mode 100644 index 00000000..42ff0b3f --- /dev/null +++ b/src/Handler/Asset/Copy/CopyInfo/CopyInfoPayload.php @@ -0,0 +1,39 @@ +query->getString('type') ?: null, + sourceId: $request->query->getInt('sourceId'), + targetId: $request->query->getString('targetId') ?: null, + ); + } +} diff --git a/src/Handler/Asset/Copy/CopyInfo/CopyInfoResult.php b/src/Handler/Asset/Copy/CopyInfo/CopyInfoResult.php new file mode 100644 index 00000000..f6a6e2c1 --- /dev/null +++ b/src/Handler/Asset/Copy/CopyInfo/CopyInfoResult.php @@ -0,0 +1,26 @@ +sourceId; - $asset = Asset::getById($sourceId) ?? throw new AssetNotFoundException($sourceId); - - if (!$asset->hasChildren()) { - return new ChildIdsResult([]); - } - - $list = new Asset\Listing(); - $list->setCondition('`path` LIKE ?', [$list->escapeLike($asset->getRealFullPath()) . '/%']); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('ASC'); - - return new ChildIdsResult($list->loadIdList()); - } -} diff --git a/src/Handler/Asset/CreateAssetFolderHandler.php b/src/Handler/Asset/CreateAssetFolder/CreateAssetFolderHandler.php similarity index 96% rename from src/Handler/Asset/CreateAssetFolderHandler.php rename to src/Handler/Asset/CreateAssetFolder/CreateAssetFolderHandler.php index c33131c6..00b58d7b 100644 --- a/src/Handler/Asset/CreateAssetFolderHandler.php +++ b/src/Handler/Asset/CreateAssetFolder/CreateAssetFolderHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolder; use OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolder\CreateAssetFolderPayload; use OpenDxp\Model\Asset; diff --git a/src/Handler/Asset/DeleteAssetHandler.php b/src/Handler/Asset/DeleteAsset/DeleteAssetHandler.php similarity index 97% rename from src/Handler/Asset/DeleteAssetHandler.php rename to src/Handler/Asset/DeleteAsset/DeleteAssetHandler.php index a205c807..a0902b19 100644 --- a/src/Handler/Asset/DeleteAssetHandler.php +++ b/src/Handler/Asset/DeleteAsset/DeleteAssetHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAsset; use OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAsset\DeleteAssetPayload; use OpenDxp\Db\Helper; diff --git a/src/Handler/Asset/DeleteAssetResult.php b/src/Handler/Asset/DeleteAsset/DeleteAssetResult.php similarity index 90% rename from src/Handler/Asset/DeleteAssetResult.php rename to src/Handler/Asset/DeleteAsset/DeleteAssetResult.php index a0279000..e5c9bd98 100644 --- a/src/Handler/Asset/DeleteAssetResult.php +++ b/src/Handler/Asset/DeleteAsset/DeleteAssetResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\DeleteAsset; final readonly class DeleteAssetResult { diff --git a/src/Handler/Asset/Download/AddFilesToZipHandler.php b/src/Handler/Asset/Download/AddFilesToZip/AddFilesToZipHandler.php similarity index 98% rename from src/Handler/Asset/Download/AddFilesToZipHandler.php rename to src/Handler/Asset/Download/AddFilesToZip/AddFilesToZipHandler.php index 013a0cb1..309f7714 100644 --- a/src/Handler/Asset/Download/AddFilesToZipHandler.php +++ b/src/Handler/Asset/Download/AddFilesToZip/AddFilesToZipHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\AddFilesToZip; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\AddFilesToZip\AddFilesToZipPayload; diff --git a/src/Handler/Asset/Download/DownloadAssetHandler.php b/src/Handler/Asset/Download/DownloadAsset/DownloadAssetHandler.php similarity index 94% rename from src/Handler/Asset/Download/DownloadAssetHandler.php rename to src/Handler/Asset/Download/DownloadAsset/DownloadAssetHandler.php index 55e7a2b4..36b61596 100644 --- a/src/Handler/Asset/Download/DownloadAssetHandler.php +++ b/src/Handler/Asset/Download/DownloadAsset/DownloadAssetHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadAsset; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\AssetResult; diff --git a/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php b/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailHandler.php similarity index 98% rename from src/Handler/Asset/Download/DownloadImageThumbnailHandler.php rename to src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailHandler.php index 9b736bd4..ba984227 100644 --- a/src/Handler/Asset/Download/DownloadImageThumbnailHandler.php +++ b/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnail; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnail\DownloadImageThumbnailPayload; diff --git a/src/Handler/Asset/Download/DownloadImageThumbnailResult.php b/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailResult.php similarity index 90% rename from src/Handler/Asset/Download/DownloadImageThumbnailResult.php rename to src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailResult.php index 758368a1..21712489 100644 --- a/src/Handler/Asset/Download/DownloadImageThumbnailResult.php +++ b/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadImageThumbnail; use OpenDxp\Model\Asset\Image; diff --git a/src/Handler/Asset/Download/DownloadZipHandler.php b/src/Handler/Asset/Download/DownloadZip/DownloadZipHandler.php similarity index 94% rename from src/Handler/Asset/Download/DownloadZipHandler.php rename to src/Handler/Asset/Download/DownloadZip/DownloadZipHandler.php index 0fbb608f..fe46a18b 100644 --- a/src/Handler/Asset/Download/DownloadZipHandler.php +++ b/src/Handler/Asset/Download/DownloadZip/DownloadZipHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZip; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZip\DownloadZipPayload; diff --git a/src/Handler/Asset/Download/DownloadZipResult.php b/src/Handler/Asset/Download/DownloadZip/DownloadZipResult.php similarity index 90% rename from src/Handler/Asset/Download/DownloadZipResult.php rename to src/Handler/Asset/Download/DownloadZip/DownloadZipResult.php index 5ddabf0b..4dc962ec 100644 --- a/src/Handler/Asset/Download/DownloadZipResult.php +++ b/src/Handler/Asset/Download/DownloadZip/DownloadZipResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\DownloadZip; final readonly class DownloadZipResult { diff --git a/src/Handler/Asset/Download/GetDownloadZipJobsHandler.php b/src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsHandler.php similarity index 97% rename from src/Handler/Asset/Download/GetDownloadZipJobsHandler.php rename to src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsHandler.php index 348651d5..e2be63fe 100644 --- a/src/Handler/Asset/Download/GetDownloadZipJobsHandler.php +++ b/src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobs; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobs\GetDownloadZipJobsPayload; diff --git a/src/Handler/Asset/Download/GetDownloadZipJobsResult.php b/src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsResult.php similarity index 89% rename from src/Handler/Asset/Download/GetDownloadZipJobsResult.php rename to src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsResult.php index b028983d..3741a6ba 100644 --- a/src/Handler/Asset/Download/GetDownloadZipJobsResult.php +++ b/src/Handler/Asset/Download/GetDownloadZipJobs/GetDownloadZipJobsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Download\GetDownloadZipJobs; final readonly class GetDownloadZipJobsResult { diff --git a/src/Handler/Asset/Editor/LoadAssetForEditorHandler.php b/src/Handler/Asset/Editor/LoadAssetForEditor/LoadAssetForEditorHandler.php similarity index 87% rename from src/Handler/Asset/Editor/LoadAssetForEditorHandler.php rename to src/Handler/Asset/Editor/LoadAssetForEditor/LoadAssetForEditorHandler.php index b9dbde53..cd9931c1 100644 --- a/src/Handler/Asset/Editor/LoadAssetForEditorHandler.php +++ b/src/Handler/Asset/Editor/LoadAssetForEditor/LoadAssetForEditorHandler.php @@ -15,11 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\LoadAssetForEditor; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\AssetResult; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\LoadAssetForEditor\LoadAssetForEditorPayload; use OpenDxp\Model\Asset; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; diff --git a/src/Handler/Asset/Editor/SaveImageEditorHandler.php b/src/Handler/Asset/Editor/SaveImageEditor/SaveImageEditorHandler.php similarity index 95% rename from src/Handler/Asset/Editor/SaveImageEditorHandler.php rename to src/Handler/Asset/Editor/SaveImageEditor/SaveImageEditorHandler.php index c741d2cc..d02e508d 100644 --- a/src/Handler/Asset/Editor/SaveImageEditorHandler.php +++ b/src/Handler/Asset/Editor/SaveImageEditor/SaveImageEditorHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Editor\SaveImageEditor; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\AssetResult; diff --git a/src/Handler/Asset/GetAssetChildrenHandler.php b/src/Handler/Asset/GetAssetChildren/GetAssetChildrenHandler.php similarity index 98% rename from src/Handler/Asset/GetAssetChildrenHandler.php rename to src/Handler/Asset/GetAssetChildren/GetAssetChildrenHandler.php index 96ab22d2..75e0da50 100644 --- a/src/Handler/Asset/GetAssetChildrenHandler.php +++ b/src/Handler/Asset/GetAssetChildren/GetAssetChildrenHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildren; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; diff --git a/src/Handler/Asset/GetAssetChildren/GetAssetChildrenPayload.php b/src/Handler/Asset/GetAssetChildren/GetAssetChildrenPayload.php index b1a640ba..0eedbc4b 100644 --- a/src/Handler/Asset/GetAssetChildren/GetAssetChildrenPayload.php +++ b/src/Handler/Asset/GetAssetChildren/GetAssetChildrenPayload.php @@ -28,6 +28,8 @@ public function __construct( public readonly ?string $filter = null, public readonly int $limit = 100000000, public readonly int $offset = 0, + public readonly bool $hasLimit = false, + public readonly int $inSearch = 0, ) {} public static function fromRequest(Request $request): static @@ -46,6 +48,8 @@ public static function fromRequest(Request $request): static filter: $filter, limit: $limit, offset: $request->query->getInt('start'), + hasLimit: $request->query->has('limit'), + inSearch: $request->query->getInt('inSearch'), ); } } diff --git a/src/Handler/Asset/GetAssetChildrenResult.php b/src/Handler/Asset/GetAssetChildren/GetAssetChildrenResult.php similarity index 91% rename from src/Handler/Asset/GetAssetChildrenResult.php rename to src/Handler/Asset/GetAssetChildren/GetAssetChildrenResult.php index 67380708..11fce3e9 100644 --- a/src/Handler/Asset/GetAssetChildrenResult.php +++ b/src/Handler/Asset/GetAssetChildren/GetAssetChildrenResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildren; final readonly class GetAssetChildrenResult { diff --git a/src/Handler/Asset/GetAssetDataHandler.php b/src/Handler/Asset/GetAssetData/GetAssetDataHandler.php similarity index 82% rename from src/Handler/Asset/GetAssetDataHandler.php rename to src/Handler/Asset/GetAssetData/GetAssetDataHandler.php index 56505945..a44fd035 100644 --- a/src/Handler/Asset/GetAssetDataHandler.php +++ b/src/Handler/Asset/GetAssetData/GetAssetDataHandler.php @@ -15,19 +15,18 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetData; use Exception; use OpenDxp; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetData\GetAssetDataPayload; -use OpenDxp\Bundle\AdminBundle\Event\ElementAdminStyleEvent; use OpenDxp\Bundle\AdminBundle\Exception\Asset\AssetNotFoundException; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; use OpenDxp\Model\Asset; use OpenDxp\Model\Element; -use OpenDxp\Model\Element\AdminStyle; use OpenDxp\Model\Metadata; use OpenDxp\Model\Schedule\Task; use Symfony\Component\EventDispatcher\GenericEvent; @@ -44,7 +43,8 @@ public function __construct( private readonly AdminUserContextInterface $userContext, private readonly EventDispatcherInterface $eventDispatcher, private readonly UrlGeneratorInterface $urlGenerator, - private readonly ElementResponseNormalizer $elementResponseNormalizer, + private readonly AdminStyleEnricher $adminStyleEnricher, + private readonly UserNamesEnricher $userNamesEnricher, private readonly EditLockService $editLockService, ) {} @@ -160,9 +160,8 @@ public function __invoke(GetAssetDataPayload $payload): GetAssetDataResult $asset->getScheduledTasks() ); - $this->elementResponseNormalizer->normalize($asset, $data, static::class); - - $this->applyAdminStyle($asset, ElementAdminStyleEvent::CONTEXT_EDITOR, $data); + $this->userNamesEnricher->enrich($asset, $data); + $this->adminStyleEnricher->forEditor($asset, $data); $data['php'] = [ 'classes' => [$asset::class, ...array_values(class_parents($asset))], @@ -203,31 +202,5 @@ private function getDocumentPreviewPdf(Asset\Document $asset): mixed return $stream; } - private function applyAdminStyle(Asset $asset, int $context, array &$data): void - { - $event = new ElementAdminStyleEvent($asset, new AdminStyle($asset), $context); - OpenDxp::getEventDispatcher()->dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); - $adminStyle = $event->getAdminStyle(); - - $data['iconCls'] = $adminStyle->getElementIconClass() !== false ? $adminStyle->getElementIconClass() : null; - if (!$data['iconCls']) { - $data['icon'] = $adminStyle->getElementIcon() !== false ? $adminStyle->getElementIcon() : null; - } else { - $data['icon'] = null; - } - if ($adminStyle->getElementCssClass() !== false) { - if (!isset($data['cls'])) { - $data['cls'] = ''; - } - $data['cls'] .= $adminStyle->getElementCssClass() . ' '; - } - $data['qtipCfg'] = $adminStyle->getElementQtipConfig(); - - $elementText = $adminStyle->getElementText(); - if ($elementText !== null) { - $data['text'] = $elementText; - } - } - } diff --git a/src/Handler/Asset/GetAssetDataResult.php b/src/Handler/Asset/GetAssetData/GetAssetDataResult.php similarity index 90% rename from src/Handler/Asset/GetAssetDataResult.php rename to src/Handler/Asset/GetAssetData/GetAssetDataResult.php index ee8aab36..7b44ea6d 100644 --- a/src/Handler/Asset/GetAssetDataResult.php +++ b/src/Handler/Asset/GetAssetData/GetAssetDataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetData; final readonly class GetAssetDataResult { diff --git a/src/Handler/Asset/GridProxy/GridProxyHandler.php b/src/Handler/Asset/GridProxy/GridProxyHandler.php new file mode 100644 index 00000000..38ecbfa4 --- /dev/null +++ b/src/Handler/Asset/GridProxy/GridProxyHandler.php @@ -0,0 +1,44 @@ +params; + + $filterPrepareEvent = new GenericEvent(null, ['requestParams' => $params]); + $this->eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE); + $params = $filterPrepareEvent->getArgument('requestParams'); + + return new GridProxyResult( + $this->assetGridService->gridProxy($params, $payload->language) + ); + } +} diff --git a/src/Handler/Asset/GridProxy/GridProxyPayload.php b/src/Handler/Asset/GridProxy/GridProxyPayload.php new file mode 100644 index 00000000..f1569dc4 --- /dev/null +++ b/src/Handler/Asset/GridProxy/GridProxyPayload.php @@ -0,0 +1,39 @@ +query->has('language') ? $request->query->getString('language') : null; + + return new static( + params: [...$request->request->all(), ...$request->query->all()], + language: $rawLanguage !== 'default' ? $rawLanguage : null, + ); + } +} diff --git a/src/Handler/Element/GetNicePathResult.php b/src/Handler/Asset/GridProxy/GridProxyResult.php similarity index 85% rename from src/Handler/Element/GetNicePathResult.php rename to src/Handler/Asset/GridProxy/GridProxyResult.php index ac7c8750..b691b67b 100644 --- a/src/Handler/Element/GetNicePathResult.php +++ b/src/Handler/Asset/GridProxy/GridProxyResult.php @@ -1,5 +1,7 @@ request->getInt('gridConfigId'), + gridConfigId: $request->request->getInt('gridConfigId'), + noSystemColumns: (bool) $request->query->get('no_system_columns'), ); } } diff --git a/src/Handler/Asset/Helper/DoAssetExportHandler.php b/src/Handler/Asset/Helper/DoAssetExport/DoAssetExportHandler.php similarity index 98% rename from src/Handler/Asset/Helper/DoAssetExportHandler.php rename to src/Handler/Asset/Helper/DoAssetExport/DoAssetExportHandler.php index 3f2c6984..5ed95cd9 100644 --- a/src/Handler/Asset/Helper/DoAssetExportHandler.php +++ b/src/Handler/Asset/Helper/DoAssetExport/DoAssetExportHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExport; use League\Flysystem\UnableToReadFile; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\DoAssetExport\DoAssetExportPayload; diff --git a/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php b/src/Handler/Asset/Helper/ExecuteAssetBatch/ExecuteAssetBatchHandler.php similarity index 94% rename from src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php rename to src/Handler/Asset/Helper/ExecuteAssetBatch/ExecuteAssetBatchHandler.php index aa24b5d6..bfc01e6d 100644 --- a/src/Handler/Asset/Helper/ExecuteAssetBatchHandler.php +++ b/src/Handler/Asset/Helper/ExecuteAssetBatch/ExecuteAssetBatchHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatch; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\ExecuteAssetBatch\ExecuteAssetBatchPayload; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService; diff --git a/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php b/src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsHandler.php similarity index 94% rename from src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php rename to src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsHandler.php index 0bd1cf2c..ea9a8d4d 100644 --- a/src/Handler/Asset/Helper/GetAssetBatchJobsHandler.php +++ b/src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobs; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobs\GetAssetBatchJobsPayload; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService; diff --git a/src/Handler/Asset/Helper/GetAssetBatchJobsResult.php b/src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsResult.php similarity index 89% rename from src/Handler/Asset/Helper/GetAssetBatchJobsResult.php rename to src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsResult.php index 9c51a001..8cfa7594 100644 --- a/src/Handler/Asset/Helper/GetAssetBatchJobsResult.php +++ b/src/Handler/Asset/Helper/GetAssetBatchJobs/GetAssetBatchJobsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetBatchJobs; final readonly class GetAssetBatchJobsResult { diff --git a/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigHandler.php b/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfig/GetAssetMetadataForColumnConfigHandler.php similarity index 97% rename from src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigHandler.php rename to src/Handler/Asset/Helper/GetAssetMetadataForColumnConfig/GetAssetMetadataForColumnConfigHandler.php index 634e3ed9..eabc50bd 100644 --- a/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigHandler.php +++ b/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfig/GetAssetMetadataForColumnConfigHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetMetadataForColumnConfig; use OpenDxp\Model\Asset; use OpenDxp\Model\Metadata; diff --git a/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigResult.php b/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfig/GetAssetMetadataForColumnConfigResult.php similarity index 87% rename from src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigResult.php rename to src/Handler/Asset/Helper/GetAssetMetadataForColumnConfig/GetAssetMetadataForColumnConfigResult.php index 2ca93512..f842c3ff 100644 --- a/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfigResult.php +++ b/src/Handler/Asset/Helper/GetAssetMetadataForColumnConfig/GetAssetMetadataForColumnConfigResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetAssetMetadataForColumnConfig; final readonly class GetAssetMetadataForColumnConfigResult { diff --git a/src/Handler/Asset/Helper/GetExportJobsHandler.php b/src/Handler/Asset/Helper/GetExportJobs/GetExportJobsHandler.php similarity index 96% rename from src/Handler/Asset/Helper/GetExportJobsHandler.php rename to src/Handler/Asset/Helper/GetExportJobs/GetExportJobsHandler.php index 389e7adc..49799096 100644 --- a/src/Handler/Asset/Helper/GetExportJobsHandler.php +++ b/src/Handler/Asset/Helper/GetExportJobs/GetExportJobsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobs; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobs\GetExportJobsPayload; use OpenDxp\Bundle\AdminBundle\Helper\GridHelperService; diff --git a/src/Handler/Asset/Helper/GetExportJobsResult.php b/src/Handler/Asset/Helper/GetExportJobs/GetExportJobsResult.php similarity index 90% rename from src/Handler/Asset/Helper/GetExportJobsResult.php rename to src/Handler/Asset/Helper/GetExportJobs/GetExportJobsResult.php index e55abd49..be241529 100644 --- a/src/Handler/Asset/Helper/GetExportJobsResult.php +++ b/src/Handler/Asset/Helper/GetExportJobs/GetExportJobsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\GetExportJobs; final readonly class GetExportJobsResult { diff --git a/src/Handler/Asset/Helper/MarkGridConfigFavouriteHandler.php b/src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouriteHandler.php similarity index 96% rename from src/Handler/Asset/Helper/MarkGridConfigFavouriteHandler.php rename to src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouriteHandler.php index 9e1ac5e8..28aa0764 100644 --- a/src/Handler/Asset/Helper/MarkGridConfigFavouriteHandler.php +++ b/src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouriteHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavourite; use Exception; use OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavourite\MarkGridConfigFavouritePayload; diff --git a/src/Handler/Asset/Helper/MarkGridConfigFavouriteResult.php b/src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouriteResult.php similarity index 88% rename from src/Handler/Asset/Helper/MarkGridConfigFavouriteResult.php rename to src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouriteResult.php index 8673eab6..10a0b9ee 100644 --- a/src/Handler/Asset/Helper/MarkGridConfigFavouriteResult.php +++ b/src/Handler/Asset/Helper/MarkGridConfigFavourite/MarkGridConfigFavouriteResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Helper\MarkGridConfigFavourite; final readonly class MarkGridConfigFavouriteResult { diff --git a/src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsHandler.php b/src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsHandler.php new file mode 100644 index 00000000..40801e14 --- /dev/null +++ b/src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsHandler.php @@ -0,0 +1,46 @@ +columns as $item) { + if (!empty($item->isOperator)) { + $itemKey = '#' . uniqid('', false); + + $item->key = $itemKey; + $newData[] = $item; + $helperColumns[$itemKey] = $item; + } else { + $newData[] = $item; + } + } + + return new PrepareHelperColumnConfigsResult( + newData: $newData, + helperColumns: $helperColumns, + ); + } +} diff --git a/src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsPayload.php b/src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsPayload.php new file mode 100644 index 00000000..4da54de6 --- /dev/null +++ b/src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsPayload.php @@ -0,0 +1,34 @@ +request->getString('columns')) ?? [], + ); + } +} diff --git a/src/Handler/Login/GenerateTwoFactorSetupResult.php b/src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsResult.php similarity index 67% rename from src/Handler/Login/GenerateTwoFactorSetupResult.php rename to src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsResult.php index 0d1ccb0f..ed8cdcee 100644 --- a/src/Handler/Login/GenerateTwoFactorSetupResult.php +++ b/src/Handler/Asset/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsResult.php @@ -1,5 +1,7 @@ query->getInt('id'), + versionId: $request->query->getInt('id'), + userTimezone: $request->query->getString('userTimezone') ?: null, ); } } diff --git a/src/Handler/Asset/Version/ShowVersionResult.php b/src/Handler/Asset/Version/ShowVersion/ShowVersionResult.php similarity index 91% rename from src/Handler/Asset/Version/ShowVersionResult.php rename to src/Handler/Asset/Version/ShowVersion/ShowVersionResult.php index cbee6baa..6e87cc63 100644 --- a/src/Handler/Asset/Version/ShowVersionResult.php +++ b/src/Handler/Asset/Version/ShowVersion/ShowVersionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Version; +namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Version\ShowVersion; use OpenDxp\Model\Asset; use OpenDxp\Model\Version; diff --git a/src/Handler/DataObject/AddObjectHandler.php b/src/Handler/DataObject/AddObject/AddObjectHandler.php similarity index 96% rename from src/Handler/DataObject/AddObjectHandler.php rename to src/Handler/DataObject/AddObject/AddObjectHandler.php index d354bc90..d61da5b3 100644 --- a/src/Handler/DataObject/AddObjectHandler.php +++ b/src/Handler/DataObject/AddObject/AddObjectHandler.php @@ -15,9 +15,8 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObject; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\AddObjectPayload; use OpenDxp\Model; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; diff --git a/src/Payload/DataObject/AddObjectPayload.php b/src/Handler/DataObject/AddObject/AddObjectPayload.php similarity index 95% rename from src/Payload/DataObject/AddObjectPayload.php rename to src/Handler/DataObject/AddObject/AddObjectPayload.php index 227c083a..6ae61e2f 100644 --- a/src/Payload/DataObject/AddObjectPayload.php +++ b/src/Handler/DataObject/AddObject/AddObjectPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObject; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/AddObjectResult.php b/src/Handler/DataObject/AddObject/AddObjectResult.php similarity index 90% rename from src/Handler/DataObject/AddObjectResult.php rename to src/Handler/DataObject/AddObject/AddObjectResult.php index d2c8f286..d038bd9a 100644 --- a/src/Handler/DataObject/AddObjectResult.php +++ b/src/Handler/DataObject/AddObject/AddObjectResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObject; final readonly class AddObjectResult { diff --git a/src/Handler/DataObject/AddObjectFolderHandler.php b/src/Handler/DataObject/AddObjectFolder/AddObjectFolderHandler.php similarity index 93% rename from src/Handler/DataObject/AddObjectFolderHandler.php rename to src/Handler/DataObject/AddObjectFolder/AddObjectFolderHandler.php index 881b375e..02e4c4ab 100644 --- a/src/Handler/DataObject/AddObjectFolderHandler.php +++ b/src/Handler/DataObject/AddObjectFolder/AddObjectFolderHandler.php @@ -15,13 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObjectFolder; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\AddObjectFolderPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class AddObjectFolderHandler diff --git a/src/Payload/DataObject/AddObjectFolderPayload.php b/src/Handler/DataObject/AddObjectFolder/AddObjectFolderPayload.php similarity index 93% rename from src/Payload/DataObject/AddObjectFolderPayload.php rename to src/Handler/DataObject/AddObjectFolder/AddObjectFolderPayload.php index 5864cc4b..0a90f056 100644 --- a/src/Payload/DataObject/AddObjectFolderPayload.php +++ b/src/Handler/DataObject/AddObjectFolder/AddObjectFolderPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\AddObjectFolder; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ChangeChildrenSortByHandler.php b/src/Handler/DataObject/ChangeChildrenSortBy/ChangeChildrenSortByHandler.php similarity index 97% rename from src/Handler/DataObject/ChangeChildrenSortByHandler.php rename to src/Handler/DataObject/ChangeChildrenSortBy/ChangeChildrenSortByHandler.php index b9e7dc1d..ed05817e 100644 --- a/src/Handler/DataObject/ChangeChildrenSortByHandler.php +++ b/src/Handler/DataObject/ChangeChildrenSortBy/ChangeChildrenSortByHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ChangeChildrenSortBy; use Exception; use OpenDxp\Db; @@ -24,7 +24,6 @@ use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\ChangeChildrenSortByPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class ChangeChildrenSortByHandler diff --git a/src/Payload/DataObject/ChangeChildrenSortByPayload.php b/src/Handler/DataObject/ChangeChildrenSortBy/ChangeChildrenSortByPayload.php similarity index 93% rename from src/Payload/DataObject/ChangeChildrenSortByPayload.php rename to src/Handler/DataObject/ChangeChildrenSortBy/ChangeChildrenSortByPayload.php index f6175786..a858e003 100644 --- a/src/Payload/DataObject/ChangeChildrenSortByPayload.php +++ b/src/Handler/DataObject/ChangeChildrenSortBy/ChangeChildrenSortByPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ChangeChildrenSortBy; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/AddClassHandler.php b/src/Handler/DataObject/ClassDef/AddClass/AddClassHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/AddClassHandler.php rename to src/Handler/DataObject/ClassDef/AddClass/AddClassHandler.php index b09c65af..fbbcff40 100644 --- a/src/Handler/DataObject/ClassDef/AddClassHandler.php +++ b/src/Handler/DataObject/ClassDef/AddClass/AddClassHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClass; use Exception; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ClassDef/AddClassPayload.php b/src/Handler/DataObject/ClassDef/AddClass/AddClassPayload.php similarity index 98% rename from src/Handler/DataObject/ClassDef/AddClassPayload.php rename to src/Handler/DataObject/ClassDef/AddClass/AddClassPayload.php index f25713b0..a53175e1 100644 --- a/src/Handler/DataObject/ClassDef/AddClassPayload.php +++ b/src/Handler/DataObject/ClassDef/AddClass/AddClassPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClass; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/AddClassResult.php b/src/Handler/DataObject/ClassDef/AddClass/AddClassResult.php similarity index 98% rename from src/Handler/DataObject/ClassDef/AddClassResult.php rename to src/Handler/DataObject/ClassDef/AddClass/AddClassResult.php index c2e02208..ee3247bc 100644 --- a/src/Handler/DataObject/ClassDef/AddClassResult.php +++ b/src/Handler/DataObject/ClassDef/AddClass/AddClassResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\AddClass; final readonly class AddClassResult { diff --git a/src/Handler/DataObject/ClassDef/BulkCommitHandler.php b/src/Handler/DataObject/ClassDef/BulkCommit/BulkCommitHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/BulkCommitHandler.php rename to src/Handler/DataObject/ClassDef/BulkCommit/BulkCommitHandler.php index 96c95355..33946e65 100644 --- a/src/Handler/DataObject/ClassDef/BulkCommitHandler.php +++ b/src/Handler/DataObject/ClassDef/BulkCommit/BulkCommitHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommit; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ClassDef/BulkCommitPayload.php b/src/Handler/DataObject/ClassDef/BulkCommit/BulkCommitPayload.php similarity index 97% rename from src/Handler/DataObject/ClassDef/BulkCommitPayload.php rename to src/Handler/DataObject/ClassDef/BulkCommit/BulkCommitPayload.php index b15462fc..b26a97a1 100644 --- a/src/Handler/DataObject/ClassDef/BulkCommitPayload.php +++ b/src/Handler/DataObject/ClassDef/BulkCommit/BulkCommitPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkCommit; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/BulkExportPreparePayload.php b/src/Handler/DataObject/ClassDef/BulkExportPrepare/BulkExportPreparePayload.php similarity index 96% rename from src/Handler/DataObject/ClassDef/BulkExportPreparePayload.php rename to src/Handler/DataObject/ClassDef/BulkExportPrepare/BulkExportPreparePayload.php index bf5ff87d..482f32ee 100644 --- a/src/Handler/DataObject/ClassDef/BulkExportPreparePayload.php +++ b/src/Handler/DataObject/ClassDef/BulkExportPrepare/BulkExportPreparePayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkExportPrepare; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php b/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php deleted file mode 100644 index 5a900ca8..00000000 --- a/src/Handler/DataObject/ClassDef/BulkExportPrepareHandler.php +++ /dev/null @@ -1,38 +0,0 @@ -requestStack->getCurrentRequest()->getSession(), - static function (AttributeBagInterface $session) use ($payload): void { - $session->set('class_bulk_export_settings', $payload->data); - }, - 'opendxp_objects' - ); - } -} diff --git a/src/Handler/DataObject/ClassDef/BulkImportHandler.php b/src/Handler/DataObject/ClassDef/BulkImport/BulkImportHandler.php similarity index 79% rename from src/Handler/DataObject/ClassDef/BulkImportHandler.php rename to src/Handler/DataObject/ClassDef/BulkImport/BulkImportHandler.php index 9d8da407..c95fa647 100644 --- a/src/Handler/DataObject/ClassDef/BulkImportHandler.php +++ b/src/Handler/DataObject/ClassDef/BulkImport/BulkImportHandler.php @@ -15,29 +15,15 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; - -use OpenDxp\Tool\Session; -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImport; final class BulkImportHandler { - public function __construct(private readonly RequestStack $requestStack) {} - public function __invoke(BulkImportPayload $payload): BulkImportResult { $tmpName = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/bulk-import-' . uniqid('', false) . '.tmp'; file_put_contents($tmpName, $payload->json); - Session::useBag( - $this->requestStack->getCurrentRequest()->getSession(), - static function (AttributeBagInterface $session) use ($tmpName): void { - $session->set('class_bulk_import_file', $tmpName); - }, - 'opendxp_objects' - ); - $parsed = json_decode($payload->json, true); $result = []; @@ -78,6 +64,6 @@ static function (AttributeBagInterface $session) use ($tmpName): void { } } - return new BulkImportResult(items: $result); + return new BulkImportResult(items: $result, tmpFile: $tmpName); } } diff --git a/src/Handler/DataObject/ClassDef/BulkImportPayload.php b/src/Handler/DataObject/ClassDef/BulkImport/BulkImportPayload.php similarity index 98% rename from src/Handler/DataObject/ClassDef/BulkImportPayload.php rename to src/Handler/DataObject/ClassDef/BulkImport/BulkImportPayload.php index ec627e14..661deec7 100644 --- a/src/Handler/DataObject/ClassDef/BulkImportPayload.php +++ b/src/Handler/DataObject/ClassDef/BulkImport/BulkImportPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImport; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\File\UploadedFile; diff --git a/src/Handler/DataObject/ClassDef/BulkImportResult.php b/src/Handler/DataObject/ClassDef/BulkImport/BulkImportResult.php similarity index 93% rename from src/Handler/DataObject/ClassDef/BulkImportResult.php rename to src/Handler/DataObject/ClassDef/BulkImport/BulkImportResult.php index 18f9a9bb..c686d6af 100644 --- a/src/Handler/DataObject/ClassDef/BulkImportResult.php +++ b/src/Handler/DataObject/ClassDef/BulkImport/BulkImportResult.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\BulkImport; final readonly class BulkImportResult { public function __construct( public array $items, + public string $tmpFile, ) {} } diff --git a/src/Handler/DataObject/ClassDef/DeleteClassHandler.php b/src/Handler/DataObject/ClassDef/DeleteClass/DeleteClassHandler.php similarity index 98% rename from src/Handler/DataObject/ClassDef/DeleteClassHandler.php rename to src/Handler/DataObject/ClassDef/DeleteClass/DeleteClassHandler.php index b7546d1a..3e5dcb78 100644 --- a/src/Handler/DataObject/ClassDef/DeleteClassHandler.php +++ b/src/Handler/DataObject/ClassDef/DeleteClass/DeleteClassHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClass; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ClassDef/DeleteClassPayload.php b/src/Handler/DataObject/ClassDef/DeleteClass/DeleteClassPayload.php similarity index 97% rename from src/Handler/DataObject/ClassDef/DeleteClassPayload.php rename to src/Handler/DataObject/ClassDef/DeleteClass/DeleteClassPayload.php index b97cf04b..2232ff0d 100644 --- a/src/Handler/DataObject/ClassDef/DeleteClassPayload.php +++ b/src/Handler/DataObject/ClassDef/DeleteClass/DeleteClassPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteClass; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php b/src/Handler/DataObject/ClassDef/DeleteSelectOptions/DeleteSelectOptionsHandler.php similarity index 97% rename from src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php rename to src/Handler/DataObject/ClassDef/DeleteSelectOptions/DeleteSelectOptionsHandler.php index ff79093f..34361f07 100644 --- a/src/Handler/DataObject/ClassDef/DeleteSelectOptionsHandler.php +++ b/src/Handler/DataObject/ClassDef/DeleteSelectOptions/DeleteSelectOptionsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptions; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/src/Handler/DataObject/ClassDef/DeleteSelectOptionsPayload.php b/src/Handler/DataObject/ClassDef/DeleteSelectOptions/DeleteSelectOptionsPayload.php similarity index 96% rename from src/Handler/DataObject/ClassDef/DeleteSelectOptionsPayload.php rename to src/Handler/DataObject/ClassDef/DeleteSelectOptions/DeleteSelectOptionsPayload.php index 0947acaa..ee391d3c 100644 --- a/src/Handler/DataObject/ClassDef/DeleteSelectOptionsPayload.php +++ b/src/Handler/DataObject/ClassDef/DeleteSelectOptions/DeleteSelectOptionsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DeleteSelectOptions; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use OpenDxp\Model\DataObject\SelectOptions\Config; diff --git a/src/Handler/DataObject/ClassDef/DoBulkExportHandler.php b/src/Handler/DataObject/ClassDef/DoBulkExport/DoBulkExportHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/DoBulkExportHandler.php rename to src/Handler/DataObject/ClassDef/DoBulkExport/DoBulkExportHandler.php index e05d652b..e219d160 100644 --- a/src/Handler/DataObject/ClassDef/DoBulkExportHandler.php +++ b/src/Handler/DataObject/ClassDef/DoBulkExport/DoBulkExportHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DoBulkExport; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ClassDef/DoBulkExportResult.php b/src/Handler/DataObject/ClassDef/DoBulkExport/DoBulkExportResult.php similarity index 97% rename from src/Handler/DataObject/ClassDef/DoBulkExportResult.php rename to src/Handler/DataObject/ClassDef/DoBulkExport/DoBulkExportResult.php index 1e12cac2..322da383 100644 --- a/src/Handler/DataObject/ClassDef/DoBulkExportResult.php +++ b/src/Handler/DataObject/ClassDef/DoBulkExport/DoBulkExportResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\DoBulkExport; final readonly class DoBulkExportResult { diff --git a/src/Handler/DataObject/ClassDef/ExportClassHandler.php b/src/Handler/DataObject/ClassDef/ExportClass/ExportClassHandler.php similarity index 98% rename from src/Handler/DataObject/ClassDef/ExportClassHandler.php rename to src/Handler/DataObject/ClassDef/ExportClass/ExportClassHandler.php index 163d1779..d67637de 100644 --- a/src/Handler/DataObject/ClassDef/ExportClassHandler.php +++ b/src/Handler/DataObject/ClassDef/ExportClass/ExportClassHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClass; use OpenDxp\Logger; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ClassDef/ExportClassPayload.php b/src/Handler/DataObject/ClassDef/ExportClass/ExportClassPayload.php similarity index 97% rename from src/Handler/DataObject/ClassDef/ExportClassPayload.php rename to src/Handler/DataObject/ClassDef/ExportClass/ExportClassPayload.php index 4fb87fd0..6324e1e9 100644 --- a/src/Handler/DataObject/ClassDef/ExportClassPayload.php +++ b/src/Handler/DataObject/ClassDef/ExportClass/ExportClassPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClass; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/ExportClassResult.php b/src/Handler/DataObject/ClassDef/ExportClass/ExportClassResult.php similarity index 98% rename from src/Handler/DataObject/ClassDef/ExportClassResult.php rename to src/Handler/DataObject/ClassDef/ExportClass/ExportClassResult.php index e76693b7..49c2a414 100644 --- a/src/Handler/DataObject/ClassDef/ExportClassResult.php +++ b/src/Handler/DataObject/ClassDef/ExportClass/ExportClassResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ExportClass; final readonly class ExportClassResult { diff --git a/src/Handler/DataObject/ClassDef/GetAssetTypesHandler.php b/src/Handler/DataObject/ClassDef/GetAssetTypes/GetAssetTypesHandler.php similarity index 98% rename from src/Handler/DataObject/ClassDef/GetAssetTypesHandler.php rename to src/Handler/DataObject/ClassDef/GetAssetTypes/GetAssetTypesHandler.php index d3e764b4..550bce63 100644 --- a/src/Handler/DataObject/ClassDef/GetAssetTypesHandler.php +++ b/src/Handler/DataObject/ClassDef/GetAssetTypes/GetAssetTypesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetAssetTypes; use OpenDxp\Model\Asset; diff --git a/src/Handler/DataObject/ClassDef/GetAssetTypesResult.php b/src/Handler/DataObject/ClassDef/GetAssetTypes/GetAssetTypesResult.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetAssetTypesResult.php rename to src/Handler/DataObject/ClassDef/GetAssetTypes/GetAssetTypesResult.php index 9ece2065..7a0659a8 100644 --- a/src/Handler/DataObject/ClassDef/GetAssetTypesResult.php +++ b/src/Handler/DataObject/ClassDef/GetAssetTypes/GetAssetTypesResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetAssetTypes; final readonly class GetAssetTypesResult { diff --git a/src/Handler/DataObject/ClassDef/GetClassHandler.php b/src/Handler/DataObject/ClassDef/GetClass/GetClassHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/GetClassHandler.php rename to src/Handler/DataObject/ClassDef/GetClass/GetClassHandler.php index d4490307..c4060e01 100644 --- a/src/Handler/DataObject/ClassDef/GetClassHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClass/GetClassHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClass; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/src/Handler/DataObject/ClassDef/GetClassPayload.php b/src/Handler/DataObject/ClassDef/GetClass/GetClassPayload.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetClassPayload.php rename to src/Handler/DataObject/ClassDef/GetClass/GetClassPayload.php index eee0a724..0b3f29df 100644 --- a/src/Handler/DataObject/ClassDef/GetClassPayload.php +++ b/src/Handler/DataObject/ClassDef/GetClass/GetClassPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClass; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/GetClassResult.php b/src/Handler/DataObject/ClassDef/GetClass/GetClassResult.php similarity index 98% rename from src/Handler/DataObject/ClassDef/GetClassResult.php rename to src/Handler/DataObject/ClassDef/GetClass/GetClassResult.php index 40f86e6b..218dc816 100644 --- a/src/Handler/DataObject/ClassDef/GetClassResult.php +++ b/src/Handler/DataObject/ClassDef/GetClass/GetClassResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClass; final readonly class GetClassResult { diff --git a/src/Handler/DataObject/ClassDef/GetClassBulkExportListHandler.php b/src/Handler/DataObject/ClassDef/GetClassBulkExportList/GetClassBulkExportListHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/GetClassBulkExportListHandler.php rename to src/Handler/DataObject/ClassDef/GetClassBulkExportList/GetClassBulkExportListHandler.php index 45f4024e..097ee037 100644 --- a/src/Handler/DataObject/ClassDef/GetClassBulkExportListHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClassBulkExportList/GetClassBulkExportListHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassBulkExportList; use OpenDxp\Model\DataObject; use OpenDxp\Model\User; diff --git a/src/Handler/DataObject/ClassDef/GetClassBulkExportListResult.php b/src/Handler/DataObject/ClassDef/GetClassBulkExportList/GetClassBulkExportListResult.php similarity index 96% rename from src/Handler/DataObject/ClassDef/GetClassBulkExportListResult.php rename to src/Handler/DataObject/ClassDef/GetClassBulkExportList/GetClassBulkExportListResult.php index e3818b45..dc9467cf 100644 --- a/src/Handler/DataObject/ClassDef/GetClassBulkExportListResult.php +++ b/src/Handler/DataObject/ClassDef/GetClassBulkExportList/GetClassBulkExportListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassBulkExportList; final readonly class GetClassBulkExportListResult { diff --git a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigHandler.php b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigHandler.php rename to src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigHandler.php index 772c8ffc..9a014769 100644 --- a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfig; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigPayload.php b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigPayload.php similarity index 94% rename from src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigPayload.php rename to src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigPayload.php index 5b31352d..c00ac6b6 100644 --- a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigPayload.php +++ b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfig; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigResult.php b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigResult.php similarity index 95% rename from src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigResult.php rename to src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigResult.php index 94af95fd..d8424f08 100644 --- a/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfigResult.php +++ b/src/Handler/DataObject/ClassDef/GetClassDefinitionForColumnConfig/GetClassDefinitionForColumnConfigResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassDefinitionForColumnConfig; final readonly class GetClassDefinitionForColumnConfigResult { diff --git a/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php b/src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/GetClassIconsHandler.php rename to src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsHandler.php index 7fa8c31e..5cc9cd90 100644 --- a/src/Handler/DataObject/ClassDef/GetClassIconsHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIcons; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Helper\FileSystemHelper; diff --git a/src/Handler/DataObject/ClassDef/GetClassIconsPayload.php b/src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsPayload.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetClassIconsPayload.php rename to src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsPayload.php index 38ba6d97..7873d07c 100644 --- a/src/Handler/DataObject/ClassDef/GetClassIconsPayload.php +++ b/src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIcons; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/GetClassIconsResult.php b/src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsResult.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetClassIconsResult.php rename to src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsResult.php index bec40b50..331e5263 100644 --- a/src/Handler/DataObject/ClassDef/GetClassIconsResult.php +++ b/src/Handler/DataObject/ClassDef/GetClassIcons/GetClassIconsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassIcons; final readonly class GetClassIconsResult { diff --git a/src/Handler/DataObject/ClassDef/GetClassTreeHandler.php b/src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreeHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/GetClassTreeHandler.php rename to src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreeHandler.php index 34a6551f..89ddb79e 100644 --- a/src/Handler/DataObject/ClassDef/GetClassTreeHandler.php +++ b/src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreeHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTree; use OpenDxp\Model\DataObject; use OpenDxp\Model\Translation; diff --git a/src/Handler/DataObject/ClassDef/GetClassTreePayload.php b/src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreePayload.php similarity index 98% rename from src/Handler/DataObject/ClassDef/GetClassTreePayload.php rename to src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreePayload.php index 64e48b01..d80db648 100644 --- a/src/Handler/DataObject/ClassDef/GetClassTreePayload.php +++ b/src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreePayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTree; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/GetClassTreeResult.php b/src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreeResult.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetClassTreeResult.php rename to src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreeResult.php index c59ef585..e7e6c53a 100644 --- a/src/Handler/DataObject/ClassDef/GetClassTreeResult.php +++ b/src/Handler/DataObject/ClassDef/GetClassTree/GetClassTreeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetClassTree; final readonly class GetClassTreeResult { diff --git a/src/Handler/DataObject/ClassDef/GetDocumentTypesHandler.php b/src/Handler/DataObject/ClassDef/GetDocumentTypes/GetDocumentTypesHandler.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetDocumentTypesHandler.php rename to src/Handler/DataObject/ClassDef/GetDocumentTypes/GetDocumentTypesHandler.php index 17fb68fc..79cbac37 100644 --- a/src/Handler/DataObject/ClassDef/GetDocumentTypesHandler.php +++ b/src/Handler/DataObject/ClassDef/GetDocumentTypes/GetDocumentTypesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetDocumentTypes; use OpenDxp\Model\Document; diff --git a/src/Handler/DataObject/ClassDef/GetDocumentTypesResult.php b/src/Handler/DataObject/ClassDef/GetDocumentTypes/GetDocumentTypesResult.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetDocumentTypesResult.php rename to src/Handler/DataObject/ClassDef/GetDocumentTypes/GetDocumentTypesResult.php index 32864f08..03125457 100644 --- a/src/Handler/DataObject/ClassDef/GetDocumentTypesResult.php +++ b/src/Handler/DataObject/ClassDef/GetDocumentTypes/GetDocumentTypesResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetDocumentTypes; final readonly class GetDocumentTypesResult { diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsHandler.php b/src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsHandler.php similarity index 98% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsHandler.php rename to src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsHandler.php index f9ebe19d..62c8f725 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsHandler.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptions; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsPayload.php b/src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsPayload.php similarity index 96% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsPayload.php rename to src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsPayload.php index b252416c..dacf364f 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsPayload.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptions; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use OpenDxp\Model\DataObject\SelectOptions\Config; diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsResult.php b/src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsResult.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsResult.php rename to src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsResult.php index c0cc8820..99611403 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsResult.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptions/GetSelectOptionsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptions; final readonly class GetSelectOptionsResult { diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeHandler.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreeHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsTreeHandler.php rename to src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreeHandler.php index e5668844..f928c673 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeHandler.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreeHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTree; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreePayload.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreePayload.php similarity index 95% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsTreePayload.php rename to src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreePayload.php index 0d329f82..02e7c473 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreePayload.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreePayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTree; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeResult.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreeResult.php similarity index 96% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsTreeResult.php rename to src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreeResult.php index 95dc12f8..27cf6a62 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsTreeResult.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsTree/GetSelectOptionsTreeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsTree; final readonly class GetSelectOptionsTreeResult { diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesHandler.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesHandler.php similarity index 98% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesHandler.php rename to src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesHandler.php index bf6f179e..9611c563 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesHandler.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsages; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesPayload.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesPayload.php similarity index 96% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesPayload.php rename to src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesPayload.php index a2b101fb..fd0eb1a4 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesPayload.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsages; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use OpenDxp\Model\DataObject\SelectOptions\Config; diff --git a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesResult.php b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesResult.php similarity index 96% rename from src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesResult.php rename to src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesResult.php index 78718856..417559b1 100644 --- a/src/Handler/DataObject/ClassDef/GetSelectOptionsUsagesResult.php +++ b/src/Handler/DataObject/ClassDef/GetSelectOptionsUsages/GetSelectOptionsUsagesResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetSelectOptionsUsages; final readonly class GetSelectOptionsUsagesResult { diff --git a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewHandler.php b/src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewHandler.php similarity index 98% rename from src/Handler/DataObject/ClassDef/GetTextLayoutPreviewHandler.php rename to src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewHandler.php index 274bdcdd..cd910215 100644 --- a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewHandler.php +++ b/src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreview; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewPayload.php b/src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewPayload.php similarity index 97% rename from src/Handler/DataObject/ClassDef/GetTextLayoutPreviewPayload.php rename to src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewPayload.php index c13cb8de..420174e4 100644 --- a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewPayload.php +++ b/src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreview; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewResult.php b/src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewResult.php similarity index 96% rename from src/Handler/DataObject/ClassDef/GetTextLayoutPreviewResult.php rename to src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewResult.php index f165ef9f..27923881 100644 --- a/src/Handler/DataObject/ClassDef/GetTextLayoutPreviewResult.php +++ b/src/Handler/DataObject/ClassDef/GetTextLayoutPreview/GetTextLayoutPreviewResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetTextLayoutPreview; final readonly class GetTextLayoutPreviewResult { diff --git a/src/Handler/DataObject/ClassDef/GetVideoAllowedTypesHandler.php b/src/Handler/DataObject/ClassDef/GetVideoAllowedTypes/GetVideoAllowedTypesHandler.php similarity index 98% rename from src/Handler/DataObject/ClassDef/GetVideoAllowedTypesHandler.php rename to src/Handler/DataObject/ClassDef/GetVideoAllowedTypes/GetVideoAllowedTypesHandler.php index ee2e9ea0..c303dbfd 100644 --- a/src/Handler/DataObject/ClassDef/GetVideoAllowedTypesHandler.php +++ b/src/Handler/DataObject/ClassDef/GetVideoAllowedTypes/GetVideoAllowedTypesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetVideoAllowedTypes; use OpenDxp\Model\DataObject; use Symfony\Contracts\Translation\TranslatorInterface; diff --git a/src/Handler/DataObject/ClassDef/GetVideoAllowedTypesResult.php b/src/Handler/DataObject/ClassDef/GetVideoAllowedTypes/GetVideoAllowedTypesResult.php similarity index 96% rename from src/Handler/DataObject/ClassDef/GetVideoAllowedTypesResult.php rename to src/Handler/DataObject/ClassDef/GetVideoAllowedTypes/GetVideoAllowedTypesResult.php index a7493cc9..872bb7a5 100644 --- a/src/Handler/DataObject/ClassDef/GetVideoAllowedTypesResult.php +++ b/src/Handler/DataObject/ClassDef/GetVideoAllowedTypes/GetVideoAllowedTypesResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\GetVideoAllowedTypes; final readonly class GetVideoAllowedTypesResult { diff --git a/src/Handler/DataObject/ClassDef/ImportClassHandler.php b/src/Handler/DataObject/ClassDef/ImportClass/ImportClassHandler.php similarity index 98% rename from src/Handler/DataObject/ClassDef/ImportClassHandler.php rename to src/Handler/DataObject/ClassDef/ImportClass/ImportClassHandler.php index 7e40deb8..105414cc 100644 --- a/src/Handler/DataObject/ClassDef/ImportClassHandler.php +++ b/src/Handler/DataObject/ClassDef/ImportClass/ImportClassHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClass; use OpenDxp\Model\DataObject; use RuntimeException; diff --git a/src/Handler/DataObject/ClassDef/ImportClassPayload.php b/src/Handler/DataObject/ClassDef/ImportClass/ImportClassPayload.php similarity index 98% rename from src/Handler/DataObject/ClassDef/ImportClassPayload.php rename to src/Handler/DataObject/ClassDef/ImportClass/ImportClassPayload.php index 5f68a532..2c94ff98 100644 --- a/src/Handler/DataObject/ClassDef/ImportClassPayload.php +++ b/src/Handler/DataObject/ClassDef/ImportClass/ImportClassPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\ImportClass; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\File\UploadedFile; diff --git a/src/Handler/DataObject/ClassDef/SaveClassDefinitionHandler.php b/src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/SaveClassDefinitionHandler.php rename to src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionHandler.php index fdaf497b..82e0bcaa 100644 --- a/src/Handler/DataObject/ClassDef/SaveClassDefinitionHandler.php +++ b/src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinition; use OpenDxp\Model\DataObject; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; diff --git a/src/Handler/DataObject/ClassDef/SaveClassDefinitionPayload.php b/src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionPayload.php similarity index 97% rename from src/Handler/DataObject/ClassDef/SaveClassDefinitionPayload.php rename to src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionPayload.php index c81bdeb7..088daeb7 100644 --- a/src/Handler/DataObject/ClassDef/SaveClassDefinitionPayload.php +++ b/src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinition; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/ClassDef/SaveClassDefinitionResult.php b/src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionResult.php similarity index 97% rename from src/Handler/DataObject/ClassDef/SaveClassDefinitionResult.php rename to src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionResult.php index b7e460f3..88781769 100644 --- a/src/Handler/DataObject/ClassDef/SaveClassDefinitionResult.php +++ b/src/Handler/DataObject/ClassDef/SaveClassDefinition/SaveClassDefinitionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveClassDefinition; use OpenDxp\Model\DataObject\ClassDefinition; diff --git a/src/Handler/DataObject/ClassDef/SaveSelectOptionsHandler.php b/src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsHandler.php similarity index 99% rename from src/Handler/DataObject/ClassDef/SaveSelectOptionsHandler.php rename to src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsHandler.php index 06454db9..f1a13cd1 100644 --- a/src/Handler/DataObject/ClassDef/SaveSelectOptionsHandler.php +++ b/src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptions; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ClassDef/SaveSelectOptionsPayload.php b/src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsPayload.php similarity index 98% rename from src/Handler/DataObject/ClassDef/SaveSelectOptionsPayload.php rename to src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsPayload.php index 57a4d22a..18481eed 100644 --- a/src/Handler/DataObject/ClassDef/SaveSelectOptionsPayload.php +++ b/src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptions; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use OpenDxp\Model\DataObject\SelectOptions\Config; diff --git a/src/Handler/DataObject/ClassDef/SaveSelectOptionsResult.php b/src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsResult.php similarity index 97% rename from src/Handler/DataObject/ClassDef/SaveSelectOptionsResult.php rename to src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsResult.php index e85091e2..6e127d3a 100644 --- a/src/Handler/DataObject/ClassDef/SaveSelectOptionsResult.php +++ b/src/Handler/DataObject/ClassDef/SaveSelectOptions/SaveSelectOptionsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SaveSelectOptions; final readonly class SaveSelectOptionsResult { diff --git a/src/Handler/DataObject/ClassDef/SuggestClassIdentifierHandler.php b/src/Handler/DataObject/ClassDef/SuggestClassIdentifier/SuggestClassIdentifierHandler.php similarity index 97% rename from src/Handler/DataObject/ClassDef/SuggestClassIdentifierHandler.php rename to src/Handler/DataObject/ClassDef/SuggestClassIdentifier/SuggestClassIdentifierHandler.php index 92b25208..4ec8852e 100644 --- a/src/Handler/DataObject/ClassDef/SuggestClassIdentifierHandler.php +++ b/src/Handler/DataObject/ClassDef/SuggestClassIdentifier/SuggestClassIdentifierHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SuggestClassIdentifier; use OpenDxp\Db; diff --git a/src/Handler/DataObject/ClassDef/SuggestClassIdentifierResult.php b/src/Handler/DataObject/ClassDef/SuggestClassIdentifier/SuggestClassIdentifierResult.php similarity index 96% rename from src/Handler/DataObject/ClassDef/SuggestClassIdentifierResult.php rename to src/Handler/DataObject/ClassDef/SuggestClassIdentifier/SuggestClassIdentifierResult.php index 0295c5e7..323b4a95 100644 --- a/src/Handler/DataObject/ClassDef/SuggestClassIdentifierResult.php +++ b/src/Handler/DataObject/ClassDef/SuggestClassIdentifier/SuggestClassIdentifierResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ClassDef\SuggestClassIdentifier; final readonly class SuggestClassIdentifierResult { diff --git a/src/Handler/DataObject/Classificationstore/AddCollectionsHandler.php b/src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsHandler.php similarity index 99% rename from src/Handler/DataObject/Classificationstore/AddCollectionsHandler.php rename to src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsHandler.php index ba65aa39..e463e5ef 100644 --- a/src/Handler/DataObject/Classificationstore/AddCollectionsHandler.php +++ b/src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollections; use Doctrine\DBAL\ArrayParameterType; use OpenDxp\Db; diff --git a/src/Handler/DataObject/Classificationstore/AddCollectionsPayload.php b/src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsPayload.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/AddCollectionsPayload.php rename to src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsPayload.php index 2eda63b9..e618903f 100644 --- a/src/Handler/DataObject/Classificationstore/AddCollectionsPayload.php +++ b/src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollections; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/AddCollectionsResult.php b/src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/AddCollectionsResult.php rename to src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsResult.php index 03fa1511..234247dc 100644 --- a/src/Handler/DataObject/Classificationstore/AddCollectionsResult.php +++ b/src/Handler/DataObject/Classificationstore/AddCollections/AddCollectionsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddCollections; final readonly class AddCollectionsResult { diff --git a/src/Handler/DataObject/Classificationstore/AddGroupsHandler.php b/src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsHandler.php similarity index 99% rename from src/Handler/DataObject/Classificationstore/AddGroupsHandler.php rename to src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsHandler.php index 9051b973..fe1acd5f 100644 --- a/src/Handler/DataObject/Classificationstore/AddGroupsHandler.php +++ b/src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroups; use OpenDxp\Model\DataObject; use OpenDxp\Model\DataObject\ClassDefinition\Data\LayoutDefinitionEnrichmentInterface; diff --git a/src/Handler/DataObject/Classificationstore/AddGroupsPayload.php b/src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsPayload.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/AddGroupsPayload.php rename to src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsPayload.php index 6d5ea320..283af37c 100644 --- a/src/Handler/DataObject/Classificationstore/AddGroupsPayload.php +++ b/src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroups; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/AddGroupsResult.php b/src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/AddGroupsResult.php rename to src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsResult.php index 0cea0fa2..d95661e2 100644 --- a/src/Handler/DataObject/Classificationstore/AddGroupsResult.php +++ b/src/Handler/DataObject/Classificationstore/AddGroups/AddGroupsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddGroups; final readonly class AddGroupsResult { diff --git a/src/Handler/DataObject/Classificationstore/AddPropertyHandler.php b/src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyHandler.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/AddPropertyHandler.php rename to src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyHandler.php index 76e9912e..03b37857 100644 --- a/src/Handler/DataObject/Classificationstore/AddPropertyHandler.php +++ b/src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddProperty; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/AddPropertyPayload.php b/src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyPayload.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/AddPropertyPayload.php rename to src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyPayload.php index af3ad439..64f96699 100644 --- a/src/Handler/DataObject/Classificationstore/AddPropertyPayload.php +++ b/src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddProperty; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/AddPropertyResult.php b/src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/AddPropertyResult.php rename to src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyResult.php index 1f960b9b..e938bd65 100644 --- a/src/Handler/DataObject/Classificationstore/AddPropertyResult.php +++ b/src/Handler/DataObject/Classificationstore/AddProperty/AddPropertyResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\AddProperty; final readonly class AddPropertyResult { diff --git a/src/Handler/DataObject/Classificationstore/CreateCollectionHandler.php b/src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionHandler.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/CreateCollectionHandler.php rename to src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionHandler.php index 7126a2b0..ca966118 100644 --- a/src/Handler/DataObject/Classificationstore/CreateCollectionHandler.php +++ b/src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollection; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/CreateCollectionPayload.php b/src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionPayload.php similarity index 95% rename from src/Handler/DataObject/Classificationstore/CreateCollectionPayload.php rename to src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionPayload.php index 6f61e856..b6f73f01 100644 --- a/src/Handler/DataObject/Classificationstore/CreateCollectionPayload.php +++ b/src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollection; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use OpenDxp\Security\SecurityHelper; diff --git a/src/Handler/DataObject/Classificationstore/CreateCollectionResult.php b/src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionResult.php similarity index 95% rename from src/Handler/DataObject/Classificationstore/CreateCollectionResult.php rename to src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionResult.php index bdc54693..1eae6daf 100644 --- a/src/Handler/DataObject/Classificationstore/CreateCollectionResult.php +++ b/src/Handler/DataObject/Classificationstore/CreateCollection/CreateCollectionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateCollection; final readonly class CreateCollectionResult { diff --git a/src/Handler/DataObject/Classificationstore/CreateGroupHandler.php b/src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupHandler.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/CreateGroupHandler.php rename to src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupHandler.php index 12ebfa9b..eb49c44c 100644 --- a/src/Handler/DataObject/Classificationstore/CreateGroupHandler.php +++ b/src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroup; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/CreateGroupPayload.php b/src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupPayload.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/CreateGroupPayload.php rename to src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupPayload.php index 7bd60467..1baff897 100644 --- a/src/Handler/DataObject/Classificationstore/CreateGroupPayload.php +++ b/src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroup; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use OpenDxp\Security\SecurityHelper; diff --git a/src/Handler/DataObject/Classificationstore/CreateGroupResult.php b/src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/CreateGroupResult.php rename to src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupResult.php index a51ef495..a5ac70cc 100644 --- a/src/Handler/DataObject/Classificationstore/CreateGroupResult.php +++ b/src/Handler/DataObject/Classificationstore/CreateGroup/CreateGroupResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateGroup; final readonly class CreateGroupResult { diff --git a/src/Handler/DataObject/Classificationstore/CreateStoreHandler.php b/src/Handler/DataObject/Classificationstore/CreateStore/CreateStoreHandler.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/CreateStoreHandler.php rename to src/Handler/DataObject/Classificationstore/CreateStore/CreateStoreHandler.php index 745fd9e9..72bdeb45 100644 --- a/src/Handler/DataObject/Classificationstore/CreateStoreHandler.php +++ b/src/Handler/DataObject/Classificationstore/CreateStore/CreateStoreHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStore; use Exception; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/CreateStorePayload.php b/src/Handler/DataObject/Classificationstore/CreateStore/CreateStorePayload.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/CreateStorePayload.php rename to src/Handler/DataObject/Classificationstore/CreateStore/CreateStorePayload.php index d1aff2ae..5e10c46c 100644 --- a/src/Handler/DataObject/Classificationstore/CreateStorePayload.php +++ b/src/Handler/DataObject/Classificationstore/CreateStore/CreateStorePayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStore; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use OpenDxp\Security\SecurityHelper; diff --git a/src/Handler/DataObject/Classificationstore/CreateStoreResult.php b/src/Handler/DataObject/Classificationstore/CreateStore/CreateStoreResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/CreateStoreResult.php rename to src/Handler/DataObject/Classificationstore/CreateStore/CreateStoreResult.php index 87dc2458..224746e6 100644 --- a/src/Handler/DataObject/Classificationstore/CreateStoreResult.php +++ b/src/Handler/DataObject/Classificationstore/CreateStore/CreateStoreResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\CreateStore; final readonly class CreateStoreResult { diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionHandler.php b/src/Handler/DataObject/Classificationstore/DeleteCollection/DeleteCollectionHandler.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/DeleteCollectionHandler.php rename to src/Handler/DataObject/Classificationstore/DeleteCollection/DeleteCollectionHandler.php index 7c433393..dca7af6a 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteCollectionHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteCollection/DeleteCollectionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollection; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionPayload.php b/src/Handler/DataObject/Classificationstore/DeleteCollection/DeleteCollectionPayload.php similarity index 94% rename from src/Handler/DataObject/Classificationstore/DeleteCollectionPayload.php rename to src/Handler/DataObject/Classificationstore/DeleteCollection/DeleteCollectionPayload.php index 7cd77b49..ec0d3b52 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteCollectionPayload.php +++ b/src/Handler/DataObject/Classificationstore/DeleteCollection/DeleteCollectionPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollection; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelation/DeleteCollectionRelationHandler.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php rename to src/Handler/DataObject/Classificationstore/DeleteCollectionRelation/DeleteCollectionRelationHandler.php index 99c18017..79d88b5f 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelation/DeleteCollectionRelationHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelation; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationPayload.php b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelation/DeleteCollectionRelationPayload.php similarity index 94% rename from src/Handler/DataObject/Classificationstore/DeleteCollectionRelationPayload.php rename to src/Handler/DataObject/Classificationstore/DeleteCollectionRelation/DeleteCollectionRelationPayload.php index 8f40892d..0bdcbc39 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteCollectionRelationPayload.php +++ b/src/Handler/DataObject/Classificationstore/DeleteCollectionRelation/DeleteCollectionRelationPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteCollectionRelation; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php b/src/Handler/DataObject/Classificationstore/DeleteGroup/DeleteGroupHandler.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php rename to src/Handler/DataObject/Classificationstore/DeleteGroup/DeleteGroupHandler.php index 0575ab7e..e48f6ccc 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteGroupHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteGroup/DeleteGroupHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroup; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/DeleteGroupPayload.php b/src/Handler/DataObject/Classificationstore/DeleteGroup/DeleteGroupPayload.php similarity index 95% rename from src/Handler/DataObject/Classificationstore/DeleteGroupPayload.php rename to src/Handler/DataObject/Classificationstore/DeleteGroup/DeleteGroupPayload.php index 18bb2ce0..75bb4336 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteGroupPayload.php +++ b/src/Handler/DataObject/Classificationstore/DeleteGroup/DeleteGroupPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteGroup; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php b/src/Handler/DataObject/Classificationstore/DeleteProperty/DeletePropertyHandler.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php rename to src/Handler/DataObject/Classificationstore/DeleteProperty/DeletePropertyHandler.php index 83d8cd72..17150673 100644 --- a/src/Handler/DataObject/Classificationstore/DeletePropertyHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteProperty/DeletePropertyHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteProperty; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/DeletePropertyPayload.php b/src/Handler/DataObject/Classificationstore/DeleteProperty/DeletePropertyPayload.php similarity index 94% rename from src/Handler/DataObject/Classificationstore/DeletePropertyPayload.php rename to src/Handler/DataObject/Classificationstore/DeleteProperty/DeletePropertyPayload.php index 1d0a8fb7..14a3feae 100644 --- a/src/Handler/DataObject/Classificationstore/DeletePropertyPayload.php +++ b/src/Handler/DataObject/Classificationstore/DeleteProperty/DeletePropertyPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteProperty; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php b/src/Handler/DataObject/Classificationstore/DeleteRelation/DeleteRelationHandler.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php rename to src/Handler/DataObject/Classificationstore/DeleteRelation/DeleteRelationHandler.php index 93a5c97c..e3e85a77 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteRelationHandler.php +++ b/src/Handler/DataObject/Classificationstore/DeleteRelation/DeleteRelationHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelation; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/DeleteRelationPayload.php b/src/Handler/DataObject/Classificationstore/DeleteRelation/DeleteRelationPayload.php similarity index 95% rename from src/Handler/DataObject/Classificationstore/DeleteRelationPayload.php rename to src/Handler/DataObject/Classificationstore/DeleteRelation/DeleteRelationPayload.php index ca8ef231..ace7d6b9 100644 --- a/src/Handler/DataObject/Classificationstore/DeleteRelationPayload.php +++ b/src/Handler/DataObject/Classificationstore/DeleteRelation/DeleteRelationPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\DeleteRelation; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/EditStoreHandler.php b/src/Handler/DataObject/Classificationstore/EditStore/EditStoreHandler.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/EditStoreHandler.php rename to src/Handler/DataObject/Classificationstore/EditStore/EditStoreHandler.php index d5a4dc72..3cb08b92 100644 --- a/src/Handler/DataObject/Classificationstore/EditStoreHandler.php +++ b/src/Handler/DataObject/Classificationstore/EditStore/EditStoreHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStore; use Exception; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/EditStorePayload.php b/src/Handler/DataObject/Classificationstore/EditStore/EditStorePayload.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/EditStorePayload.php rename to src/Handler/DataObject/Classificationstore/EditStore/EditStorePayload.php index 9be904f3..436c097e 100644 --- a/src/Handler/DataObject/Classificationstore/EditStorePayload.php +++ b/src/Handler/DataObject/Classificationstore/EditStore/EditStorePayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\EditStore; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php b/src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsHandler.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php rename to src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsHandler.php index beaf7008..52796c1f 100644 --- a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelations; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Db; diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsPayload.php b/src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsPayload.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/GetCollectionRelationsPayload.php rename to src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsPayload.php index 8124adb7..25f8c619 100644 --- a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsPayload.php +++ b/src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelations; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsResult.php b/src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsResult.php similarity index 95% rename from src/Handler/DataObject/Classificationstore/GetCollectionRelationsResult.php rename to src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsResult.php index c0658e1b..562039fc 100644 --- a/src/Handler/DataObject/Classificationstore/GetCollectionRelationsResult.php +++ b/src/Handler/DataObject/Classificationstore/GetCollectionRelations/GetCollectionRelationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollectionRelations; final readonly class GetCollectionRelationsResult { diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionsHandler.php b/src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsHandler.php similarity index 99% rename from src/Handler/DataObject/Classificationstore/GetCollectionsHandler.php rename to src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsHandler.php index bb817289..2ffe9929 100644 --- a/src/Handler/DataObject/Classificationstore/GetCollectionsHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollections; use Doctrine\DBAL\ArrayParameterType; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionsPayload.php b/src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsPayload.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/GetCollectionsPayload.php rename to src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsPayload.php index 263856a7..143f8e7a 100644 --- a/src/Handler/DataObject/Classificationstore/GetCollectionsPayload.php +++ b/src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollections; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/GetCollectionsResult.php b/src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/GetCollectionsResult.php rename to src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsResult.php index 00df9b10..dac28d23 100644 --- a/src/Handler/DataObject/Classificationstore/GetCollectionsResult.php +++ b/src/Handler/DataObject/Classificationstore/GetCollections/GetCollectionsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetCollections; final readonly class GetCollectionsResult { diff --git a/src/Handler/DataObject/Classificationstore/GetGroupsHandler.php b/src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsHandler.php similarity index 99% rename from src/Handler/DataObject/Classificationstore/GetGroupsHandler.php rename to src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsHandler.php index c99b7be5..a4031f99 100644 --- a/src/Handler/DataObject/Classificationstore/GetGroupsHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroups; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Bundle\AdminBundle\Service\Translation\AdminSearchTermResolver; diff --git a/src/Handler/DataObject/Classificationstore/GetGroupsPayload.php b/src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsPayload.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/GetGroupsPayload.php rename to src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsPayload.php index 6acab087..21aebf73 100644 --- a/src/Handler/DataObject/Classificationstore/GetGroupsPayload.php +++ b/src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroups; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/GetGroupsResult.php b/src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/GetGroupsResult.php rename to src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsResult.php index 99720c49..1c641f0a 100644 --- a/src/Handler/DataObject/Classificationstore/GetGroupsResult.php +++ b/src/Handler/DataObject/Classificationstore/GetGroups/GetGroupsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetGroups; final readonly class GetGroupsResult { diff --git a/src/Handler/DataObject/Classificationstore/GetPageHandler.php b/src/Handler/DataObject/Classificationstore/GetPage/GetPageHandler.php similarity index 99% rename from src/Handler/DataObject/Classificationstore/GetPageHandler.php rename to src/Handler/DataObject/Classificationstore/GetPage/GetPageHandler.php index be91c317..83f984c2 100644 --- a/src/Handler/DataObject/Classificationstore/GetPageHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetPage/GetPageHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPage; use OpenDxp\Db; use OpenDxp\Helper\ArrayHelper; diff --git a/src/Handler/DataObject/Classificationstore/GetPagePayload.php b/src/Handler/DataObject/Classificationstore/GetPage/GetPagePayload.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/GetPagePayload.php rename to src/Handler/DataObject/Classificationstore/GetPage/GetPagePayload.php index 0a8896d8..9d662902 100644 --- a/src/Handler/DataObject/Classificationstore/GetPagePayload.php +++ b/src/Handler/DataObject/Classificationstore/GetPage/GetPagePayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPage; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/GetPageResult.php b/src/Handler/DataObject/Classificationstore/GetPage/GetPageResult.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/GetPageResult.php rename to src/Handler/DataObject/Classificationstore/GetPage/GetPageResult.php index 7bc7beec..0fe4dc24 100644 --- a/src/Handler/DataObject/Classificationstore/GetPageResult.php +++ b/src/Handler/DataObject/Classificationstore/GetPage/GetPageResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetPage; final readonly class GetPageResult { diff --git a/src/Handler/DataObject/Classificationstore/GetPropertiesHandler.php b/src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesHandler.php similarity index 99% rename from src/Handler/DataObject/Classificationstore/GetPropertiesHandler.php rename to src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesHandler.php index 9730e147..0f4894b3 100644 --- a/src/Handler/DataObject/Classificationstore/GetPropertiesHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetProperties; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Db; diff --git a/src/Handler/DataObject/Classificationstore/GetPropertiesPayload.php b/src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesPayload.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/GetPropertiesPayload.php rename to src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesPayload.php index 37f4529e..1fafc0c3 100644 --- a/src/Handler/DataObject/Classificationstore/GetPropertiesPayload.php +++ b/src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetProperties; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/GetPropertiesResult.php b/src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/GetPropertiesResult.php rename to src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesResult.php index 82f6f7e2..2b8f495e 100644 --- a/src/Handler/DataObject/Classificationstore/GetPropertiesResult.php +++ b/src/Handler/DataObject/Classificationstore/GetProperties/GetPropertiesResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetProperties; final readonly class GetPropertiesResult { diff --git a/src/Handler/DataObject/Classificationstore/GetRelationsHandler.php b/src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsHandler.php similarity index 99% rename from src/Handler/DataObject/Classificationstore/GetRelationsHandler.php rename to src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsHandler.php index c4d899a0..f6821d05 100644 --- a/src/Handler/DataObject/Classificationstore/GetRelationsHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelations; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Db; diff --git a/src/Handler/DataObject/Classificationstore/GetRelationsPayload.php b/src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsPayload.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/GetRelationsPayload.php rename to src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsPayload.php index d6755451..49c4253e 100644 --- a/src/Handler/DataObject/Classificationstore/GetRelationsPayload.php +++ b/src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelations; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/GetRelationsResult.php b/src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/GetRelationsResult.php rename to src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsResult.php index 3c7c9b4c..3565152d 100644 --- a/src/Handler/DataObject/Classificationstore/GetRelationsResult.php +++ b/src/Handler/DataObject/Classificationstore/GetRelations/GetRelationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetRelations; final readonly class GetRelationsResult { diff --git a/src/Handler/DataObject/Classificationstore/GetStoreTreeHandler.php b/src/Handler/DataObject/Classificationstore/GetStoreTree/GetStoreTreeHandler.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/GetStoreTreeHandler.php rename to src/Handler/DataObject/Classificationstore/GetStoreTree/GetStoreTreeHandler.php index 86a7b6ea..b7185825 100644 --- a/src/Handler/DataObject/Classificationstore/GetStoreTreeHandler.php +++ b/src/Handler/DataObject/Classificationstore/GetStoreTree/GetStoreTreeHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetStoreTree; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/GetStoreTreeResult.php b/src/Handler/DataObject/Classificationstore/GetStoreTree/GetStoreTreeResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/GetStoreTreeResult.php rename to src/Handler/DataObject/Classificationstore/GetStoreTree/GetStoreTreeResult.php index c003960b..e9eb1530 100644 --- a/src/Handler/DataObject/Classificationstore/GetStoreTreeResult.php +++ b/src/Handler/DataObject/Classificationstore/GetStoreTree/GetStoreTreeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetStoreTree; final readonly class GetStoreTreeResult { diff --git a/src/Handler/DataObject/Classificationstore/ListStoresHandler.php b/src/Handler/DataObject/Classificationstore/ListStores/ListStoresHandler.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/ListStoresHandler.php rename to src/Handler/DataObject/Classificationstore/ListStores/ListStoresHandler.php index ea2bff59..d9b41a4a 100644 --- a/src/Handler/DataObject/Classificationstore/ListStoresHandler.php +++ b/src/Handler/DataObject/Classificationstore/ListStores/ListStoresHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\ListStores; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/ListStoresResult.php b/src/Handler/DataObject/Classificationstore/ListStores/ListStoresResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/ListStoresResult.php rename to src/Handler/DataObject/Classificationstore/ListStores/ListStoresResult.php index 4b0fa72a..e39512e1 100644 --- a/src/Handler/DataObject/Classificationstore/ListStoresResult.php +++ b/src/Handler/DataObject/Classificationstore/ListStores/ListStoresResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\ListStores; final readonly class ListStoresResult { diff --git a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsHandler.php b/src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsHandler.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/SaveCollectionRelationsHandler.php rename to src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsHandler.php index 015f3153..5945f8d4 100644 --- a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsHandler.php +++ b/src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelations; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsPayload.php b/src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsPayload.php similarity index 94% rename from src/Handler/DataObject/Classificationstore/SaveCollectionRelationsPayload.php rename to src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsPayload.php index eed76819..48170967 100644 --- a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsPayload.php +++ b/src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelations; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsResult.php b/src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsResult.php similarity index 94% rename from src/Handler/DataObject/Classificationstore/SaveCollectionRelationsResult.php rename to src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsResult.php index 5fede306..4ede0332 100644 --- a/src/Handler/DataObject/Classificationstore/SaveCollectionRelationsResult.php +++ b/src/Handler/DataObject/Classificationstore/SaveCollectionRelations/SaveCollectionRelationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveCollectionRelations; final readonly class SaveCollectionRelationsResult { diff --git a/src/Handler/DataObject/Classificationstore/SaveRelationHandler.php b/src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationHandler.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/SaveRelationHandler.php rename to src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationHandler.php index a7327d33..60431057 100644 --- a/src/Handler/DataObject/Classificationstore/SaveRelationHandler.php +++ b/src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelation; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/SaveRelationPayload.php b/src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationPayload.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/SaveRelationPayload.php rename to src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationPayload.php index c4565e5f..61298d23 100644 --- a/src/Handler/DataObject/Classificationstore/SaveRelationPayload.php +++ b/src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelation; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/SaveRelationResult.php b/src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/SaveRelationResult.php rename to src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationResult.php index 6f1afbaa..cce70f5f 100644 --- a/src/Handler/DataObject/Classificationstore/SaveRelationResult.php +++ b/src/Handler/DataObject/Classificationstore/SaveRelation/SaveRelationResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SaveRelation; final readonly class SaveRelationResult { diff --git a/src/Handler/DataObject/Classificationstore/SearchRelationsHandler.php b/src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsHandler.php similarity index 99% rename from src/Handler/DataObject/Classificationstore/SearchRelationsHandler.php rename to src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsHandler.php index b648f62a..d0639f00 100644 --- a/src/Handler/DataObject/Classificationstore/SearchRelationsHandler.php +++ b/src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelations; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Bundle\AdminBundle\Service\Translation\AdminSearchTermResolver; diff --git a/src/Handler/DataObject/Classificationstore/SearchRelationsPayload.php b/src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsPayload.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/SearchRelationsPayload.php rename to src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsPayload.php index 5194e83c..d2d919d2 100644 --- a/src/Handler/DataObject/Classificationstore/SearchRelationsPayload.php +++ b/src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelations; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/SearchRelationsResult.php b/src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/SearchRelationsResult.php rename to src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsResult.php index cbfb0040..71a30ad6 100644 --- a/src/Handler/DataObject/Classificationstore/SearchRelationsResult.php +++ b/src/Handler/DataObject/Classificationstore/SearchRelations/SearchRelationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\SearchRelations; final readonly class SearchRelationsResult { diff --git a/src/Handler/DataObject/Classificationstore/UpdateCollectionHandler.php b/src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionHandler.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/UpdateCollectionHandler.php rename to src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionHandler.php index a1f786b5..eac9542c 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateCollectionHandler.php +++ b/src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollection; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/UpdateCollectionPayload.php b/src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionPayload.php similarity index 95% rename from src/Handler/DataObject/Classificationstore/UpdateCollectionPayload.php rename to src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionPayload.php index fcb45c0a..01fb85af 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateCollectionPayload.php +++ b/src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollection; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/UpdateCollectionResult.php b/src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionResult.php similarity index 95% rename from src/Handler/DataObject/Classificationstore/UpdateCollectionResult.php rename to src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionResult.php index ca36bc40..b2926e4c 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateCollectionResult.php +++ b/src/Handler/DataObject/Classificationstore/UpdateCollection/UpdateCollectionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateCollection; final readonly class UpdateCollectionResult { diff --git a/src/Handler/DataObject/Classificationstore/UpdateGroupHandler.php b/src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupHandler.php similarity index 98% rename from src/Handler/DataObject/Classificationstore/UpdateGroupHandler.php rename to src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupHandler.php index ad8e56ca..27e0b498 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateGroupHandler.php +++ b/src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroup; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/UpdateGroupPayload.php b/src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupPayload.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/UpdateGroupPayload.php rename to src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupPayload.php index 74e8008c..e661f96a 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateGroupPayload.php +++ b/src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroup; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/UpdateGroupResult.php b/src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/UpdateGroupResult.php rename to src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupResult.php index 8fead9fc..6a9cde08 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateGroupResult.php +++ b/src/Handler/DataObject/Classificationstore/UpdateGroup/UpdateGroupResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateGroup; final readonly class UpdateGroupResult { diff --git a/src/Handler/DataObject/Classificationstore/UpdatePropertyHandler.php b/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyHandler.php similarity index 97% rename from src/Handler/DataObject/Classificationstore/UpdatePropertyHandler.php rename to src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyHandler.php index 454ab6ba..97dd5e87 100644 --- a/src/Handler/DataObject/Classificationstore/UpdatePropertyHandler.php +++ b/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateProperty; use OpenDxp\Model\DataObject\Classificationstore; diff --git a/src/Handler/DataObject/Classificationstore/UpdatePropertyPayload.php b/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyPayload.php similarity index 95% rename from src/Handler/DataObject/Classificationstore/UpdatePropertyPayload.php rename to src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyPayload.php index 097aa30d..5b13be78 100644 --- a/src/Handler/DataObject/Classificationstore/UpdatePropertyPayload.php +++ b/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyPayload.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateProperty; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Classificationstore/UpdatePropertyResult.php b/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyResult.php similarity index 96% rename from src/Handler/DataObject/Classificationstore/UpdatePropertyResult.php rename to src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyResult.php index 80522a2c..e20943b1 100644 --- a/src/Handler/DataObject/Classificationstore/UpdatePropertyResult.php +++ b/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateProperty; final readonly class UpdatePropertyResult { diff --git a/src/Handler/DataObject/Copy/CopyInfo/CopyInfoHandler.php b/src/Handler/DataObject/Copy/CopyInfo/CopyInfoHandler.php new file mode 100644 index 00000000..4345d44d --- /dev/null +++ b/src/Handler/DataObject/Copy/CopyInfo/CopyInfoHandler.php @@ -0,0 +1,99 @@ +type === 'recursive' || $payload->type === 'recursive-update-references') { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_dataobject_dataobject_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $payload->sourceId, + 'targetId' => $payload->targetId, + 'type' => 'child', + 'transactionId' => $transactionId, + 'saveParentId' => true, + ], + ]]; + + $object = DataObject::getById($payload->sourceId) ?? throw new DataObjectNotFoundException($payload->sourceId); + $childIds = []; + + if ($object->hasChildren(DataObject::$types)) { + $list = new DataObject\Listing(); + $list->setCondition('`path` LIKE ' . $list->quote($list->escapeLike($object->getRealFullPath()) . '/%')); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('ASC'); + $list->setObjectTypes(DataObject::$types); + $childIds = $list->loadIdList(); + } + + foreach ($childIds as $id) { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_dataobject_dataobject_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $id, + 'targetParentId' => $payload->targetId, + 'sourceParentId' => $payload->sourceId, + 'type' => 'child', + 'transactionId' => $transactionId, + ], + ]]; + } + + if ($payload->type === 'recursive-update-references' && count($childIds) > 0) { + for ($i = 0; $i < (count($childIds) + 1); $i++) { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_dataobject_dataobject_copyrewriteids'), + 'method' => 'PUT', + 'params' => [ + 'transactionId' => $transactionId, + '_dc' => uniqid('', false), + ], + ]]; + } + } + } elseif ($payload->type === 'child' || $payload->type === 'replace') { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_dataobject_dataobject_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $payload->sourceId, + 'targetId' => $payload->targetId, + 'type' => $payload->type, + 'transactionId' => $transactionId, + ], + ]]; + } + + return new CopyInfoResult($transactionId, $pasteJobs); + } +} diff --git a/src/Handler/DataObject/Copy/CopyInfo/CopyInfoPayload.php b/src/Handler/DataObject/Copy/CopyInfo/CopyInfoPayload.php new file mode 100644 index 00000000..e7c9c828 --- /dev/null +++ b/src/Handler/DataObject/Copy/CopyInfo/CopyInfoPayload.php @@ -0,0 +1,39 @@ +query->getString('type') ?: null, + sourceId: $request->query->getInt('sourceId'), + targetId: $request->query->has('targetId') ? $request->query->getInt('targetId') : null, + ); + } +} diff --git a/src/Handler/DataObject/Copy/CopyInfo/CopyInfoResult.php b/src/Handler/DataObject/Copy/CopyInfo/CopyInfoResult.php new file mode 100644 index 00000000..89cad16f --- /dev/null +++ b/src/Handler/DataObject/Copy/CopyInfo/CopyInfoResult.php @@ -0,0 +1,26 @@ +sourceId) ?? throw new DataObjectNotFoundException($payload->sourceId); - - if (!$object->hasChildren(DataObject::$types)) { - return new GetDataObjectChildIdsResult([]); - } - - $list = new DataObject\Listing(); - $list->setCondition('`path` LIKE ' . $list->quote($list->escapeLike($object->getRealFullPath()) . '/%')); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('ASC'); - $list->setObjectTypes(DataObject::$types); - - return new GetDataObjectChildIdsResult($list->loadIdList()); - } -} diff --git a/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/AddCustomLayout/AddCustomLayoutHandler.php similarity index 98% rename from src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php rename to src/Handler/DataObject/CustomLayout/AddCustomLayout/AddCustomLayoutHandler.php index ec15ff7f..528eab1d 100644 --- a/src/Handler/DataObject/CustomLayout/AddCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/AddCustomLayout/AddCustomLayoutHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\AddCustomLayout; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\AddCustomLayout\AddCustomLayoutPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; diff --git a/src/Handler/DataObject/CustomLayout/DeleteCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/DeleteCustomLayout/DeleteCustomLayoutHandler.php similarity index 98% rename from src/Handler/DataObject/CustomLayout/DeleteCustomLayoutHandler.php rename to src/Handler/DataObject/CustomLayout/DeleteCustomLayout/DeleteCustomLayoutHandler.php index 9815710d..b9061ff1 100644 --- a/src/Handler/DataObject/CustomLayout/DeleteCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/DeleteCustomLayout/DeleteCustomLayoutHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\DeleteCustomLayout; use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutHandler.php similarity index 98% rename from src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php rename to src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutHandler.php index 6351b0ee..40a035bb 100644 --- a/src/Handler/DataObject/CustomLayout/ExportCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayout; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayout\ExportCustomLayoutPayload; use OpenDxp\Logger; diff --git a/src/Handler/DataObject/CustomLayout/ExportCustomLayoutResult.php b/src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutResult.php similarity index 96% rename from src/Handler/DataObject/CustomLayout/ExportCustomLayoutResult.php rename to src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutResult.php index 4b81981c..f12fbce7 100644 --- a/src/Handler/DataObject/CustomLayout/ExportCustomLayoutResult.php +++ b/src/Handler/DataObject/CustomLayout/ExportCustomLayout/ExportCustomLayoutResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ExportCustomLayout; final readonly class ExportCustomLayoutResult { diff --git a/src/Handler/DataObject/CustomLayout/AllLayoutsResult.php b/src/Handler/DataObject/CustomLayout/GetAllLayouts/AllLayoutsResult.php similarity index 97% rename from src/Handler/DataObject/CustomLayout/AllLayoutsResult.php rename to src/Handler/DataObject/CustomLayout/GetAllLayouts/AllLayoutsResult.php index 5b9215f6..3b37c720 100644 --- a/src/Handler/DataObject/CustomLayout/AllLayoutsResult.php +++ b/src/Handler/DataObject/CustomLayout/GetAllLayouts/AllLayoutsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetAllLayouts; final readonly class AllLayoutsResult { diff --git a/src/Handler/DataObject/CustomLayout/GetAllLayoutsHandler.php b/src/Handler/DataObject/CustomLayout/GetAllLayouts/GetAllLayoutsHandler.php similarity index 99% rename from src/Handler/DataObject/CustomLayout/GetAllLayoutsHandler.php rename to src/Handler/DataObject/CustomLayout/GetAllLayouts/GetAllLayoutsHandler.php index 069e9d10..3243c7c2 100644 --- a/src/Handler/DataObject/CustomLayout/GetAllLayoutsHandler.php +++ b/src/Handler/DataObject/CustomLayout/GetAllLayouts/GetAllLayoutsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetAllLayouts; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutHandler.php similarity index 99% rename from src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php rename to src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutHandler.php index 80469bea..ec91f4a9 100644 --- a/src/Handler/DataObject/CustomLayout/GetCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayout; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayout\GetCustomLayoutPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutResult.php b/src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutResult.php similarity index 97% rename from src/Handler/DataObject/CustomLayout/GetCustomLayoutResult.php rename to src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutResult.php index 308470e3..8aad38de 100644 --- a/src/Handler/DataObject/CustomLayout/GetCustomLayoutResult.php +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayout/GetCustomLayoutResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayout; final readonly class GetCustomLayoutResult { diff --git a/src/Handler/DataObject/CustomLayout/CustomLayoutDefinitionsResult.php b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/CustomLayoutDefinitionsResult.php similarity index 95% rename from src/Handler/DataObject/CustomLayout/CustomLayoutDefinitionsResult.php rename to src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/CustomLayoutDefinitionsResult.php index 2f7dbe6c..343f6103 100644 --- a/src/Handler/DataObject/CustomLayout/CustomLayoutDefinitionsResult.php +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/CustomLayoutDefinitionsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitions; final readonly class CustomLayoutDefinitionsResult { diff --git a/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/GetCustomLayoutDefinitionsHandler.php similarity index 97% rename from src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php rename to src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/GetCustomLayoutDefinitionsHandler.php index 66ab56ca..743af7ca 100644 --- a/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitionsHandler.php +++ b/src/Handler/DataObject/CustomLayout/GetCustomLayoutDefinitions/GetCustomLayoutDefinitionsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitions; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\GetCustomLayoutDefinitions\GetCustomLayoutDefinitionsPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/CustomLayout/ImportCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/ImportCustomLayout/ImportCustomLayoutHandler.php similarity index 98% rename from src/Handler/DataObject/CustomLayout/ImportCustomLayoutHandler.php rename to src/Handler/DataObject/CustomLayout/ImportCustomLayout/ImportCustomLayoutHandler.php index bc8b6506..4d06f80e 100644 --- a/src/Handler/DataObject/CustomLayout/ImportCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/ImportCustomLayout/ImportCustomLayoutHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ImportCustomLayout; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\ImportCustomLayout\ImportCustomLayoutPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php b/src/Handler/DataObject/CustomLayout/SaveCustomLayout/SaveCustomLayoutHandler.php similarity index 98% rename from src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php rename to src/Handler/DataObject/CustomLayout/SaveCustomLayout/SaveCustomLayoutHandler.php index 30eb77f5..73e89a4d 100644 --- a/src/Handler/DataObject/CustomLayout/SaveCustomLayoutHandler.php +++ b/src/Handler/DataObject/CustomLayout/SaveCustomLayout/SaveCustomLayoutHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SaveCustomLayout; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SaveCustomLayout\SaveCustomLayoutPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierHandler.php similarity index 97% rename from src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php rename to src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierHandler.php index ecfb6392..1faade98 100644 --- a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierHandler.php +++ b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier\SuggestCustomLayoutIdentifierPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierResult.php b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierResult.php similarity index 95% rename from src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierResult.php rename to src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierResult.php index c64cdce9..e25704a6 100644 --- a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifierResult.php +++ b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier; final readonly class SuggestCustomLayoutIdentifierResult { diff --git a/src/Handler/DataObject/DataObjectGridProxyHandler.php b/src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyHandler.php similarity index 92% rename from src/Handler/DataObject/DataObjectGridProxyHandler.php rename to src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyHandler.php index 58407236..2441b238 100644 --- a/src/Handler/DataObject/DataObjectGridProxyHandler.php +++ b/src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyHandler.php @@ -15,10 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\DataObjectGridProxy; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DataObjectGridProxyPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService; use OpenDxp\Model\DataObject; use Symfony\Component\EventDispatcher\GenericEvent; diff --git a/src/Payload/DataObject/DataObjectGridProxyPayload.php b/src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyPayload.php similarity index 94% rename from src/Payload/DataObject/DataObjectGridProxyPayload.php rename to src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyPayload.php index a1b8a925..d7700d42 100644 --- a/src/Payload/DataObject/DataObjectGridProxyPayload.php +++ b/src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\DataObjectGridProxy; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/DataObjectGridProxyResult.php b/src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyResult.php similarity index 89% rename from src/Handler/DataObject/DataObjectGridProxyResult.php rename to src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyResult.php index 1f1d28c1..f02472ca 100644 --- a/src/Handler/DataObject/DataObjectGridProxyResult.php +++ b/src/Handler/DataObject/DataObjectGridProxy/DataObjectGridProxyResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\DataObjectGridProxy; final readonly class DataObjectGridProxyResult { diff --git a/src/Handler/DataObject/DeleteDataObjectHandler.php b/src/Handler/DataObject/DeleteDataObject/DeleteDataObjectHandler.php similarity index 94% rename from src/Handler/DataObject/DeleteDataObjectHandler.php rename to src/Handler/DataObject/DeleteDataObject/DeleteDataObjectHandler.php index 4a50d2b0..efddf7fc 100644 --- a/src/Handler/DataObject/DeleteDataObjectHandler.php +++ b/src/Handler/DataObject/DeleteDataObject/DeleteDataObjectHandler.php @@ -15,9 +15,8 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\DeleteDataObject; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\DeleteDataObjectPayload; use OpenDxp\Model\DataObject; use RuntimeException; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; diff --git a/src/Payload/DataObject/DeleteDataObjectPayload.php b/src/Handler/DataObject/DeleteDataObject/DeleteDataObjectPayload.php similarity index 93% rename from src/Payload/DataObject/DeleteDataObjectPayload.php rename to src/Handler/DataObject/DeleteDataObject/DeleteDataObjectPayload.php index 09799da6..72b4c372 100644 --- a/src/Payload/DataObject/DeleteDataObjectPayload.php +++ b/src/Handler/DataObject/DeleteDataObject/DeleteDataObjectPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\DeleteDataObject; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/DeleteDataObjectResult.php b/src/Handler/DataObject/DeleteDataObject/DeleteDataObjectResult.php similarity index 89% rename from src/Handler/DataObject/DeleteDataObjectResult.php rename to src/Handler/DataObject/DeleteDataObject/DeleteDataObjectResult.php index 89ee950c..e83f5f3b 100644 --- a/src/Handler/DataObject/DeleteDataObjectResult.php +++ b/src/Handler/DataObject/DeleteDataObject/DeleteDataObjectResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\DeleteDataObject; final readonly class DeleteDataObjectResult { diff --git a/src/Handler/DataObject/FieldCollection/DeleteFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/DeleteFieldCollection/DeleteFieldCollectionHandler.php similarity index 96% rename from src/Handler/DataObject/FieldCollection/DeleteFieldCollectionHandler.php rename to src/Handler/DataObject/FieldCollection/DeleteFieldCollection/DeleteFieldCollectionHandler.php index b2fbf4ff..ed062a56 100644 --- a/src/Handler/DataObject/FieldCollection/DeleteFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/DeleteFieldCollection/DeleteFieldCollectionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\DeleteFieldCollection; use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionHandler.php similarity index 98% rename from src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php rename to src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionHandler.php index bf7c31f0..b712bd05 100644 --- a/src/Handler/DataObject/FieldCollection/ExportFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollection; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollection\ExportFieldCollectionPayload; use OpenDxp\Logger; diff --git a/src/Handler/DataObject/FieldCollection/ExportFieldCollectionResult.php b/src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionResult.php similarity index 95% rename from src/Handler/DataObject/FieldCollection/ExportFieldCollectionResult.php rename to src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionResult.php index 0b8766b7..e60dbead 100644 --- a/src/Handler/DataObject/FieldCollection/ExportFieldCollectionResult.php +++ b/src/Handler/DataObject/FieldCollection/ExportFieldCollection/ExportFieldCollectionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ExportFieldCollection; final readonly class ExportFieldCollectionResult { diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionHandler.php similarity index 97% rename from src/Handler/DataObject/FieldCollection/GetFieldCollectionHandler.php rename to src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionHandler.php index 72914105..75a0a0e1 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollection; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollection\GetFieldCollectionPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionResult.php b/src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionResult.php similarity index 96% rename from src/Handler/DataObject/FieldCollection/GetFieldCollectionResult.php rename to src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionResult.php index 0e669567..b01be6e2 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionResult.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollection/GetFieldCollectionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollection; final readonly class GetFieldCollectionResult { diff --git a/src/Handler/DataObject/FieldCollection/FieldCollectionListResult.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionList/FieldCollectionListResult.php similarity index 95% rename from src/Handler/DataObject/FieldCollection/FieldCollectionListResult.php rename to src/Handler/DataObject/FieldCollection/GetFieldCollectionList/FieldCollectionListResult.php index ef180263..29c03f56 100644 --- a/src/Handler/DataObject/FieldCollection/FieldCollectionListResult.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionList/FieldCollectionListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionList; final readonly class FieldCollectionListResult { diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionList/GetFieldCollectionListHandler.php similarity index 98% rename from src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php rename to src/Handler/DataObject/FieldCollection/GetFieldCollectionList/GetFieldCollectionListHandler.php index ee6a8372..4b3bd890 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionListHandler.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionList/GetFieldCollectionListHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionList; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionList\GetFieldCollectionListPayload; diff --git a/src/Handler/DataObject/FieldCollection/FieldCollectionTreeResult.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/FieldCollectionTreeResult.php similarity index 95% rename from src/Handler/DataObject/FieldCollection/FieldCollectionTreeResult.php rename to src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/FieldCollectionTreeResult.php index 139847f2..e14e7930 100644 --- a/src/Handler/DataObject/FieldCollection/FieldCollectionTreeResult.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/FieldCollectionTreeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTree; final readonly class FieldCollectionTreeResult { diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionTreeHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/GetFieldCollectionTreeHandler.php similarity index 99% rename from src/Handler/DataObject/FieldCollection/GetFieldCollectionTreeHandler.php rename to src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/GetFieldCollectionTreeHandler.php index 3f84e76c..5126c6b9 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionTreeHandler.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionTree/GetFieldCollectionTreeHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTree; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionTree\GetFieldCollectionTreePayload; diff --git a/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php b/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsages/GetFieldCollectionUsagesHandler.php similarity index 97% rename from src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php rename to src/Handler/DataObject/FieldCollection/GetFieldCollectionUsages/GetFieldCollectionUsagesHandler.php index 1f4eea9b..6c09fea9 100644 --- a/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsagesHandler.php +++ b/src/Handler/DataObject/FieldCollection/GetFieldCollectionUsages/GetFieldCollectionUsagesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionUsages; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\GetFieldCollectionUsages\GetFieldCollectionUsagesPayload; use OpenDxp\Model\DataObject\ClassDefinition\Data\Fieldcollections; diff --git a/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/ImportFieldCollection/ImportFieldCollectionHandler.php similarity index 97% rename from src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php rename to src/Handler/DataObject/FieldCollection/ImportFieldCollection/ImportFieldCollectionHandler.php index f727ef81..20f6ed65 100644 --- a/src/Handler/DataObject/FieldCollection/ImportFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/ImportFieldCollection/ImportFieldCollectionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ImportFieldCollection; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\ImportFieldCollection\ImportFieldCollectionPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/FieldCollection/UpdateFieldCollectionHandler.php b/src/Handler/DataObject/FieldCollection/UpdateFieldCollection/UpdateFieldCollectionHandler.php similarity index 98% rename from src/Handler/DataObject/FieldCollection/UpdateFieldCollectionHandler.php rename to src/Handler/DataObject/FieldCollection/UpdateFieldCollection/UpdateFieldCollectionHandler.php index 93120f40..4418043e 100644 --- a/src/Handler/DataObject/FieldCollection/UpdateFieldCollectionHandler.php +++ b/src/Handler/DataObject/FieldCollection/UpdateFieldCollection/UpdateFieldCollectionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\UpdateFieldCollection; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\FieldCollection\UpdateFieldCollection\UpdateFieldCollectionPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/GetDataObjectHandler.php b/src/Handler/DataObject/GetDataObject/GetDataObjectHandler.php similarity index 95% rename from src/Handler/DataObject/GetDataObjectHandler.php rename to src/Handler/DataObject/GetDataObject/GetDataObjectHandler.php index a446a2e1..8deb3159 100644 --- a/src/Handler/DataObject/GetDataObjectHandler.php +++ b/src/Handler/DataObject/GetDataObject/GetDataObjectHandler.php @@ -15,14 +15,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObject; +use OpenDxp\Bundle\AdminBundle\Enricher\DataObject\CustomLayoutEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\DataObject\DraftEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Helper\DataObjectVersionHelper; use OpenDxp\Bundle\AdminBundle\Model\DataObject\DataObjectLoadContext; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; -use OpenDxp\Model; use OpenDxp\Model\DataObject; use OpenDxp\Model\DataObject\ClassDefinition\Data\ManyToManyObjectRelation; use OpenDxp\Model\DataObject\ClassDefinition\Data\Relations\AbstractRelations; @@ -36,7 +38,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Throwable; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\GetDataObjectPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetDataObjectHandler @@ -45,7 +46,10 @@ public function __construct( private readonly AdminUserContextInterface $userContext, private readonly PreviewGeneratorInterface $defaultPreviewGenerator, private readonly EditLockService $editLockService, - private readonly ElementResponseNormalizer $normalizer, + private readonly AdminStyleEnricher $adminStyleEnricher, + private readonly UserNamesEnricher $userNamesEnricher, + private readonly CustomLayoutEnricher $customLayoutEnricher, + private readonly DraftEnricher $draftEnricher, private readonly EventDispatcherInterface $eventDispatcher, ) {} @@ -235,7 +239,10 @@ public function __invoke(GetDataObjectPayload $payload): GetDataObjectResult $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_GET_PRE_SEND_DATA); $objectData = $event->getArgument('data'); - $this->normalizer->normalize($object, $objectData, self::class, ['draftVersion' => $draftVersion]); + $this->adminStyleEnricher->forEditor($object, $objectData['general']); + $this->userNamesEnricher->enrich($object, $objectData['general']); + $this->customLayoutEnricher->enrich($object, $objectData); + $this->draftEnricher->enrich($object, $objectData, $draftVersion); return new GetDataObjectResult(data: $objectData); } diff --git a/src/Payload/DataObject/GetDataObjectPayload.php b/src/Handler/DataObject/GetDataObject/GetDataObjectPayload.php similarity index 93% rename from src/Payload/DataObject/GetDataObjectPayload.php rename to src/Handler/DataObject/GetDataObject/GetDataObjectPayload.php index 02afaeca..03ffc476 100644 --- a/src/Payload/DataObject/GetDataObjectPayload.php +++ b/src/Handler/DataObject/GetDataObject/GetDataObjectPayload.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObject; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/GetDataObjectResult.php b/src/Handler/DataObject/GetDataObject/GetDataObjectResult.php similarity index 89% rename from src/Handler/DataObject/GetDataObjectResult.php rename to src/Handler/DataObject/GetDataObject/GetDataObjectResult.php index 443bf982..47706f36 100644 --- a/src/Handler/DataObject/GetDataObjectResult.php +++ b/src/Handler/DataObject/GetDataObject/GetDataObjectResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObject; final readonly class GetDataObjectResult { diff --git a/src/Handler/DataObject/GetDataObjectChildrenHandler.php b/src/Handler/DataObject/GetDataObjectChildren/GetDataObjectChildrenHandler.php similarity index 94% rename from src/Handler/DataObject/GetDataObjectChildrenHandler.php rename to src/Handler/DataObject/GetDataObjectChildren/GetDataObjectChildrenHandler.php index 8ab07818..ef1db255 100644 --- a/src/Handler/DataObject/GetDataObjectChildrenHandler.php +++ b/src/Handler/DataObject/GetDataObjectChildren/GetDataObjectChildrenHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectChildren; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectChildrenResult; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Model\DataObject; use OpenDxp\Model\User; diff --git a/src/Handler/DataObject/GetDataObjectFolderHandler.php b/src/Handler/DataObject/GetDataObjectFolder/GetDataObjectFolderHandler.php similarity index 92% rename from src/Handler/DataObject/GetDataObjectFolderHandler.php rename to src/Handler/DataObject/GetDataObjectFolder/GetDataObjectFolderHandler.php index 9ca5af90..39b87dce 100644 --- a/src/Handler/DataObject/GetDataObjectFolderHandler.php +++ b/src/Handler/DataObject/GetDataObjectFolder/GetDataObjectFolderHandler.php @@ -15,10 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectFolder; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; @@ -32,7 +32,7 @@ final class GetDataObjectFolderHandler { public function __construct( private readonly AdminUserContextInterface $userContext, - private readonly ElementResponseNormalizer $normalizer, + private readonly UserNamesEnricher $userNamesEnricher, private readonly EventDispatcherInterface $eventDispatcher, ) {} @@ -67,7 +67,7 @@ public function __invoke(IdQueryPayload $payload): GetDataObjectFolderResult $objectData['userPermissions'] = $object->getUserPermissions($adminUser); $objectData['classes'] = $this->prepareChildClasses($object->getDao()->getClasses()); - $this->normalizer->normalize($object, $objectData, self::class); + $this->userNamesEnricher->enrich($object, $objectData['general']); $event = new GenericEvent($this, ['data' => $objectData, 'object' => $object]); $this->eventDispatcher->dispatch($event, AdminEvents::OBJECT_GET_PRE_SEND_DATA); diff --git a/src/Handler/DataObject/GetDataObjectFolderResult.php b/src/Handler/DataObject/GetDataObjectFolder/GetDataObjectFolderResult.php similarity index 89% rename from src/Handler/DataObject/GetDataObjectFolderResult.php rename to src/Handler/DataObject/GetDataObjectFolder/GetDataObjectFolderResult.php index 64da507c..66c65198 100644 --- a/src/Handler/DataObject/GetDataObjectFolderResult.php +++ b/src/Handler/DataObject/GetDataObjectFolder/GetDataObjectFolderResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectFolder; final readonly class GetDataObjectFolderResult { diff --git a/src/Handler/DataObject/GetDataObjectPreviewUrlHandler.php b/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlHandler.php similarity index 79% rename from src/Handler/DataObject/GetDataObjectPreviewUrlHandler.php rename to src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlHandler.php index 578721cc..0553b205 100644 --- a/src/Handler/DataObject/GetDataObjectPreviewUrlHandler.php +++ b/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlHandler.php @@ -15,9 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrl; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrl\GetDataObjectPreviewUrlPayload; +use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; +use OpenDxp\Model\DataObject; use OpenDxp\Model\DataObject\ClassDefinition\PreviewGeneratorInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -25,11 +26,17 @@ final class GetDataObjectPreviewUrlHandler { public function __construct( private readonly PreviewGeneratorInterface $defaultPreviewGenerator, + private readonly SessionService $sessionService, ) {} public function __invoke(GetDataObjectPreviewUrlPayload $payload): string { - $object = $payload->object; + $object = $this->sessionService->getObject('object', $payload->id); + + if (!$object instanceof DataObject\Concrete) { + throw new NotFoundHttpException(sprintf('Expected an object of type "%s", got "%s"', DataObject\Concrete::class, get_debug_type($object))); + } + $queryParams = $payload->queryParams; $url = null; if ($previewService = $object->getClass()->getPreviewGenerator()) { diff --git a/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlPayload.php b/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlPayload.php index 4af7197c..d8bb915a 100644 --- a/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlPayload.php +++ b/src/Handler/DataObject/GetDataObjectPreviewUrl/GetDataObjectPreviewUrlPayload.php @@ -17,12 +17,21 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectPreviewUrl; -use OpenDxp\Model\DataObject\Concrete; +use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; +use Symfony\Component\HttpFoundation\Request; -final readonly class GetDataObjectPreviewUrlPayload +final readonly class GetDataObjectPreviewUrlPayload implements ExtJsPayloadInterface { public function __construct( - public readonly Concrete $object, + public readonly int $id = 0, public readonly array $queryParams = [], ) {} + + public static function fromRequest(Request $request): static + { + return new static( + id: $request->query->getInt('id'), + queryParams: $request->query->all(), + ); + } } diff --git a/src/Handler/DataObject/GetIdPathPagingInfoHandler.php b/src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoHandler.php similarity index 95% rename from src/Handler/DataObject/GetIdPathPagingInfoHandler.php rename to src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoHandler.php index f5b24dbe..a461ca6b 100644 --- a/src/Handler/DataObject/GetIdPathPagingInfoHandler.php +++ b/src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfo; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfo\GetIdPathPagingInfoPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/GetIdPathPagingInfoResult.php b/src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoResult.php similarity index 89% rename from src/Handler/DataObject/GetIdPathPagingInfoResult.php rename to src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoResult.php index 2578e0d2..c2b3efdd 100644 --- a/src/Handler/DataObject/GetIdPathPagingInfoResult.php +++ b/src/Handler/DataObject/GetIdPathPagingInfo/GetIdPathPagingInfoResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetIdPathPagingInfo; final readonly class GetIdPathPagingInfoResult { diff --git a/src/Handler/DataObject/GetSelectOptionsHandler.php b/src/Handler/DataObject/GetSelectOptions/GetSelectOptionsHandler.php similarity index 94% rename from src/Handler/DataObject/GetSelectOptionsHandler.php rename to src/Handler/DataObject/GetSelectOptions/GetSelectOptionsHandler.php index c4a655cd..b4c12b05 100644 --- a/src/Handler/DataObject/GetSelectOptionsHandler.php +++ b/src/Handler/DataObject/GetSelectOptions/GetSelectOptionsHandler.php @@ -15,9 +15,8 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetSelectOptions; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\GetSelectOptionsPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPayloadMapper; use OpenDxp\Model\DataObject; use OpenDxp\Model\DataObject\ClassDefinition\Helper\OptionsProviderResolver; diff --git a/src/Payload/DataObject/GetSelectOptionsPayload.php b/src/Handler/DataObject/GetSelectOptions/GetSelectOptionsPayload.php similarity index 94% rename from src/Payload/DataObject/GetSelectOptionsPayload.php rename to src/Handler/DataObject/GetSelectOptions/GetSelectOptionsPayload.php index 14985d66..a59fad48 100644 --- a/src/Payload/DataObject/GetSelectOptionsPayload.php +++ b/src/Handler/DataObject/GetSelectOptions/GetSelectOptionsPayload.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetSelectOptions; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php b/src/Handler/DataObject/Helper/ApplyGridConfigToAll/ApplyGridConfigToAllHandler.php similarity index 94% rename from src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php rename to src/Handler/DataObject/Helper/ApplyGridConfigToAll/ApplyGridConfigToAllHandler.php index ed0b0c3a..094778a3 100644 --- a/src/Handler/DataObject/Helper/ApplyGridConfigToAllHandler.php +++ b/src/Handler/DataObject/Helper/ApplyGridConfigToAll/ApplyGridConfigToAllHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAll; use OpenDxp\Db; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/Helper/ApplyGridConfigToAllPayload.php b/src/Handler/DataObject/Helper/ApplyGridConfigToAll/ApplyGridConfigToAllPayload.php similarity index 93% rename from src/Handler/DataObject/Helper/ApplyGridConfigToAllPayload.php rename to src/Handler/DataObject/Helper/ApplyGridConfigToAll/ApplyGridConfigToAllPayload.php index af0263d1..05ef527e 100644 --- a/src/Handler/DataObject/Helper/ApplyGridConfigToAllPayload.php +++ b/src/Handler/DataObject/Helper/ApplyGridConfigToAll/ApplyGridConfigToAllPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ApplyGridConfigToAll; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php deleted file mode 100644 index dbc24879..00000000 --- a/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigHandler.php +++ /dev/null @@ -1,46 +0,0 @@ -userContext->getAdminUser(); - $gridConfig = GridConfig::getById($payload->gridConfigId); - if (!$gridConfig) { - throw new NotFoundHttpException('Grid config not found: ' . $gridConfigId); - } - - if ($gridConfig->getOwnerId() !== $adminUser->getId() && !$adminUser->isAdmin()) { - throw new BadRequestHttpException("don't mess with someone elses grid config"); - } - - $gridConfig->delete(); - } -} diff --git a/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigHandler.php index f0632a63..6f0aa698 100644 --- a/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigHandler.php +++ b/src/Handler/DataObject/Helper/DeleteGridColumnConfig/DeleteGridColumnConfigHandler.php @@ -18,10 +18,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DeleteGridColumnConfig; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; +use OpenDxp\Bundle\AdminBundle\Model\GridConfig; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Grid\DataObjectGridColumnConfigResolver; use OpenDxp\Config; use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; final class DeleteGridColumnConfigHandler @@ -31,10 +35,23 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, private readonly Config $config, private readonly RequestStack $requestStack, + private readonly AdminUserContextInterface $userContext, ) {} public function __invoke(DeleteGridColumnConfigPayload $payload): array { + if ($payload->gridConfigId !== null) { + $adminUser = $this->userContext->getAdminUser(); + $gridConfig = GridConfig::getById($payload->gridConfigId); + if (!$gridConfig) { + throw new NotFoundHttpException('Grid config not found: ' . $payload->gridConfigId); + } + if ($gridConfig->getOwnerId() !== $adminUser->getId() && !$adminUser->isAdmin()) { + throw new BadRequestHttpException("don't mess with someone elses grid config"); + } + $gridConfig->delete(); + } + $params = [ 'id' => $payload->id, 'objectId' => $payload->objectId, diff --git a/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php b/src/Handler/DataObject/Helper/DoDataObjectExport/DoDataObjectExportHandler.php similarity index 98% rename from src/Handler/DataObject/Helper/DoDataObjectExportHandler.php rename to src/Handler/DataObject/Helper/DoDataObjectExport/DoDataObjectExportHandler.php index 1a192489..6dddf987 100644 --- a/src/Handler/DataObject/Helper/DoDataObjectExportHandler.php +++ b/src/Handler/DataObject/Helper/DoDataObjectExport/DoDataObjectExportHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExport; use InvalidArgumentException; use League\Flysystem\UnableToReadFile; diff --git a/src/Handler/DataObject/Helper/DoDataObjectExportPayload.php b/src/Handler/DataObject/Helper/DoDataObjectExport/DoDataObjectExportPayload.php similarity index 96% rename from src/Handler/DataObject/Helper/DoDataObjectExportPayload.php rename to src/Handler/DataObject/Helper/DoDataObjectExport/DoDataObjectExportPayload.php index 77a753aa..e7549ae9 100644 --- a/src/Handler/DataObject/Helper/DoDataObjectExportPayload.php +++ b/src/Handler/DataObject/Helper/DoDataObjectExport/DoDataObjectExportPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\DoDataObjectExport; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use OpenDxp\File; diff --git a/src/Handler/DataObject/Helper/ExecuteBatchHandler.php b/src/Handler/DataObject/Helper/ExecuteBatch/ExecuteBatchHandler.php similarity index 93% rename from src/Handler/DataObject/Helper/ExecuteBatchHandler.php rename to src/Handler/DataObject/Helper/ExecuteBatch/ExecuteBatchHandler.php index 411675bd..ad6da056 100644 --- a/src/Handler/DataObject/Helper/ExecuteBatchHandler.php +++ b/src/Handler/DataObject/Helper/ExecuteBatch/ExecuteBatchHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatch; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService; diff --git a/src/Handler/DataObject/Helper/ExecuteBatchPayload.php b/src/Handler/DataObject/Helper/ExecuteBatch/ExecuteBatchPayload.php similarity index 94% rename from src/Handler/DataObject/Helper/ExecuteBatchPayload.php rename to src/Handler/DataObject/Helper/ExecuteBatch/ExecuteBatchPayload.php index 18312ac5..d1bf156c 100644 --- a/src/Handler/DataObject/Helper/ExecuteBatchPayload.php +++ b/src/Handler/DataObject/Helper/ExecuteBatch/ExecuteBatchPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ExecuteBatch; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php b/src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsHandler.php similarity index 97% rename from src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php rename to src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsHandler.php index 42041473..3a271795 100644 --- a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsHandler.php +++ b/src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFields; use OpenDxp\Bundle\AdminBundle\Service\Grid\DataObjectGridColumnConfigResolver; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsPayload.php b/src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsPayload.php similarity index 91% rename from src/Handler/DataObject/Helper/GetAvailableVisibleFieldsPayload.php rename to src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsPayload.php index e7b0532d..9a3c8d96 100644 --- a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsPayload.php +++ b/src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFields; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsResult.php b/src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsResult.php similarity index 87% rename from src/Handler/DataObject/Helper/GetAvailableVisibleFieldsResult.php rename to src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsResult.php index d2bb84d2..0a0e2c85 100644 --- a/src/Handler/DataObject/Helper/GetAvailableVisibleFieldsResult.php +++ b/src/Handler/DataObject/Helper/GetAvailableVisibleFields/GetAvailableVisibleFieldsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetAvailableVisibleFields; final readonly class GetAvailableVisibleFieldsResult { diff --git a/src/Handler/DataObject/Helper/GetBatchJobsHandler.php b/src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsHandler.php similarity index 93% rename from src/Handler/DataObject/Helper/GetBatchJobsHandler.php rename to src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsHandler.php index 0d8a6838..061f6569 100644 --- a/src/Handler/DataObject/Helper/GetBatchJobsHandler.php +++ b/src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobs; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService; diff --git a/src/Handler/DataObject/Helper/GetBatchJobsPayload.php b/src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsPayload.php similarity index 93% rename from src/Handler/DataObject/Helper/GetBatchJobsPayload.php rename to src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsPayload.php index 235ce33b..a28314a6 100644 --- a/src/Handler/DataObject/Helper/GetBatchJobsPayload.php +++ b/src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobs; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/GetBatchJobsResult.php b/src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsResult.php similarity index 89% rename from src/Handler/DataObject/Helper/GetBatchJobsResult.php rename to src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsResult.php index 65ee0bbf..7935fd01 100644 --- a/src/Handler/DataObject/Helper/GetBatchJobsResult.php +++ b/src/Handler/DataObject/Helper/GetBatchJobs/GetBatchJobsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetBatchJobs; final readonly class GetBatchJobsResult { diff --git a/src/Handler/DataObject/Helper/GetExportConfigsHandler.php b/src/Handler/DataObject/Helper/GetExportConfigs/GetExportConfigsHandler.php similarity index 94% rename from src/Handler/DataObject/Helper/GetExportConfigsHandler.php rename to src/Handler/DataObject/Helper/GetExportConfigs/GetExportConfigsHandler.php index 478d1e77..6823c0b2 100644 --- a/src/Handler/DataObject/Helper/GetExportConfigsHandler.php +++ b/src/Handler/DataObject/Helper/GetExportConfigs/GetExportConfigsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigs; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridColumnConfigService; diff --git a/src/Handler/DataObject/Helper/GetExportConfigsPayload.php b/src/Handler/DataObject/Helper/GetExportConfigs/GetExportConfigsPayload.php similarity index 92% rename from src/Handler/DataObject/Helper/GetExportConfigsPayload.php rename to src/Handler/DataObject/Helper/GetExportConfigs/GetExportConfigsPayload.php index fcc0f06c..c8993cba 100644 --- a/src/Handler/DataObject/Helper/GetExportConfigsPayload.php +++ b/src/Handler/DataObject/Helper/GetExportConfigs/GetExportConfigsPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportConfigs; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/GetExportJobsHandler.php b/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsHandler.php similarity index 96% rename from src/Handler/DataObject/Helper/GetExportJobsHandler.php rename to src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsHandler.php index 7a279946..a7258585 100644 --- a/src/Handler/DataObject/Helper/GetExportJobsHandler.php +++ b/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobs; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Helper\GridHelperService; diff --git a/src/Handler/DataObject/Helper/GetExportJobsPayload.php b/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsPayload.php similarity index 93% rename from src/Handler/DataObject/Helper/GetExportJobsPayload.php rename to src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsPayload.php index cd75a725..23bb21a7 100644 --- a/src/Handler/DataObject/Helper/GetExportJobsPayload.php +++ b/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobs; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/GetExportJobsResult.php b/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsResult.php similarity index 89% rename from src/Handler/DataObject/Helper/GetExportJobsResult.php rename to src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsResult.php index 4febe800..e4ec3bd6 100644 --- a/src/Handler/DataObject/Helper/GetExportJobsResult.php +++ b/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\GetExportJobs; final readonly class GetExportJobsResult { diff --git a/src/Handler/DataObject/Helper/ImportUploadHandler.php b/src/Handler/DataObject/Helper/ImportUpload/ImportUploadHandler.php similarity index 93% rename from src/Handler/DataObject/Helper/ImportUploadHandler.php rename to src/Handler/DataObject/Helper/ImportUpload/ImportUploadHandler.php index bd667045..cbba6cf6 100644 --- a/src/Handler/DataObject/Helper/ImportUploadHandler.php +++ b/src/Handler/DataObject/Helper/ImportUpload/ImportUploadHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUpload; use OpenDxp\Tool\Text; use Symfony\Component\Filesystem\Filesystem; diff --git a/src/Handler/DataObject/Helper/ImportUploadPayload.php b/src/Handler/DataObject/Helper/ImportUpload/ImportUploadPayload.php similarity index 94% rename from src/Handler/DataObject/Helper/ImportUploadPayload.php rename to src/Handler/DataObject/Helper/ImportUpload/ImportUploadPayload.php index 6a515d2c..135776d8 100644 --- a/src/Handler/DataObject/Helper/ImportUploadPayload.php +++ b/src/Handler/DataObject/Helper/ImportUpload/ImportUploadPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\ImportUpload; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\File\UploadedFile; diff --git a/src/Handler/DataObject/Helper/LoadObjectDataHandler.php b/src/Handler/DataObject/Helper/LoadObjectData/LoadObjectDataHandler.php similarity index 92% rename from src/Handler/DataObject/Helper/LoadObjectDataHandler.php rename to src/Handler/DataObject/Helper/LoadObjectData/LoadObjectDataHandler.php index ef681364..5043d936 100644 --- a/src/Handler/DataObject/Helper/LoadObjectDataHandler.php +++ b/src/Handler/DataObject/Helper/LoadObjectData/LoadObjectDataHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectData; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; use OpenDxp\Bundle\AdminBundle\Service\GridData; diff --git a/src/Handler/DataObject/Helper/LoadObjectDataPayload.php b/src/Handler/DataObject/Helper/LoadObjectData/LoadObjectDataPayload.php similarity index 92% rename from src/Handler/DataObject/Helper/LoadObjectDataPayload.php rename to src/Handler/DataObject/Helper/LoadObjectData/LoadObjectDataPayload.php index c40982cf..58396e7e 100644 --- a/src/Handler/DataObject/Helper/LoadObjectDataPayload.php +++ b/src/Handler/DataObject/Helper/LoadObjectData/LoadObjectDataPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\LoadObjectData; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteHandler.php b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouriteHandler.php similarity index 96% rename from src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteHandler.php rename to src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouriteHandler.php index a4b97ed1..92e42c38 100644 --- a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteHandler.php +++ b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouriteHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavourite; use Exception; use OpenDxp\Bundle\AdminBundle\Model\GridConfig; diff --git a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouritePayload.php b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouritePayload.php similarity index 93% rename from src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouritePayload.php rename to src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouritePayload.php index 25d2e19b..511ac7c7 100644 --- a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouritePayload.php +++ b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouritePayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavourite; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteResult.php b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouriteResult.php similarity index 87% rename from src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteResult.php rename to src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouriteResult.php index 6c440079..27644033 100644 --- a/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavouriteResult.php +++ b/src/Handler/DataObject/Helper/MarkDataObjectGridConfigFavourite/MarkDataObjectGridConfigFavouriteResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\MarkDataObjectGridConfigFavourite; final readonly class MarkDataObjectGridConfigFavouriteResult { diff --git a/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsHandler.php b/src/Handler/DataObject/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsHandler.php similarity index 86% rename from src/Handler/DataObject/Helper/PrepareHelperColumnConfigsHandler.php rename to src/Handler/DataObject/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsHandler.php index ccbaebff..63630f94 100644 --- a/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsHandler.php +++ b/src/Handler/DataObject/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigs; final class PrepareHelperColumnConfigsHandler { @@ -28,7 +28,6 @@ public function __invoke(PrepareHelperColumnConfigsPayload $payload): array $newData = []; foreach ($payload->columns as $item) { - $item = (object) $item; if (!empty($item->isOperator)) { $itemKey = '#' . uniqid('', false); $item->key = $itemKey; @@ -39,7 +38,7 @@ public function __invoke(PrepareHelperColumnConfigsPayload $payload): array return [ 'newData' => $newData, - 'helperColumns' => [...$helperColumns, ...$payload->existingHelperColumns], + 'helperColumns' => $helperColumns, ]; } } diff --git a/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsPayload.php b/src/Handler/DataObject/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsPayload.php similarity index 60% rename from src/Handler/DataObject/Helper/PrepareHelperColumnConfigsPayload.php rename to src/Handler/DataObject/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsPayload.php index ecc539b7..0c431e04 100644 --- a/src/Handler/DataObject/Helper/PrepareHelperColumnConfigsPayload.php +++ b/src/Handler/DataObject/Helper/PrepareHelperColumnConfigs/PrepareHelperColumnConfigsPayload.php @@ -15,29 +15,21 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\PrepareHelperColumnConfigs; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; -use OpenDxp\Tool\Session; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; -final class PrepareHelperColumnConfigsPayload implements ExtJsPayloadInterface +final readonly class PrepareHelperColumnConfigsPayload implements ExtJsPayloadInterface { public function __construct( public readonly array $columns, - public readonly array $existingHelperColumns, - public readonly AttributeBagInterface $helperColumnsBag, ) {} public static function fromRequest(Request $request): static { - $bag = Session::getSessionBag($request->getSession(), 'opendxp_gridconfig'); - return new static( - columns: json_decode($request->request->getString('columns'), true) ?? [], - existingHelperColumns: $bag->get('helpercolumns', []), - helperColumnsBag: $bag, + columns: json_decode($request->request->getString('columns')) ?? [], ); } } diff --git a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigHandler.php similarity index 98% rename from src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php rename to src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigHandler.php index 7677eb44..4ab5f87c 100644 --- a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigHandler.php +++ b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfig; use OpenDxp\Bundle\AdminBundle\Model\GridConfig; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridColumnConfigService; diff --git a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigPayload.php b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigPayload.php similarity index 94% rename from src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigPayload.php rename to src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigPayload.php index 8f675b07..87bd709f 100644 --- a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigPayload.php +++ b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfig; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigResult.php b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigResult.php similarity index 88% rename from src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigResult.php rename to src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigResult.php index 17a556ea..3297791e 100644 --- a/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfigResult.php +++ b/src/Handler/DataObject/Helper/SaveDataObjectGridColumnConfig/SaveDataObjectGridColumnConfigResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper\SaveDataObjectGridColumnConfig; final readonly class SaveDataObjectGridColumnConfigResult { diff --git a/src/Handler/DataObject/ObjectBrick/DeleteObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/DeleteObjectBrick/DeleteObjectBrickHandler.php similarity index 97% rename from src/Handler/DataObject/ObjectBrick/DeleteObjectBrickHandler.php rename to src/Handler/DataObject/ObjectBrick/DeleteObjectBrick/DeleteObjectBrickHandler.php index 9117b3ed..fb32785d 100644 --- a/src/Handler/DataObject/ObjectBrick/DeleteObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/DeleteObjectBrick/DeleteObjectBrickHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\DeleteObjectBrick; use OpenDxp\Bundle\AdminBundle\Payload\Common\StringIdBodyPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickHandler.php similarity index 98% rename from src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php rename to src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickHandler.php index 07385295..f0ade614 100644 --- a/src/Handler/DataObject/ObjectBrick/ExportObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrick; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrick\ExportObjectBrickPayload; use OpenDxp\Logger; diff --git a/src/Handler/DataObject/ObjectBrick/ExportObjectBrickResult.php b/src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickResult.php similarity index 96% rename from src/Handler/DataObject/ObjectBrick/ExportObjectBrickResult.php rename to src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickResult.php index 29522a0d..fbb29ff5 100644 --- a/src/Handler/DataObject/ObjectBrick/ExportObjectBrickResult.php +++ b/src/Handler/DataObject/ObjectBrick/ExportObjectBrick/ExportObjectBrickResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ExportObjectBrick; final readonly class ExportObjectBrickResult { diff --git a/src/Handler/DataObject/ObjectBrick/BrickUsagesResult.php b/src/Handler/DataObject/ObjectBrick/GetBrickUsages/BrickUsagesResult.php similarity index 97% rename from src/Handler/DataObject/ObjectBrick/BrickUsagesResult.php rename to src/Handler/DataObject/ObjectBrick/GetBrickUsages/BrickUsagesResult.php index f6991775..99798bee 100644 --- a/src/Handler/DataObject/ObjectBrick/BrickUsagesResult.php +++ b/src/Handler/DataObject/ObjectBrick/GetBrickUsages/BrickUsagesResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsages; final readonly class BrickUsagesResult { diff --git a/src/Handler/DataObject/ObjectBrick/GetBrickUsagesHandler.php b/src/Handler/DataObject/ObjectBrick/GetBrickUsages/GetBrickUsagesHandler.php similarity index 98% rename from src/Handler/DataObject/ObjectBrick/GetBrickUsagesHandler.php rename to src/Handler/DataObject/ObjectBrick/GetBrickUsages/GetBrickUsagesHandler.php index a42ce6ad..861f3d2a 100644 --- a/src/Handler/DataObject/ObjectBrick/GetBrickUsagesHandler.php +++ b/src/Handler/DataObject/ObjectBrick/GetBrickUsages/GetBrickUsagesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsages; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetBrickUsages\GetBrickUsagesPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickHandler.php similarity index 98% rename from src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php rename to src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickHandler.php index c30948bd..88f38f1a 100644 --- a/src/Handler/DataObject/ObjectBrick/GetObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrick; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrick\GetObjectBrickPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickResult.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickResult.php similarity index 97% rename from src/Handler/DataObject/ObjectBrick/GetObjectBrickResult.php rename to src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickResult.php index a747549c..f7885f42 100644 --- a/src/Handler/DataObject/ObjectBrick/GetObjectBrickResult.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrick/GetObjectBrickResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrick; final readonly class GetObjectBrickResult { diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickList/GetObjectBrickListHandler.php similarity index 99% rename from src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php rename to src/Handler/DataObject/ObjectBrick/GetObjectBrickList/GetObjectBrickListHandler.php index 03409912..a21901e6 100644 --- a/src/Handler/DataObject/ObjectBrick/GetObjectBrickListHandler.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickList/GetObjectBrickListHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickList; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickList\GetObjectBrickListPayload; diff --git a/src/Handler/DataObject/ObjectBrick/ObjectBrickListResult.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickList/ObjectBrickListResult.php similarity index 96% rename from src/Handler/DataObject/ObjectBrick/ObjectBrickListResult.php rename to src/Handler/DataObject/ObjectBrick/GetObjectBrickList/ObjectBrickListResult.php index d3ae9da6..a4e31ae9 100644 --- a/src/Handler/DataObject/ObjectBrick/ObjectBrickListResult.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickList/ObjectBrickListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickList; final readonly class ObjectBrickListResult { diff --git a/src/Handler/DataObject/ObjectBrick/GetObjectBrickTreeHandler.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/GetObjectBrickTreeHandler.php similarity index 99% rename from src/Handler/DataObject/ObjectBrick/GetObjectBrickTreeHandler.php rename to src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/GetObjectBrickTreeHandler.php index a4d24f6a..7bfd2922 100644 --- a/src/Handler/DataObject/ObjectBrick/GetObjectBrickTreeHandler.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/GetObjectBrickTreeHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTree; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTree\GetObjectBrickTreePayload; diff --git a/src/Handler/DataObject/ObjectBrick/ObjectBrickTreeResult.php b/src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/ObjectBrickTreeResult.php similarity index 96% rename from src/Handler/DataObject/ObjectBrick/ObjectBrickTreeResult.php rename to src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/ObjectBrickTreeResult.php index 81b20a89..810873d8 100644 --- a/src/Handler/DataObject/ObjectBrick/ObjectBrickTreeResult.php +++ b/src/Handler/DataObject/ObjectBrick/GetObjectBrickTree/ObjectBrickTreeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\GetObjectBrickTree; final readonly class ObjectBrickTreeResult { diff --git a/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/ImportObjectBrick/ImportObjectBrickHandler.php similarity index 98% rename from src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php rename to src/Handler/DataObject/ObjectBrick/ImportObjectBrick/ImportObjectBrickHandler.php index aae47451..d1cffeb9 100644 --- a/src/Handler/DataObject/ObjectBrick/ImportObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/ImportObjectBrick/ImportObjectBrickHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ImportObjectBrick; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\ImportObjectBrick\ImportObjectBrickPayload; use OpenDxp\Model\DataObject; diff --git a/src/Handler/DataObject/ObjectBrick/UpdateObjectBrickHandler.php b/src/Handler/DataObject/ObjectBrick/UpdateObjectBrick/UpdateObjectBrickHandler.php similarity index 99% rename from src/Handler/DataObject/ObjectBrick/UpdateObjectBrickHandler.php rename to src/Handler/DataObject/ObjectBrick/UpdateObjectBrick/UpdateObjectBrickHandler.php index 450271ce..28e40eca 100644 --- a/src/Handler/DataObject/ObjectBrick/UpdateObjectBrickHandler.php +++ b/src/Handler/DataObject/ObjectBrick/UpdateObjectBrick/UpdateObjectBrickHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\UpdateObjectBrick; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\ObjectBrick\UpdateObjectBrick\UpdateObjectBrickPayload; diff --git a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesHandler.php similarity index 98% rename from src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php rename to src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesHandler.php index 444329c6..c40311e8 100644 --- a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesHandler.php +++ b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValues; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValues\ConvertAllQuantityValuesPayload; use OpenDxp\Model\DataObject\Data\QuantityValue; diff --git a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesResult.php b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesResult.php similarity index 95% rename from src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesResult.php rename to src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesResult.php index 28e4c3a9..91416a3f 100644 --- a/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValuesResult.php +++ b/src/Handler/DataObject/QuantityValue/ConvertAllQuantityValues/ConvertAllQuantityValuesResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertAllQuantityValues; final readonly class ConvertAllQuantityValuesResult { diff --git a/src/Handler/DataObject/QuantityValue/ConvertQuantityValueHandler.php b/src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValueHandler.php similarity index 98% rename from src/Handler/DataObject/QuantityValue/ConvertQuantityValueHandler.php rename to src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValueHandler.php index 62712a49..9b1110eb 100644 --- a/src/Handler/DataObject/QuantityValue/ConvertQuantityValueHandler.php +++ b/src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValueHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValue; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValue\ConvertQuantityValuePayload; use OpenDxp\Model\DataObject\Data\QuantityValue; diff --git a/src/Handler/DataObject/QuantityValue/ConvertQuantityValueResult.php b/src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValueResult.php similarity index 96% rename from src/Handler/DataObject/QuantityValue/ConvertQuantityValueResult.php rename to src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValueResult.php index e57f4365..b8aefd8e 100644 --- a/src/Handler/DataObject/QuantityValue/ConvertQuantityValueResult.php +++ b/src/Handler/DataObject/QuantityValue/ConvertQuantityValue/ConvertQuantityValueResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ConvertQuantityValue; final readonly class ConvertQuantityValueResult { diff --git a/src/Handler/DataObject/QuantityValue/ExportQuantityValueUnitsHandler.php b/src/Handler/DataObject/QuantityValue/ExportQuantityValueUnits/ExportQuantityValueUnitsHandler.php similarity index 96% rename from src/Handler/DataObject/QuantityValue/ExportQuantityValueUnitsHandler.php rename to src/Handler/DataObject/QuantityValue/ExportQuantityValueUnits/ExportQuantityValueUnitsHandler.php index c9ca8c7f..924f59ff 100644 --- a/src/Handler/DataObject/QuantityValue/ExportQuantityValueUnitsHandler.php +++ b/src/Handler/DataObject/QuantityValue/ExportQuantityValueUnits/ExportQuantityValueUnitsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ExportQuantityValueUnits; use OpenDxp\Model\DataObject\QuantityValue\Service as QuantityValueService; diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListHandler.php similarity index 98% rename from src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php rename to src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListHandler.php index c34d5a89..cb499932 100644 --- a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListHandler.php +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitList; use Exception; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitList\GetQuantityValueUnitListPayload; diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListResult.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListResult.php similarity index 95% rename from src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListResult.php rename to src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListResult.php index e7d95451..00c25e82 100644 --- a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitListResult.php +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitList/GetQuantityValueUnitListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnitList; final readonly class GetQuantityValueUnitListResult { diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsHandler.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsHandler.php similarity index 98% rename from src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsHandler.php rename to src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsHandler.php index 8aff26a6..fdb0eccd 100644 --- a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsHandler.php +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnits; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnits\GetQuantityValueUnitsPayload; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; diff --git a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsResult.php b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsResult.php similarity index 96% rename from src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsResult.php rename to src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsResult.php index 9f7e79ac..b6e5d47f 100644 --- a/src/Handler/DataObject/QuantityValue/GetQuantityValueUnitsResult.php +++ b/src/Handler/DataObject/QuantityValue/GetQuantityValueUnits/GetQuantityValueUnitsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\GetQuantityValueUnits; final readonly class GetQuantityValueUnitsResult { diff --git a/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnitsHandler.php b/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnits/ImportQuantityValueUnitsHandler.php similarity index 97% rename from src/Handler/DataObject/QuantityValue/ImportQuantityValueUnitsHandler.php rename to src/Handler/DataObject/QuantityValue/ImportQuantityValueUnits/ImportQuantityValueUnitsHandler.php index d8b28288..a3c039c0 100644 --- a/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnitsHandler.php +++ b/src/Handler/DataObject/QuantityValue/ImportQuantityValueUnits/ImportQuantityValueUnitsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnits; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\ImportQuantityValueUnits\ImportQuantityValueUnitsPayload; use OpenDxp\Model\DataObject\QuantityValue\Service as QuantityValueService; diff --git a/src/Handler/DataObject/SaveDataObjectFolderHandler.php b/src/Handler/DataObject/SaveDataObjectFolder/SaveDataObjectFolderHandler.php similarity index 95% rename from src/Handler/DataObject/SaveDataObjectFolderHandler.php rename to src/Handler/DataObject/SaveDataObjectFolder/SaveDataObjectFolderHandler.php index 9333fb37..20060390 100644 --- a/src/Handler/DataObject/SaveDataObjectFolderHandler.php +++ b/src/Handler/DataObject/SaveDataObjectFolder/SaveDataObjectFolderHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectFolder; use Exception; use OpenDxp\Logger; @@ -24,7 +24,6 @@ use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\SaveDataObjectFolderPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class SaveDataObjectFolderHandler diff --git a/src/Payload/DataObject/SaveDataObjectFolderPayload.php b/src/Handler/DataObject/SaveDataObjectFolder/SaveDataObjectFolderPayload.php similarity index 94% rename from src/Payload/DataObject/SaveDataObjectFolderPayload.php rename to src/Handler/DataObject/SaveDataObjectFolder/SaveDataObjectFolderPayload.php index f061d181..c7e85ad2 100644 --- a/src/Payload/DataObject/SaveDataObjectFolderPayload.php +++ b/src/Handler/DataObject/SaveDataObjectFolder/SaveDataObjectFolderPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\SaveDataObjectFolder; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/TreeGetChildrenByIdHandler.php b/src/Handler/DataObject/TreeGetChildrenById/TreeGetChildrenByIdHandler.php similarity index 96% rename from src/Handler/DataObject/TreeGetChildrenByIdHandler.php rename to src/Handler/DataObject/TreeGetChildrenById/TreeGetChildrenByIdHandler.php index 775506e2..644ada8f 100644 --- a/src/Handler/DataObject/TreeGetChildrenByIdHandler.php +++ b/src/Handler/DataObject/TreeGetChildrenById/TreeGetChildrenByIdHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenById; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\TreeGetChildrenByIdPayload; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectChildren\GetDataObjectChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectChildrenResult; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Db; diff --git a/src/Payload/DataObject/TreeGetChildrenByIdPayload.php b/src/Handler/DataObject/TreeGetChildrenById/TreeGetChildrenByIdPayload.php similarity index 89% rename from src/Payload/DataObject/TreeGetChildrenByIdPayload.php rename to src/Handler/DataObject/TreeGetChildrenById/TreeGetChildrenByIdPayload.php index 3ed23f6e..1469d646 100644 --- a/src/Payload/DataObject/TreeGetChildrenByIdPayload.php +++ b/src/Handler/DataObject/TreeGetChildrenById/TreeGetChildrenByIdPayload.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenById; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; @@ -29,6 +29,7 @@ public function __construct( public string $view = '', public int $fromPaging = 0, public array $allParams = [], + public int $inSearch = 0, ) {} public static function fromRequest(Request $request): static @@ -41,6 +42,7 @@ public static function fromRequest(Request $request): static view: $request->query->getString('view'), fromPaging: $request->query->getInt('fromPaging'), allParams: $request->query->all(), + inSearch: $request->query->getInt('inSearch'), ); } } diff --git a/src/Handler/DataObject/UpdateDataObjectHandler.php b/src/Handler/DataObject/UpdateDataObject/UpdateDataObjectHandler.php similarity index 98% rename from src/Handler/DataObject/UpdateDataObjectHandler.php rename to src/Handler/DataObject/UpdateDataObject/UpdateDataObjectHandler.php index 9d8a2ea6..72bf7587 100644 --- a/src/Handler/DataObject/UpdateDataObjectHandler.php +++ b/src/Handler/DataObject/UpdateDataObject/UpdateDataObjectHandler.php @@ -15,11 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\UpdateDataObject; use Exception; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; -use OpenDxp\Bundle\AdminBundle\Payload\DataObject\UpdateDataObjectPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Db; diff --git a/src/Payload/DataObject/UpdateDataObjectPayload.php b/src/Handler/DataObject/UpdateDataObject/UpdateDataObjectPayload.php similarity index 94% rename from src/Payload/DataObject/UpdateDataObjectPayload.php rename to src/Handler/DataObject/UpdateDataObject/UpdateDataObjectPayload.php index 9952bff8..b4d4989e 100644 --- a/src/Payload/DataObject/UpdateDataObjectPayload.php +++ b/src/Handler/DataObject/UpdateDataObject/UpdateDataObjectPayload.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Payload\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\UpdateDataObject; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Handler/DataObject/UpdateDataObjectResult.php b/src/Handler/DataObject/UpdateDataObject/UpdateDataObjectResult.php similarity index 89% rename from src/Handler/DataObject/UpdateDataObjectResult.php rename to src/Handler/DataObject/UpdateDataObject/UpdateDataObjectResult.php index 47360661..59c7d479 100644 --- a/src/Handler/DataObject/UpdateDataObjectResult.php +++ b/src/Handler/DataObject/UpdateDataObject/UpdateDataObjectResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\UpdateDataObject; final readonly class UpdateDataObjectResult { diff --git a/src/Handler/DataObject/Variants/GetVariantsHandler.php b/src/Handler/DataObject/Variants/GetVariants/GetVariantsHandler.php similarity index 99% rename from src/Handler/DataObject/Variants/GetVariantsHandler.php rename to src/Handler/DataObject/Variants/GetVariants/GetVariantsHandler.php index d1a7afe0..453ede95 100644 --- a/src/Handler/DataObject/Variants/GetVariantsHandler.php +++ b/src/Handler/DataObject/Variants/GetVariants/GetVariantsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariants; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariants\GetVariantsPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService; diff --git a/src/Handler/DataObject/Variants/GetVariantsResult.php b/src/Handler/DataObject/Variants/GetVariants/GetVariantsResult.php similarity index 98% rename from src/Handler/DataObject/Variants/GetVariantsResult.php rename to src/Handler/DataObject/Variants/GetVariants/GetVariantsResult.php index 8f9130da..0c0a86f9 100644 --- a/src/Handler/DataObject/Variants/GetVariantsResult.php +++ b/src/Handler/DataObject/Variants/GetVariants/GetVariantsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\GetVariants; final readonly class GetVariantsResult { diff --git a/src/Handler/DataObject/Variants/UpdateObjectKeyHandler.php b/src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyHandler.php similarity index 98% rename from src/Handler/DataObject/Variants/UpdateObjectKeyHandler.php rename to src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyHandler.php index 576b221c..3e347f61 100644 --- a/src/Handler/DataObject/Variants/UpdateObjectKeyHandler.php +++ b/src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKey; use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKey\UpdateObjectKeyPayload; use OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService; diff --git a/src/Handler/DataObject/Variants/UpdateObjectKeyResult.php b/src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyResult.php similarity index 97% rename from src/Handler/DataObject/Variants/UpdateObjectKeyResult.php rename to src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyResult.php index a9deaca2..1d330543 100644 --- a/src/Handler/DataObject/Variants/UpdateObjectKeyResult.php +++ b/src/Handler/DataObject/Variants/UpdateObjectKey/UpdateObjectKeyResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants; +namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Variants\UpdateObjectKey; final readonly class UpdateObjectKeyResult { diff --git a/src/Handler/DataObject/Version/DiffVersions/DiffVersionsPayload.php b/src/Handler/DataObject/Version/DiffVersions/DiffVersionsPayload.php index 8509330d..3b611bdd 100644 --- a/src/Handler/DataObject/Version/DiffVersions/DiffVersionsPayload.php +++ b/src/Handler/DataObject/Version/DiffVersions/DiffVersionsPayload.php @@ -24,13 +24,15 @@ public function __construct( public readonly int $from, public readonly int $to, + public readonly ?string $userTimezone = null, ) {} public static function fromRequest(Request $request): static { return new static( - from: $request->attributes->getInt('from'), - to: $request->attributes->getInt('to'), + from: $request->attributes->getInt('from'), + to: $request->attributes->getInt('to'), + userTimezone: $request->query->getString('userTimezone') ?: null, ); } } diff --git a/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionHandler.php b/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionHandler.php index 63b6e55f..11092df3 100644 --- a/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionHandler.php +++ b/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionHandler.php @@ -18,14 +18,13 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PreviewVersion; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; -use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Model\DataObject; use OpenDxp\Model\Version; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; final class PreviewVersionHandler { - public function __invoke(IdQueryPayload $payload): PreviewVersionResult + public function __invoke(PreviewVersionPayload $payload): PreviewVersionResult { DataObject::setDoNotRestoreKeyAndPath(true); diff --git a/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionPayload.php b/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionPayload.php new file mode 100644 index 00000000..107014a0 --- /dev/null +++ b/src/Handler/DataObject/Version/PreviewVersion/PreviewVersionPayload.php @@ -0,0 +1,38 @@ +query->getInt('id'), + userTimezone: $request->query->getString('userTimezone') ?: null, + ); + } +} diff --git a/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php b/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php index e478462f..3140c072 100644 --- a/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php +++ b/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php @@ -17,8 +17,8 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Version\PublishVersion; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; use OpenDxp\Bundle\AdminBundle\Exception\DataObject\DataObjectNotFoundException; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\DataObject; @@ -29,7 +29,7 @@ final class PublishVersionHandler { public function __construct( private readonly AdminUserContextInterface $userContext, - private readonly ElementResponseNormalizer $normalizer, + private readonly AdminStyleEnricher $adminStyleEnricher, ) {} public function __invoke(IdBodyPayload $payload): PublishVersionResult @@ -52,7 +52,7 @@ public function __invoke(IdBodyPayload $payload): PublishVersionResult $object->save(); $treeData = []; - $this->normalizer->normalize($object, $treeData, self::class); + $this->adminStyleEnricher->forTree($object, $treeData); return new PublishVersionResult( modificationDate: (int) $object->getModificationDate(), diff --git a/src/Handler/Document/Copy/CopyInfo/CopyInfoHandler.php b/src/Handler/Document/Copy/CopyInfo/CopyInfoHandler.php new file mode 100644 index 00000000..0af86136 --- /dev/null +++ b/src/Handler/Document/Copy/CopyInfo/CopyInfoHandler.php @@ -0,0 +1,107 @@ +type === 'recursive' || $payload->type === 'recursive-update-references') { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_document_document_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $payload->sourceId, + 'targetId' => $payload->targetId, + 'type' => 'child', + 'language' => $payload->language, + 'enableInheritance' => $payload->enableInheritance, + 'transactionId' => $transactionId, + 'saveParentId' => true, + 'resetIndex' => true, + ], + ]]; + + $document = Document::getById($payload->sourceId) ?? throw new DocumentNotFoundException($payload->sourceId); + $childIds = []; + + if ($document->hasChildren()) { + $list = new Document\Listing(); + $list->setCondition('`path` LIKE ?', [$list->escapeLike($document->getRealFullPath()) . '/%']); + $list->setOrderKey('LENGTH(`path`)', false); + $list->setOrder('ASC'); + $childIds = $list->loadIdList(); + } + + foreach ($childIds as $id) { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_document_document_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $id, + 'targetParentId' => $payload->targetId, + 'sourceParentId' => $payload->sourceId, + 'type' => 'child', + 'language' => $payload->language, + 'enableInheritance' => $payload->enableInheritance, + 'transactionId' => $transactionId, + ], + ]]; + } + + if ($payload->type === 'recursive-update-references') { + for ($i = 0; $i < (count($childIds) + 1); $i++) { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_document_document_copyrewriteids'), + 'method' => 'PUT', + 'params' => [ + 'transactionId' => $transactionId, + 'enableInheritance' => $payload->enableInheritance, + '_dc' => uniqid('', false), + ], + ]]; + } + } + } elseif ($payload->type === 'child' || $payload->type === 'replace') { + $pasteJobs[] = [[ + 'url' => $this->router->generate('opendxp_admin_document_document_copy'), + 'method' => 'POST', + 'params' => [ + 'sourceId' => $payload->sourceId, + 'targetId' => $payload->targetId, + 'type' => $payload->type, + 'language' => $payload->language, + 'enableInheritance' => $payload->enableInheritance, + 'transactionId' => $transactionId, + 'resetIndex' => ($payload->type === 'child'), + ], + ]]; + } + + return new CopyInfoResult($transactionId, $pasteJobs); + } +} diff --git a/src/Handler/Document/Copy/CopyInfo/CopyInfoPayload.php b/src/Handler/Document/Copy/CopyInfo/CopyInfoPayload.php new file mode 100644 index 00000000..5104c2bf --- /dev/null +++ b/src/Handler/Document/Copy/CopyInfo/CopyInfoPayload.php @@ -0,0 +1,43 @@ +query->getString('type') ?: null, + sourceId: $request->query->getInt('sourceId'), + targetId: $request->query->getString('targetId') ?: null, + language: $request->query->getString('language') ?: null, + enableInheritance: $request->query->getString('enableInheritance') ?: null, + ); + } +} diff --git a/src/Handler/Document/Copy/CopyInfo/CopyInfoResult.php b/src/Handler/Document/Copy/CopyInfo/CopyInfoResult.php new file mode 100644 index 00000000..2c834772 --- /dev/null +++ b/src/Handler/Document/Copy/CopyInfo/CopyInfoResult.php @@ -0,0 +1,26 @@ +sourceId) ?? throw new DocumentNotFoundException($payload->sourceId); - - if (!$document->hasChildren()) { - return new GetDocumentChildIdsResult([]); - } - - $list = new Document\Listing(); - $list->setCondition('`path` LIKE ?', [$list->escapeLike($document->getRealFullPath()) . '/%']); - $list->setOrderKey('LENGTH(`path`)', false); - $list->setOrder('ASC'); - - return new GetDocumentChildIdsResult($list->loadIdList()); - } -} diff --git a/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsResult.php b/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsResult.php deleted file mode 100644 index 04c6c5b6..00000000 --- a/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsResult.php +++ /dev/null @@ -1,26 +0,0 @@ -isLocked(); $data['url'] = $email->getUrl(); - $this->normalizer->normalize($email, $data, self::class, ['draftVersion' => $draftVersion]); + $this->documentMetaEnricher->enrich($email, $data); + $this->adminStyleEnricher->forEditor($email, $data); + $this->userNamesEnricher->enrich($email, $data); + $this->propertiesEnricher->enrich($email, $data); + $this->translationEnricher->enrich($email, $data); + $this->draftEnricher->enrich($email, $data, $draftVersion); return new GetEmailDataResult(email: $email, data: $data, draftVersion: $draftVersion); } diff --git a/src/Handler/Document/Folder/GetFolderData/GetFolderDataHandler.php b/src/Handler/Document/Folder/GetFolderData/GetFolderDataHandler.php index 4d32bf6c..e4d25b38 100644 --- a/src/Handler/Document/Folder/GetFolderData/GetFolderDataHandler.php +++ b/src/Handler/Document/Folder/GetFolderData/GetFolderDataHandler.php @@ -17,13 +17,23 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Folder\GetFolderData; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\DocumentMetaEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\PropertiesEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\TranslationEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Model\Document\Folder; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class GetFolderDataHandler { - public function __construct(private readonly ElementResponseNormalizer $normalizer) {} + public function __construct( + private readonly DocumentMetaEnricher $documentMetaEnricher, + private readonly AdminStyleEnricher $adminStyleEnricher, + private readonly UserNamesEnricher $userNamesEnricher, + private readonly PropertiesEnricher $propertiesEnricher, + private readonly TranslationEnricher $translationEnricher, + ) {} public function __invoke(GetFolderDataPayload $payload): GetFolderDataResult { @@ -38,7 +48,11 @@ public function __invoke(GetFolderDataPayload $payload): GetFolderDataResult $data = $folder->getObjectVars(); $data['locked'] = $folder->isLocked(); - $this->normalizer->normalize($folder, $data, self::class); + $this->documentMetaEnricher->enrich($folder, $data); + $this->adminStyleEnricher->forEditor($folder, $data); + $this->userNamesEnricher->enrich($folder, $data); + $this->propertiesEnricher->enrich($folder, $data); + $this->translationEnricher->enrich($folder, $data); return new GetFolderDataResult(folder: $folder, data: $data); } diff --git a/src/Handler/Document/GetDocumentChildrenHandler.php b/src/Handler/Document/GetDocumentChildren/GetDocumentChildrenHandler.php similarity index 94% rename from src/Handler/Document/GetDocumentChildrenHandler.php rename to src/Handler/Document/GetDocumentChildren/GetDocumentChildrenHandler.php index 394834ce..34eb8f9e 100644 --- a/src/Handler/Document/GetDocumentChildrenHandler.php +++ b/src/Handler/Document/GetDocumentChildren/GetDocumentChildrenHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentChildren; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Model\Document; diff --git a/src/Handler/Document/GetDocumentChildrenResult.php b/src/Handler/Document/GetDocumentChildren/GetDocumentChildrenResult.php similarity index 88% rename from src/Handler/Document/GetDocumentChildrenResult.php rename to src/Handler/Document/GetDocumentChildren/GetDocumentChildrenResult.php index 3c170186..ef29fc11 100644 --- a/src/Handler/Document/GetDocumentChildrenResult.php +++ b/src/Handler/Document/GetDocumentChildren/GetDocumentChildrenResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document; +namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentChildren; final readonly class GetDocumentChildrenResult { diff --git a/src/Handler/Document/GetDocumentData/GetDocumentDataHandler.php b/src/Handler/Document/GetDocumentData/GetDocumentDataHandler.php index 8da9025e..df014a57 100644 --- a/src/Handler/Document/GetDocumentData/GetDocumentDataHandler.php +++ b/src/Handler/Document/GetDocumentData/GetDocumentDataHandler.php @@ -16,9 +16,13 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentData; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\DocumentMetaEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\PropertiesEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\TranslationEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Exception\Document\DocumentNotFoundException; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; use OpenDxp\Model\Document; use Symfony\Component\EventDispatcher\GenericEvent; @@ -29,7 +33,11 @@ final class GetDocumentDataHandler { public function __construct( private readonly EditLockService $editLockService, - private readonly ElementResponseNormalizer $normalizer, + private readonly DocumentMetaEnricher $documentMetaEnricher, + private readonly AdminStyleEnricher $adminStyleEnricher, + private readonly UserNamesEnricher $userNamesEnricher, + private readonly PropertiesEnricher $propertiesEnricher, + private readonly TranslationEnricher $translationEnricher, private readonly EventDispatcherInterface $eventDispatcher, ) {} @@ -51,7 +59,11 @@ public function __invoke(GetDocumentDataPayload $payload): GetDocumentDataResult $document = clone $document; $data = $document->getObjectVars(); - $this->normalizer->normalize($document, $data, self::class); + $this->documentMetaEnricher->enrich($document, $data); + $this->adminStyleEnricher->forEditor($document, $data); + $this->userNamesEnricher->enrich($document, $data); + $this->propertiesEnricher->enrich($document, $data); + $this->translationEnricher->enrich($document, $data); $event = new GenericEvent($this, ['data' => $data, 'document' => $document]); $this->eventDispatcher->dispatch($event, AdminEvents::DOCUMENT_GET_PRE_SEND_DATA); diff --git a/src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataHandler.php b/src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataHandler.php index bf97b3bb..f880537d 100644 --- a/src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataHandler.php +++ b/src/Handler/Document/Hardlink/GetHardlinkData/GetHardlinkDataHandler.php @@ -17,8 +17,12 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Hardlink\GetHardlinkData; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\DocumentMetaEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\PropertiesEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\TranslationEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; use OpenDxp\Model\Document\Hardlink; @@ -29,7 +33,11 @@ final class GetHardlinkDataHandler { public function __construct( private readonly EditLockService $editLockService, - private readonly ElementResponseNormalizer $normalizer, + private readonly DocumentMetaEnricher $documentMetaEnricher, + private readonly AdminStyleEnricher $adminStyleEnricher, + private readonly UserNamesEnricher $userNamesEnricher, + private readonly PropertiesEnricher $propertiesEnricher, + private readonly TranslationEnricher $translationEnricher, ) {} public function __invoke(IdQueryPayload $payload): GetHardlinkDataResult @@ -57,7 +65,11 @@ public function __invoke(IdQueryPayload $payload): GetHardlinkDataResult $data['sourcePath'] = $cloned->getSourceDocument()->getRealFullPath(); } - $this->normalizer->normalize($cloned, $data, self::class); + $this->documentMetaEnricher->enrich($cloned, $data); + $this->adminStyleEnricher->forEditor($cloned, $data); + $this->userNamesEnricher->enrich($cloned, $data); + $this->propertiesEnricher->enrich($cloned, $data); + $this->translationEnricher->enrich($cloned, $data); return new GetHardlinkDataResult(original: $link, link: $cloned, data: $data); } diff --git a/src/Handler/Document/Link/GetLinkData/GetLinkDataHandler.php b/src/Handler/Document/Link/GetLinkData/GetLinkDataHandler.php index b235d9d6..6a73c7c9 100644 --- a/src/Handler/Document/Link/GetLinkData/GetLinkDataHandler.php +++ b/src/Handler/Document/Link/GetLinkData/GetLinkDataHandler.php @@ -17,8 +17,12 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Link\GetLinkData; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\DocumentMetaEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\PropertiesEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\TranslationEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; use OpenDxp\Model\Document\Link; @@ -31,7 +35,11 @@ final class GetLinkDataHandler public function __construct( private readonly SerializerInterface $serializer, private readonly EditLockService $editLockService, - private readonly ElementResponseNormalizer $normalizer, + private readonly DocumentMetaEnricher $documentMetaEnricher, + private readonly AdminStyleEnricher $adminStyleEnricher, + private readonly UserNamesEnricher $userNamesEnricher, + private readonly PropertiesEnricher $propertiesEnricher, + private readonly TranslationEnricher $translationEnricher, ) {} public function __invoke(IdQueryPayload $payload): GetLinkDataResult @@ -58,7 +66,11 @@ public function __invoke(IdQueryPayload $payload): GetLinkDataResult $cloned->getScheduledTasks() ); - $this->normalizer->normalize($cloned, $data, self::class); + $this->documentMetaEnricher->enrich($cloned, $data); + $this->adminStyleEnricher->forEditor($cloned, $data); + $this->userNamesEnricher->enrich($cloned, $data); + $this->propertiesEnricher->enrich($cloned, $data); + $this->translationEnricher->enrich($cloned, $data); return new GetLinkDataResult(original: $link, link: $cloned, data: $data); } diff --git a/src/Handler/Document/Page/GetPageData/GetPageDataHandler.php b/src/Handler/Document/Page/GetPageData/GetPageDataHandler.php index c4512df3..cad71aab 100644 --- a/src/Handler/Document/Page/GetPageData/GetPageDataHandler.php +++ b/src/Handler/Document/Page/GetPageData/GetPageDataHandler.php @@ -17,9 +17,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\DocumentMetaEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\DraftEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\PropertiesEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\TranslationEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Helper\DocumentVersionHelper; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Handler\Document\Page\GetPageData\GetPageDataPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; @@ -34,8 +39,13 @@ final class GetPageDataHandler public function __construct( private readonly StaticPageGenerator $staticPageGenerator, private readonly EditLockService $editLockService, - private readonly ElementResponseNormalizer $normalizer, private readonly AdminUserContextInterface $userContext, + private readonly DocumentMetaEnricher $documentMetaEnricher, + private readonly AdminStyleEnricher $adminStyleEnricher, + private readonly UserNamesEnricher $userNamesEnricher, + private readonly PropertiesEnricher $propertiesEnricher, + private readonly TranslationEnricher $translationEnricher, + private readonly DraftEnricher $draftEnricher, ) {} public function __invoke(GetPageDataPayload $payload): GetPageDataResult @@ -78,7 +88,12 @@ public function __invoke(GetPageDataPayload $payload): GetPageDataResult $page->getScheduledTasks() ); - $this->normalizer->normalize($page, $data, self::class, ['draftVersion' => $draftVersion]); + $this->documentMetaEnricher->enrich($page, $data); + $this->adminStyleEnricher->forEditor($page, $data); + $this->userNamesEnricher->enrich($page, $data); + $this->propertiesEnricher->enrich($page, $data); + $this->translationEnricher->enrich($page, $data); + $this->draftEnricher->enrich($page, $data, $draftVersion); return new GetPageDataResult(page: $page, data: $data, draftVersion: $draftVersion); } diff --git a/src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataHandler.php b/src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataHandler.php index cafb7225..20cf7c95 100644 --- a/src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataHandler.php +++ b/src/Handler/Document/Snippet/GetSnippetData/GetSnippetDataHandler.php @@ -17,9 +17,14 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Snippet\GetSnippetData; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\DocumentMetaEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\DraftEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\PropertiesEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Document\TranslationEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\UserNamesEnricher; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Helper\DocumentVersionHelper; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService; @@ -32,8 +37,13 @@ final class GetSnippetDataHandler { public function __construct( private readonly EditLockService $editLockService, - private readonly ElementResponseNormalizer $normalizer, private readonly AdminUserContextInterface $userContext, + private readonly DocumentMetaEnricher $documentMetaEnricher, + private readonly AdminStyleEnricher $adminStyleEnricher, + private readonly UserNamesEnricher $userNamesEnricher, + private readonly PropertiesEnricher $propertiesEnricher, + private readonly TranslationEnricher $translationEnricher, + private readonly DraftEnricher $draftEnricher, ) {} public function __invoke(IdQueryPayload $payload): GetSnippetDataResult @@ -68,7 +78,12 @@ public function __invoke(IdQueryPayload $payload): GetSnippetDataResult $data['contentMainDocumentPath'] = $snippet->getContentMainDocument()->getRealFullPath(); } - $this->normalizer->normalize($snippet, $data, self::class, ['draftVersion' => $draftVersion]); + $this->documentMetaEnricher->enrich($snippet, $data); + $this->adminStyleEnricher->forEditor($snippet, $data); + $this->userNamesEnricher->enrich($snippet, $data); + $this->propertiesEnricher->enrich($snippet, $data); + $this->translationEnricher->enrich($snippet, $data); + $this->draftEnricher->enrich($snippet, $data, $draftVersion); return new GetSnippetDataResult(snippet: $snippet, data: $data, draftVersion: $draftVersion); } diff --git a/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenHandler.php b/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenHandler.php index f2a83b80..5bcb3d7e 100644 --- a/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenHandler.php +++ b/src/Handler/Document/TreeGetDocumentChildren/TreeGetDocumentChildrenHandler.php @@ -18,7 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildren; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentChildren\GetDocumentChildrenHandler; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\ElementServiceInterface; use OpenDxp\Db; diff --git a/src/Handler/Document/Version/PublishVersion/PublishVersionHandler.php b/src/Handler/Document/Version/PublishVersion/PublishVersionHandler.php index fa994f8d..cd862faf 100644 --- a/src/Handler/Document/Version/PublishVersion/PublishVersionHandler.php +++ b/src/Handler/Document/Version/PublishVersion/PublishVersionHandler.php @@ -17,8 +17,8 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Version\PublishVersion; +use OpenDxp\Bundle\AdminBundle\Enricher\Element\AdminStyleEnricher; use OpenDxp\Bundle\AdminBundle\Exception\Document\DocumentNotFoundException; -use OpenDxp\Bundle\AdminBundle\Normalizer\ElementResponseNormalizer; use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; @@ -31,7 +31,7 @@ final class PublishVersionHandler public function __construct( private readonly AdminUserContextInterface $userContext, private readonly SessionService $sessionService, - private readonly ElementResponseNormalizer $normalizer, + private readonly AdminStyleEnricher $adminStyleEnricher, ) {} public function __invoke(IdBodyPayload $payload): PublishVersionResult @@ -58,7 +58,7 @@ public function __invoke(IdBodyPayload $payload): PublishVersionResult $document->save(); $treeData = []; - $this->normalizer->normalize($document, $treeData, self::class); + $this->adminStyleEnricher->forEditor($document, $treeData); return new PublishVersionResult(treeData: $treeData); } diff --git a/src/Handler/Element/AddNote/AddNoteHandler.php b/src/Handler/Element/AddNote/AddNoteHandler.php new file mode 100644 index 00000000..c7f246ef --- /dev/null +++ b/src/Handler/Element/AddNote/AddNoteHandler.php @@ -0,0 +1,23 @@ +setCid($payload->cid); + $note->setCtype($payload->ctype); + $note->setDate(time()); + $note->setTitle($payload->title); + $note->setDescription($payload->description); + $note->setType($payload->type); + $note->setLocked(false); + $note->save(); + } +} diff --git a/src/Handler/Element/AddNote/AddNotePayload.php b/src/Handler/Element/AddNote/AddNotePayload.php new file mode 100644 index 00000000..0c56a0d5 --- /dev/null +++ b/src/Handler/Element/AddNote/AddNotePayload.php @@ -0,0 +1,30 @@ +request->get('cid'), + ctype: $request->request->get('ctype'), + title: $request->request->get('title'), + description: $request->request->get('description'), + type: $request->request->get('type'), + ); + } +} diff --git a/src/Handler/Element/AddNoteHandler.php b/src/Handler/Element/AddNoteHandler.php deleted file mode 100644 index 4194946a..00000000 --- a/src/Handler/Element/AddNoteHandler.php +++ /dev/null @@ -1,41 +0,0 @@ -setCid($cid); - $note->setCtype($ctype); - $note->setDate(time()); - $note->setTitle($title); - $note->setDescription($description); - $note->setType($type); - $note->setLocked(false); - $note->save(); - } -} diff --git a/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsHandler.php b/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsHandler.php new file mode 100644 index 00000000..f4dee9ee --- /dev/null +++ b/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsHandler.php @@ -0,0 +1,30 @@ +userId) { + $userList = []; + if ($user = Model\User::getById($payload->userId)) { + $userList[] = $user; + } + } else { + $userList = new Model\User\Listing(); + $userList->setCondition('`type` = ?', ['user']); + $userList = $userList->load(); + } + + $element = Element\Service::getElementById($payload->elementType, $payload->elementId); + $result = Element\PermissionChecker::check($element, $userList); + + return new AnalyzePermissionsResult(data: $result); + } +} diff --git a/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsPayload.php b/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsPayload.php new file mode 100644 index 00000000..0da836fa --- /dev/null +++ b/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsPayload.php @@ -0,0 +1,26 @@ +request->getInt('userId') ?: null, + elementType: $request->request->get('elementType'), + elementId: $request->request->getInt('elementId'), + ); + } +} diff --git a/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsResult.php b/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsResult.php new file mode 100644 index 00000000..f2057c0e --- /dev/null +++ b/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsResult.php @@ -0,0 +1,12 @@ +setCondition('`type` = ?', ['user']); - $userList = $userList->load(); - } - - $element = Element\Service::getElementById($elementType, $elementId); - $result = Element\PermissionChecker::check($element, $userList); - - return new AnalyzePermissionsResult(data: $result); - } -} diff --git a/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsHandler.php b/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsHandler.php new file mode 100644 index 00000000..52db51a3 --- /dev/null +++ b/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsHandler.php @@ -0,0 +1,23 @@ +setCondition( + 'cid = ' . $versions->quote($payload->id) . + ' AND date <> ' . $versions->quote($payload->date) . + ' AND ctype = ' . $versions->quote($payload->type) + ); + foreach ($versions->load() as $version) { + $version->delete(); + } + } +} diff --git a/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsPayload.php b/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsPayload.php new file mode 100644 index 00000000..cb8566ff --- /dev/null +++ b/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsPayload.php @@ -0,0 +1,26 @@ +request->getInt('id'), + date: $request->request->get('date'), + type: $request->request->get('type'), + ); + } +} diff --git a/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsResult.php b/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsResult.php new file mode 100644 index 00000000..a9f7dbfa --- /dev/null +++ b/src/Handler/Element/DeleteAllVersions/DeleteAllVersionsResult.php @@ -0,0 +1,10 @@ +setCondition( - 'cid = ' . $versions->quote($elementId) . - ' AND date <> ' . $versions->quote($elementModificationdate) . - ' AND ctype = ' . $versions->quote($elementType) - ); - foreach ($versions->load() as $version) { - $version->delete(); - } - } -} diff --git a/src/Handler/Element/DeleteDraft/DeleteDraftHandler.php b/src/Handler/Element/DeleteDraft/DeleteDraftHandler.php new file mode 100644 index 00000000..84f8213d --- /dev/null +++ b/src/Handler/Element/DeleteDraft/DeleteDraftHandler.php @@ -0,0 +1,19 @@ +id); + + if ($version) { + $version->delete(); + } + } +} diff --git a/src/Handler/Element/DeleteNote/DeleteNoteHandler.php b/src/Handler/Element/DeleteNote/DeleteNoteHandler.php new file mode 100644 index 00000000..e8aaf40c --- /dev/null +++ b/src/Handler/Element/DeleteNote/DeleteNoteHandler.php @@ -0,0 +1,27 @@ +id); + + if (!$note) { + return; + } + + if ($note->getLocked()) { + throw new BadRequestHttpException('note_is_locked'); + } + + $note->delete(); + } +} diff --git a/src/Handler/Element/DeleteNoteHandler.php b/src/Handler/Element/DeleteNoteHandler.php deleted file mode 100644 index fe431204..00000000 --- a/src/Handler/Element/DeleteNoteHandler.php +++ /dev/null @@ -1,39 +0,0 @@ -id); - - if (!$note) { - return; - } - - if ($note->getLocked()) { - throw new BadRequestHttpException('note_is_locked'); - } - - $note->delete(); - } -} diff --git a/src/Handler/Element/DeleteVersion/DeleteVersionHandler.php b/src/Handler/Element/DeleteVersion/DeleteVersionHandler.php new file mode 100644 index 00000000..0b44de0b --- /dev/null +++ b/src/Handler/Element/DeleteVersion/DeleteVersionHandler.php @@ -0,0 +1,15 @@ +id)?->delete(); + } +} diff --git a/src/Handler/Element/FindUsagesHandler.php b/src/Handler/Element/FindUsages/FindUsagesHandler.php similarity index 54% rename from src/Handler/Element/FindUsagesHandler.php rename to src/Handler/Element/FindUsages/FindUsagesHandler.php index 6089791a..a176749e 100644 --- a/src/Handler/Element/FindUsagesHandler.php +++ b/src/Handler/Element/FindUsages/FindUsagesHandler.php @@ -1,40 +1,21 @@ id) { + $element = Element\Service::getElementById($payload->type, $payload->id); + } elseif ($payload->path) { + $element = Element\Service::getElementByPath($payload->type, $payload->path); } if (!$element instanceof Element\ElementInterface) { @@ -45,8 +26,8 @@ public function __invoke( $results = []; $hasHidden = false; - if ($sort !== null) { - $sort = json_decode($sort)[0]; + if ($payload->sort !== null) { + $sort = json_decode($payload->sort)[0]; $orderBy = $sort->property; $orderDirection = $sort->direction; } else { @@ -54,10 +35,10 @@ public function __invoke( $orderDirection = null; } - $queryOffset = $offset; - $queryLimit = $limit; + $queryOffset = $payload->start; + $queryLimit = $payload->limit; - while (count($results) < min($limit, $total) && $queryOffset < $total) { + while (count($results) < min($payload->limit, $total) && $queryOffset < $total) { $elements = $element->getDependencies() ->getRequiredByWithPath($queryOffset, $queryLimit, $orderBy, $orderDirection); @@ -74,7 +55,7 @@ public function __invoke( } $queryOffset += count($elements); - $queryLimit = $limit - count($results); + $queryLimit = $payload->limit - count($results); } return new FindUsagesResult( diff --git a/src/Handler/Element/FindUsages/FindUsagesPayload.php b/src/Handler/Element/FindUsages/FindUsagesPayload.php new file mode 100644 index 00000000..8e22ff99 --- /dev/null +++ b/src/Handler/Element/FindUsages/FindUsagesPayload.php @@ -0,0 +1,32 @@ +query->getInt('id') ?: null, + type: $request->query->get('type'), + path: $request->query->get('path'), + limit: $request->query->getInt('limit', 50), + start: $request->query->getInt('start', 0), + sort: $request->query->get('sort'), + ); + } +} diff --git a/src/Handler/Element/FindUsages/FindUsagesResult.php b/src/Handler/Element/FindUsages/FindUsagesResult.php new file mode 100644 index 00000000..9a024bb4 --- /dev/null +++ b/src/Handler/Element/FindUsages/FindUsagesResult.php @@ -0,0 +1,14 @@ +id ?? ''; + $type = $payload->type ?? ''; + $baseUrl = $payload->baseUrl; $idList = explode(',', $ids); diff --git a/src/Handler/Element/GetDeleteInfo/GetDeleteInfoPayload.php b/src/Handler/Element/GetDeleteInfo/GetDeleteInfoPayload.php new file mode 100644 index 00000000..c6c9f0f0 --- /dev/null +++ b/src/Handler/Element/GetDeleteInfo/GetDeleteInfoPayload.php @@ -0,0 +1,26 @@ +query->get('id'), + type: $request->query->get('type'), + baseUrl: $request->getBaseUrl(), + ); + } +} diff --git a/src/Handler/Element/GetDependencies/GetDependenciesResult.php b/src/Handler/Element/GetDependencies/GetDependenciesResult.php new file mode 100644 index 00000000..b1e70081 --- /dev/null +++ b/src/Handler/Element/GetDependencies/GetDependenciesResult.php @@ -0,0 +1,12 @@ +query->getInt('id', 0), + type: $request->query->get('elementType'), + start: $request->query->getInt('start', 0), + limit: $request->query->getInt('limit', 25), + filter: $request->query->get('filter'), + ); + } +} diff --git a/src/Handler/Element/GetNicePathHandler.php b/src/Handler/Element/GetNicePath/GetNicePathHandler.php similarity index 80% rename from src/Handler/Element/GetNicePathHandler.php rename to src/Handler/Element/GetNicePath/GetNicePathHandler.php index a6e0c771..9d5841f5 100644 --- a/src/Handler/Element/GetNicePathHandler.php +++ b/src/Handler/Element/GetNicePath/GetNicePathHandler.php @@ -1,21 +1,8 @@ source; if ($source['type'] !== 'object') { throw new BadRequestHttpException('currently only objects as source elements are supported'); } @@ -42,13 +25,13 @@ public function __invoke( $id = $source['id']; $sourceObject = DataObject\Concrete::getById($id); - $ownerType = $context['containerType']; - $fieldname = $context['fieldname']; + $ownerType = $payload->context['containerType']; + $fieldname = $payload->context['fieldname']; - $fd = $this->getFieldDefinition($sourceObject, $context); - $result = $this->convertResultWithPathFormatter($sourceObject, $context, $result, $targets); + $fd = $this->getFieldDefinition($sourceObject, $payload->context); + $result = $this->convertResultWithPathFormatter($sourceObject, $payload->context, $result, $payload->targets); - if ($loadEditModeData) { + if ($payload->loadEditModeData) { $methodName = 'get' . ucfirst($fieldname); if ($ownerType === 'object' && method_exists($sourceObject, $methodName)) { $data = DataObject\Service::useInheritedValues(true, [$sourceObject, $methodName]); @@ -56,11 +39,11 @@ public function __invoke( // Inherited values show as an empty array if (is_array($editModeData) && $editModeData !== []) { foreach ($editModeData as $relationObjectAttribute) { - $relationObjectAttribute['$$nicepath'] = isset($relationObjectAttribute[$idProperty], $result[$relationObjectAttribute[$idProperty]]) - ? $result[$relationObjectAttribute[$idProperty]] + $relationObjectAttribute['$$nicepath'] = isset($relationObjectAttribute[$payload->idProperty], $result[$relationObjectAttribute[$payload->idProperty]]) + ? $result[$relationObjectAttribute[$payload->idProperty]] : null; - $result[$relationObjectAttribute[$idProperty]] = $relationObjectAttribute; + $result[$relationObjectAttribute[$payload->idProperty]] = $relationObjectAttribute; } } else { foreach ($result as $resultItemId => $resultItem) { diff --git a/src/Handler/Element/GetNicePath/GetNicePathPayload.php b/src/Handler/Element/GetNicePath/GetNicePathPayload.php new file mode 100644 index 00000000..93b898e1 --- /dev/null +++ b/src/Handler/Element/GetNicePath/GetNicePathPayload.php @@ -0,0 +1,47 @@ +|null */ + public readonly ?array $source; + + /** @var array|null */ + public readonly ?array $context; + + /** @var array|null */ + public readonly ?array $targets; + + public function __construct( + ?array $source, + ?array $context, + ?array $targets, + public readonly bool $loadEditModeData, + public readonly string $idProperty = 'id', + ) { + $this->source = $source; + $this->context = $context; + $this->targets = $targets; + } + + public static function fromRequest(Request $request): static + { + $source = $request->request->get('source'); + $context = $request->request->has('context') ? $request->request->get('context') : []; + $targets = $request->request->get('targets'); + + return new static( + source: $source !== null ? (json_decode($source, true) ?? null) : null, + context: $context !== [] ? (json_decode($context, true) ?? null) : null, + targets: $targets !== null ? (json_decode($targets, true) ?? null) : null, + loadEditModeData: $request->request->getBoolean('loadEditModeData'), + idProperty: $request->request->get('idProperty', 'id'), + ); + } +} diff --git a/src/Handler/Element/GetNicePath/GetNicePathResult.php b/src/Handler/Element/GetNicePath/GetNicePathResult.php new file mode 100644 index 00000000..9457e231 --- /dev/null +++ b/src/Handler/Element/GetNicePath/GetNicePathResult.php @@ -0,0 +1,12 @@ +ctype) { + 'document' => $this->documentNoteTypes, + 'asset' => $this->assetNoteTypes, + 'object' => $this->objectNoteTypes, + default => [], + }; + + $noteTypes = []; + foreach ($config as $noteType) { + $noteTypes[] = ['name' => $noteType]; + } + + return new GetNoteTypesResult(noteTypes: $noteTypes); + } +} diff --git a/src/Handler/Element/GetNoteTypes/GetNoteTypesPayload.php b/src/Handler/Element/GetNoteTypes/GetNoteTypesPayload.php new file mode 100644 index 00000000..6d082207 --- /dev/null +++ b/src/Handler/Element/GetNoteTypes/GetNoteTypesPayload.php @@ -0,0 +1,22 @@ +query->get('ctype'), + ); + } +} diff --git a/src/Handler/Element/GetNoteTypes/GetNoteTypesResult.php b/src/Handler/Element/GetNoteTypes/GetNoteTypesResult.php new file mode 100644 index 00000000..1d29e82a --- /dev/null +++ b/src/Handler/Element/GetNoteTypes/GetNoteTypesResult.php @@ -0,0 +1,12 @@ +elementType, $allowedTypes, true)) { + $list = new Model\Property\Predefined\Listing(); + $list->setFilter(function (Property\Predefined $predefined) use ($payload) { + if (!str_contains($predefined->getCtype(), $payload->elementType)) { + return false; + } + + return !($payload->query && stripos($this->translator->trans($predefined->getName(), [], 'admin'), (string) $payload->query) === false); + }); + + foreach ($list->getProperties() as $predefined) { + $properties[] = $predefined->getObjectVars(); + } + } + + return new GetPredefinedPropertiesResult(properties: $properties); + } +} diff --git a/src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesPayload.php b/src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesPayload.php new file mode 100644 index 00000000..6dd7bfb9 --- /dev/null +++ b/src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesPayload.php @@ -0,0 +1,24 @@ +query->get('elementType'), + query: $request->query->get('query'), + ); + } +} diff --git a/src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesResult.php b/src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesResult.php new file mode 100644 index 00000000..97a6384f --- /dev/null +++ b/src/Handler/Element/GetPredefinedProperties/GetPredefinedPropertiesResult.php @@ -0,0 +1,12 @@ +setFilter(function (Property\Predefined $predefined) use ($type, $query) { - if (!str_contains($predefined->getCtype(), $type)) { - return false; - } - - return !($query && stripos($this->translator->trans($predefined->getName(), [], 'admin'), (string) $query) === false); - }); - - foreach ($list->getProperties() as $predefined) { - $properties[] = $predefined->getObjectVars(); - } - } - - return new GetPredefinedPropertiesResult(properties: $properties); - } -} diff --git a/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsHandler.php b/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsHandler.php new file mode 100644 index 00000000..9b707e59 --- /dev/null +++ b/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsHandler.php @@ -0,0 +1,29 @@ +id) { + $element = Service::getElementById($payload->type, $payload->id); + } elseif ($payload->path) { + $element = Service::getElementByPath($payload->type, $payload->path); + } + + if (!$element instanceof ElementInterface) { + throw new NotFoundHttpException(); + } + + return new GetReplaceAssignmentsBatchJobsResult(jobs: $element->getDependencies()->getRequiredBy()); + } +} diff --git a/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsPayload.php b/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsPayload.php new file mode 100644 index 00000000..06103e5e --- /dev/null +++ b/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsPayload.php @@ -0,0 +1,26 @@ +query->getInt('id') ?: null, + type: $request->query->get('type'), + path: $request->query->get('path'), + ); + } +} diff --git a/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsResult.php b/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsResult.php new file mode 100644 index 00000000..becd4c84 --- /dev/null +++ b/src/Handler/Element/GetReplaceAssignmentsBatchJobs/GetReplaceAssignmentsBatchJobsResult.php @@ -0,0 +1,12 @@ +id || !in_array($payload->type, $allowedTypes)) { + return new GetDependenciesResult(data: false); + } + + $element = Model\Element\Service::getElementById($payload->type, $payload->id); + $dependencies = $element->getDependencies(); + + if ($payload->filter) { + $filters = json_decode($payload->filter, true) ?? []; + $value = null; + $elements = null; + + foreach ($filters as $filter) { + if ($filter['type'] === 'string') { + $value = ($filter['value'] ?? ''); + } + $elements = $element->getDependencies()->getFilterRequiredByPath($payload->start, $payload->limit, $value); + } + + if ($elements !== null && count($elements) > 0) { + $result = Model\Element\Service::getFilterRequiredByPathForFrontend($elements); + $result['total'] = count($result['requiredBy']); + + return new GetDependenciesResult(data: $result); + } + + return new GetDependenciesResult(data: $elements ?? []); + } + + if ($element instanceof Model\Element\ElementInterface) { + $dependenciesResult = Model\Element\Service::getRequiredByDependenciesForFrontend($dependencies, $payload->start, $payload->limit); + + $dependenciesResult['start'] = $payload->start; + $dependenciesResult['limit'] = $payload->limit; + $dependenciesResult['total'] = $dependencies->getRequiredByTotalCount(); + + return new GetDependenciesResult(data: $dependenciesResult); + } + + return new GetDependenciesResult(data: false); + } +} diff --git a/src/Handler/Element/GetRequiredByDependencies/GetRequiredByDependenciesPayload.php b/src/Handler/Element/GetRequiredByDependencies/GetRequiredByDependenciesPayload.php new file mode 100644 index 00000000..ed83002b --- /dev/null +++ b/src/Handler/Element/GetRequiredByDependencies/GetRequiredByDependenciesPayload.php @@ -0,0 +1,30 @@ +query->getInt('id', 0), + type: $request->query->get('elementType'), + start: $request->query->getInt('start', 0), + limit: $request->query->getInt('limit', 25), + filter: $request->query->get('filter'), + ); + } +} diff --git a/src/Handler/Element/GetRequiredByDependenciesHandler.php b/src/Handler/Element/GetRequiredByDependenciesHandler.php deleted file mode 100644 index bc1165eb..00000000 --- a/src/Handler/Element/GetRequiredByDependenciesHandler.php +++ /dev/null @@ -1,74 +0,0 @@ -getDependencies(); - - if ($filterJson) { - $filters = json_decode($filterJson, true) ?? []; - $value = null; - $elements = null; - - foreach ($filters as $filter) { - if ($filter['type'] === 'string') { - $value = ($filter['value'] ?? ''); - } - $elements = $element->getDependencies()->getFilterRequiredByPath($offset, $limit, $value); - } - - if ($elements !== null && count($elements) > 0) { - $result = Model\Element\Service::getFilterRequiredByPathForFrontend($elements); - $result['total'] = count($result['requiredBy']); - - return new GetRequiresDependenciesResult(data: $result); - } - - return new GetRequiresDependenciesResult(data: $elements ?? []); - } - - if ($element instanceof Model\Element\ElementInterface) { - $dependenciesResult = Model\Element\Service::getRequiredByDependenciesForFrontend($dependencies, $offset, $limit); - - $dependenciesResult['start'] = $offset; - $dependenciesResult['limit'] = $limit; - $dependenciesResult['total'] = $dependencies->getRequiredByTotalCount(); - - return new GetRequiresDependenciesResult(data: $dependenciesResult); - } - - return new GetRequiresDependenciesResult(data: false); - } -} diff --git a/src/Handler/Element/GetRequiresDependencies/GetRequiresDependenciesHandler.php b/src/Handler/Element/GetRequiresDependencies/GetRequiresDependenciesHandler.php new file mode 100644 index 00000000..92d31212 --- /dev/null +++ b/src/Handler/Element/GetRequiresDependencies/GetRequiresDependenciesHandler.php @@ -0,0 +1,58 @@ +id || !in_array($payload->type, $allowedTypes)) { + return new GetDependenciesResult(data: false); + } + + $element = Model\Element\Service::getElementById($payload->type, $payload->id); + $dependencies = $element->getDependencies(); + + if ($payload->filter) { + $filters = json_decode($payload->filter, true) ?? []; + $value = null; + $elements = null; + + foreach ($filters as $filter) { + if ($filter['type'] === 'string') { + $value = ($filter['value'] ?? ''); + } + $elements = $element->getDependencies()->getFilterRequiresByPath($payload->start, $payload->limit, $value); + } + + if ($elements !== null && count($elements) > 0) { + $result = Model\Element\Service::getFilterRequiresForFrontend($elements); + $result['total'] = count($result['requires']); + + return new GetDependenciesResult(data: $result); + } + + return new GetDependenciesResult(data: $elements ?? []); + } + + if ($element instanceof Model\Element\ElementInterface) { + $dependenciesResult = Model\Element\Service::getRequiresDependenciesForFrontend($dependencies, $payload->start, $payload->limit); + + $dependenciesResult['start'] = $payload->start; + $dependenciesResult['limit'] = $payload->limit; + $dependenciesResult['total'] = $dependencies->getRequiresTotalCount(); + + return new GetDependenciesResult(data: $dependenciesResult); + } + + return new GetDependenciesResult(data: false); + } +} diff --git a/src/Handler/Element/GetRequiresDependencies/GetRequiresDependenciesPayload.php b/src/Handler/Element/GetRequiresDependencies/GetRequiresDependenciesPayload.php new file mode 100644 index 00000000..17f659c8 --- /dev/null +++ b/src/Handler/Element/GetRequiresDependencies/GetRequiresDependenciesPayload.php @@ -0,0 +1,30 @@ +query->getInt('id', 0), + type: $request->query->get('elementType'), + start: $request->query->getInt('start', 0), + limit: $request->query->getInt('limit', 25), + filter: $request->query->get('filter'), + ); + } +} diff --git a/src/Handler/Element/GetRequiresDependenciesHandler.php b/src/Handler/Element/GetRequiresDependenciesHandler.php deleted file mode 100644 index ec7e90ec..00000000 --- a/src/Handler/Element/GetRequiresDependenciesHandler.php +++ /dev/null @@ -1,74 +0,0 @@ -getDependencies(); - - if ($filterJson) { - $filters = json_decode($filterJson, true) ?? []; - $value = null; - $elements = null; - - foreach ($filters as $filter) { - if ($filter['type'] === 'string') { - $value = ($filter['value'] ?? ''); - } - $elements = $element->getDependencies()->getFilterRequiresByPath($offset, $limit, $value); - } - - if ($elements !== null && count($elements) > 0) { - $result = Model\Element\Service::getFilterRequiresForFrontend($elements); - $result['total'] = count($result['requires']); - - return new GetRequiresDependenciesResult(data: $result); - } - - return new GetRequiresDependenciesResult(data: $elements ?? []); - } - - if ($element instanceof Model\Element\ElementInterface) { - $dependenciesResult = Model\Element\Service::getRequiresDependenciesForFrontend($dependencies, $offset, $limit); - - $dependenciesResult['start'] = $offset; - $dependenciesResult['limit'] = $limit; - $dependenciesResult['total'] = $dependencies->getRequiresTotalCount(); - - return new GetRequiresDependenciesResult(data: $dependenciesResult); - } - - return new GetRequiresDependenciesResult(data: false); - } -} diff --git a/src/Handler/Element/GetSubtypeHandler.php b/src/Handler/Element/GetSubtype/GetSubtypeHandler.php similarity index 65% rename from src/Handler/Element/GetSubtypeHandler.php rename to src/Handler/Element/GetSubtype/GetSubtypeHandler.php index f2556856..6700e2eb 100644 --- a/src/Handler/Element/GetSubtypeHandler.php +++ b/src/Handler/Element/GetSubtype/GetSubtypeHandler.php @@ -1,21 +1,8 @@ id); - $event = new ResolveElementEvent($type, $idOrPath); + $event = new ResolveElementEvent($payload->type, $idOrPath); OpenDxp::getEventDispatcher()->dispatch($event, AdminEvents::RESOLVE_ELEMENT); $idOrPath = $event->getId(); $resolvedType = $event->getType(); @@ -58,6 +45,6 @@ public function __invoke(string $id, ?string $type): GetSubtypeResult $subtype = 'folder'; } - return new GetSubtypeResult(subtype: $subtype, id: $el->getId(), type: $type); + return new GetSubtypeResult(subtype: $subtype, id: $el->getId(), type: $payload->type); } } diff --git a/src/Handler/Element/GetSubtype/GetSubtypePayload.php b/src/Handler/Element/GetSubtype/GetSubtypePayload.php new file mode 100644 index 00000000..5120e023 --- /dev/null +++ b/src/Handler/Element/GetSubtype/GetSubtypePayload.php @@ -0,0 +1,24 @@ +query->getString('id'), + type: $request->query->get('type'), + ); + } +} diff --git a/src/Handler/Element/GetSubtype/GetSubtypeResult.php b/src/Handler/Element/GetSubtype/GetSubtypeResult.php new file mode 100644 index 00000000..ffd365a2 --- /dev/null +++ b/src/Handler/Element/GetSubtype/GetSubtypeResult.php @@ -0,0 +1,14 @@ +userContext->getAdminUser(); $allowedTypes = ['asset', 'document', 'object']; - if (!$id || !in_array($type, $allowedTypes)) { + if (!$payload->id || !in_array($payload->elementType, $allowedTypes)) { throw new NotFoundHttpException('Element type not found'); } - $element = Model\Element\Service::getElementById($type, $id); + $element = Model\Element\Service::getElementById($payload->elementType, $payload->id); if (!$element) { - throw new NotFoundHttpException($type . ' with id [' . $id . "] doesn't exist"); + throw new NotFoundHttpException($payload->elementType . ' with id [' . $payload->id . "] doesn't exist"); } if (!$element->isAllowed('versions')) { - throw new AccessDeniedHttpException('Permission denied, ' . $type . ' id [' . $id . ']'); + throw new AccessDeniedHttpException('Permission denied, ' . $payload->elementType . ' id [' . $payload->id . ']'); } $schedule = $element->getScheduledTasks(); diff --git a/src/Handler/Element/GetVersions/GetVersionsPayload.php b/src/Handler/Element/GetVersions/GetVersionsPayload.php new file mode 100644 index 00000000..12436b20 --- /dev/null +++ b/src/Handler/Element/GetVersions/GetVersionsPayload.php @@ -0,0 +1,24 @@ +query->getInt('id', 0), + elementType: $request->query->get('elementType'), + ); + } +} diff --git a/src/Handler/Element/GetVersions/GetVersionsResult.php b/src/Handler/Element/GetVersions/GetVersionsResult.php new file mode 100644 index 00000000..32e84760 --- /dev/null +++ b/src/Handler/Element/GetVersions/GetVersionsResult.php @@ -0,0 +1,12 @@ +id, $payload->type, $payload->sessionId); + } +} diff --git a/src/Handler/Element/LockElement/LockElementPayload.php b/src/Handler/Element/LockElement/LockElementPayload.php new file mode 100644 index 00000000..b912759e --- /dev/null +++ b/src/Handler/Element/LockElement/LockElementPayload.php @@ -0,0 +1,26 @@ +request->getInt('id'), + type: $request->request->getString('type'), + sessionId: $request->getSession()->getId(), + ); + } +} diff --git a/src/Handler/Element/LockElement/LockElementResult.php b/src/Handler/Element/LockElement/LockElementResult.php new file mode 100644 index 00000000..ed674b39 --- /dev/null +++ b/src/Handler/Element/LockElement/LockElementResult.php @@ -0,0 +1,10 @@ +userContext->getAdminUser(); - $element = Element\Service::getElementById($type, $id); - $sourceEl = Element\Service::getElementById($sourceType, $sourceId); - $targetEl = Element\Service::getElementById($targetType, $targetId); + $element = Element\Service::getElementById($payload->type, $payload->id); + $sourceEl = Element\Service::getElementById($payload->sourceType, $payload->sourceId); + $targetEl = Element\Service::getElementById($payload->targetType, $payload->targetId); if (!$element || !$sourceEl || !$targetEl) { throw new NotFoundHttpException('One or more elements not found'); } - if ($sourceType !== $targetType || $sourceEl->getType() !== $targetEl->getType()) { + if ($payload->sourceType !== $payload->targetType || $sourceEl->getType() !== $targetEl->getType()) { throw new BadRequestHttpException('source-type and target-type do not match'); } @@ -59,7 +40,7 @@ public function __invoke( } $rewriteConfig = [ - $sourceType => [ + $payload->sourceType => [ $sourceEl->getId() => $targetEl->getId(), ], ]; diff --git a/src/Handler/Element/ReplaceAssignments/ReplaceAssignmentsPayload.php b/src/Handler/Element/ReplaceAssignments/ReplaceAssignmentsPayload.php new file mode 100644 index 00000000..3f56a998 --- /dev/null +++ b/src/Handler/Element/ReplaceAssignments/ReplaceAssignmentsPayload.php @@ -0,0 +1,32 @@ +request->get('type'), + id: $request->request->getInt('id'), + sourceType: $request->request->get('sourceType'), + sourceId: $request->request->getInt('sourceId'), + targetType: $request->request->get('targetType'), + targetId: $request->request->getInt('targetId'), + ); + } +} diff --git a/src/Handler/Element/ReplaceAssignments/ReplaceAssignmentsResult.php b/src/Handler/Element/ReplaceAssignments/ReplaceAssignmentsResult.php new file mode 100644 index 00000000..f10e6945 --- /dev/null +++ b/src/Handler/Element/ReplaceAssignments/ReplaceAssignmentsResult.php @@ -0,0 +1,10 @@ +type === 'asset') { + $element = Asset::getById($payload->id); + } elseif ($payload->type === 'document') { + $element = Document::getById($payload->id); + } else { + $element = DataObject::getById($payload->id); + } + + if (!$element) { + throw new NotFoundHttpException('Element not found'); + } + + return new TypePathResult( + index: method_exists($element, 'getIndex') ? (int) $element->getIndex() : 0, + idPath: Element\Service::getIdPath($element), + typePath: Element\Service::getTypePath($element), + fullpath: $element->getRealFullPath(), + sortIndexPath: $payload->type !== 'asset' ? Element\Service::getSortIndexPath($element) : null, + ); + } +} diff --git a/src/Handler/Element/TypePath/TypePathPayload.php b/src/Handler/Element/TypePath/TypePathPayload.php new file mode 100644 index 00000000..9b3d86ae --- /dev/null +++ b/src/Handler/Element/TypePath/TypePathPayload.php @@ -0,0 +1,24 @@ +query->getInt('id', 0), + type: $request->query->get('type'), + ); + } +} diff --git a/src/Handler/Element/TypePath/TypePathResult.php b/src/Handler/Element/TypePath/TypePathResult.php new file mode 100644 index 00000000..3a603e32 --- /dev/null +++ b/src/Handler/Element/TypePath/TypePathResult.php @@ -0,0 +1,16 @@ +getIndex() : 0, - idPath: Element\Service::getIdPath($element), - typePath: Element\Service::getTypePath($element), - fullpath: $element->getRealFullPath(), - sortIndexPath: $type !== 'asset' ? Element\Service::getSortIndexPath($element) : null, - ); - } -} diff --git a/src/Handler/Element/TypePathResult.php b/src/Handler/Element/TypePathResult.php deleted file mode 100644 index 6fd90389..00000000 --- a/src/Handler/Element/TypePathResult.php +++ /dev/null @@ -1,29 +0,0 @@ -id, $payload->type); + } +} diff --git a/src/Handler/Element/UnlockElement/UnlockElementPayload.php b/src/Handler/Element/UnlockElement/UnlockElementPayload.php new file mode 100644 index 00000000..dc57f11e --- /dev/null +++ b/src/Handler/Element/UnlockElement/UnlockElementPayload.php @@ -0,0 +1,24 @@ +request->getInt('id'), + type: $request->request->getString('type'), + ); + } +} diff --git a/src/Handler/Element/UnlockElement/UnlockElementResult.php b/src/Handler/Element/UnlockElement/UnlockElementResult.php new file mode 100644 index 00000000..0c2d1e72 --- /dev/null +++ b/src/Handler/Element/UnlockElement/UnlockElementResult.php @@ -0,0 +1,10 @@ + $elements */ + public function __invoke(UnlockElementsPayload $payload): void + { + foreach ($payload->elements as $element) { + Editlock::unlock((int) $element['id'], $element['type']); + } + } +} diff --git a/src/Handler/Element/UnlockElements/UnlockElementsPayload.php b/src/Handler/Element/UnlockElements/UnlockElementsPayload.php new file mode 100644 index 00000000..200ba2bc --- /dev/null +++ b/src/Handler/Element/UnlockElements/UnlockElementsPayload.php @@ -0,0 +1,25 @@ + $elements */ + public function __construct( + public readonly array $elements, + ) {} + + public static function fromRequest(Request $request): static + { + $body = json_decode($request->getContent(), true) ?? []; + + return new static( + elements: $body['elements'] ?? [], + ); + } +} diff --git a/src/Handler/Element/UnlockElements/UnlockElementsResult.php b/src/Handler/Element/UnlockElements/UnlockElementsResult.php new file mode 100644 index 00000000..8014c1dd --- /dev/null +++ b/src/Handler/Element/UnlockElements/UnlockElementsResult.php @@ -0,0 +1,10 @@ + $elements */ - public function __invoke(array $elements): void - { - foreach ($elements as $element) { - Editlock::unlock((int) $element['id'], $element['type']); - } - } -} diff --git a/src/Handler/Element/UnlockPropagate/UnlockPropagateHandler.php b/src/Handler/Element/UnlockPropagate/UnlockPropagateHandler.php new file mode 100644 index 00000000..63c7eaee --- /dev/null +++ b/src/Handler/Element/UnlockPropagate/UnlockPropagateHandler.php @@ -0,0 +1,22 @@ +type, $payload->id); + if (!$element) { + return new UnlockPropagateResult(success: false); + } + + $element->unlockPropagate(); + + return new UnlockPropagateResult(success: true); + } +} diff --git a/src/Handler/Element/UnlockPropagate/UnlockPropagatePayload.php b/src/Handler/Element/UnlockPropagate/UnlockPropagatePayload.php new file mode 100644 index 00000000..08667d92 --- /dev/null +++ b/src/Handler/Element/UnlockPropagate/UnlockPropagatePayload.php @@ -0,0 +1,24 @@ +request->get('type'), + id: $request->request->getInt('id'), + ); + } +} diff --git a/src/Handler/Element/UnlockPropagate/UnlockPropagateResult.php b/src/Handler/Element/UnlockPropagate/UnlockPropagateResult.php new file mode 100644 index 00000000..d4100da1 --- /dev/null +++ b/src/Handler/Element/UnlockPropagate/UnlockPropagateResult.php @@ -0,0 +1,12 @@ +data ?? []; + $version = Version::getById($data['id']); + + if ($version && ($data['public'] != $version->getPublic() || $data['note'] != $version->getNote())) { + $version->setPublic($data['public']); + $version->setNote($data['note']); + $version->save(); + } + } +} diff --git a/src/Handler/Element/VersionUpdate/VersionUpdatePayload.php b/src/Handler/Element/VersionUpdate/VersionUpdatePayload.php new file mode 100644 index 00000000..b043301d --- /dev/null +++ b/src/Handler/Element/VersionUpdate/VersionUpdatePayload.php @@ -0,0 +1,30 @@ +|null */ + public readonly ?array $data; + + public function __construct(?array $data) + { + $this->data = $data; + } + + public static function fromRequest(Request $request): static + { + $data = $request->request->get('data'); + + return new static( + data: $data !== null + ? (json_decode($data, true) ?? null) + : null, + ); + } +} diff --git a/src/Handler/Element/VersionUpdate/VersionUpdateResult.php b/src/Handler/Element/VersionUpdate/VersionUpdateResult.php new file mode 100644 index 00000000..f2fd2bb6 --- /dev/null +++ b/src/Handler/Element/VersionUpdate/VersionUpdateResult.php @@ -0,0 +1,10 @@ +getPublic() || $data['note'] != $version->getNote()) { - $version->setPublic($data['public']); - $version->setNote($data['note']); - $version->save(); - } - } -} diff --git a/src/Handler/Email/Blocklist/CreateBlocklistEntry/CreateBlocklistEntryHandler.php b/src/Handler/Email/Blocklist/CreateBlocklistEntry/CreateBlocklistEntryHandler.php new file mode 100644 index 00000000..d9c7a909 --- /dev/null +++ b/src/Handler/Email/Blocklist/CreateBlocklistEntry/CreateBlocklistEntryHandler.php @@ -0,0 +1,23 @@ +data; + unset($data['id']); + + $address = new Tool\Email\Blocklist(); + $address->setValues($data); + $address->save(); + + return $address->getObjectVars(); + } +} diff --git a/src/Handler/Email/Blocklist/DeleteBlocklistEntry/DeleteBlocklistEntryHandler.php b/src/Handler/Email/Blocklist/DeleteBlocklistEntry/DeleteBlocklistEntryHandler.php new file mode 100644 index 00000000..877f133d --- /dev/null +++ b/src/Handler/Email/Blocklist/DeleteBlocklistEntry/DeleteBlocklistEntryHandler.php @@ -0,0 +1,17 @@ +data['address']); + $entry->delete(); + } +} diff --git a/src/Handler/Email/Blocklist/UpdateBlocklistEntry/UpdateBlocklistEntryHandler.php b/src/Handler/Email/Blocklist/UpdateBlocklistEntry/UpdateBlocklistEntryHandler.php new file mode 100644 index 00000000..5b7c3614 --- /dev/null +++ b/src/Handler/Email/Blocklist/UpdateBlocklistEntry/UpdateBlocklistEntryHandler.php @@ -0,0 +1,20 @@ +data['address']); + $address->setValues($payload->data); + $address->save(); + + return $address->getObjectVars(); + } +} diff --git a/src/Handler/Email/BlocklistPayload.php b/src/Handler/Email/BlocklistPayload.php index 9d9c1982..c7623216 100644 --- a/src/Handler/Email/BlocklistPayload.php +++ b/src/Handler/Email/BlocklistPayload.php @@ -1,17 +1,5 @@ data; - unset($data['id']); - - $address = new Tool\Email\Blocklist(); - $address->setValues($data); - $address->save(); - - return $address->getObjectVars(); - } -} diff --git a/src/Handler/Email/DeleteBlocklistEntryHandler.php b/src/Handler/Email/DeleteBlocklistEntryHandler.php deleted file mode 100644 index dc632cc4..00000000 --- a/src/Handler/Email/DeleteBlocklistEntryHandler.php +++ /dev/null @@ -1,29 +0,0 @@ -data['address']); - $entry->delete(); - } -} diff --git a/src/Handler/Email/DeleteEmailLog/DeleteEmailLogHandler.php b/src/Handler/Email/DeleteEmailLog/DeleteEmailLogHandler.php new file mode 100644 index 00000000..232b75b8 --- /dev/null +++ b/src/Handler/Email/DeleteEmailLog/DeleteEmailLogHandler.php @@ -0,0 +1,22 @@ +id); + if (!$emailLog instanceof Tool\Email\Log) { + throw new NotFoundHttpException('Email log with ID ' . $payload->id . ' not found.'); + } + + $emailLog->delete(); + } +} diff --git a/src/Handler/Email/DeleteEmailLogHandler.php b/src/Handler/Email/DeleteEmailLogHandler.php deleted file mode 100644 index 78b47738..00000000 --- a/src/Handler/Email/DeleteEmailLogHandler.php +++ /dev/null @@ -1,34 +0,0 @@ -delete(); - } -} diff --git a/src/Handler/Email/GetBlocklistHandler.php b/src/Handler/Email/GetBlocklist/GetBlocklistHandler.php similarity index 63% rename from src/Handler/Email/GetBlocklistHandler.php rename to src/Handler/Email/GetBlocklist/GetBlocklistHandler.php index 8e42aacd..e13e3750 100644 --- a/src/Handler/Email/GetBlocklistHandler.php +++ b/src/Handler/Email/GetBlocklist/GetBlocklistHandler.php @@ -1,23 +1,11 @@ getTextLog(), - htmlLog: $log->getHtmlLog(), - objectVars: $log->getObjectVars(), - ); - } -} diff --git a/src/Handler/Email/GetEmailLogResult.php b/src/Handler/Email/GetEmailLogResult.php deleted file mode 100644 index 14864edb..00000000 --- a/src/Handler/Email/GetEmailLogResult.php +++ /dev/null @@ -1,27 +0,0 @@ -setCondition('documentId = ' . $documentId); + if ($payload->documentId !== null) { + $list->setCondition('documentId = ' . $payload->documentId); } - $list->setLimit($limit); - $list->setOffset($offset); + $list->setLimit($payload->limit); + $list->setOffset($payload->start); $list->setOrderKey('sentDate'); $list->setOrder('DESC'); - if ($filter !== null) { - if ($filter === '*') { - $filter = ''; - } + if ($payload->filter !== null) { + $filter = $payload->filter === '*' ? '' : $payload->filter; $filter = str_replace('%', '*', $filter); $filter = htmlspecialchars($filter, ENT_QUOTES); @@ -64,8 +45,8 @@ public function __invoke( $condition = '( MATCH (`from`,`to`,`cc`,`bcc`,`subject`,`params`) AGAINST (' . $list->quote($filter) . ' IN BOOLEAN MODE) )'; - if ($documentId !== null) { - $condition .= 'AND documentId = ' . $documentId; + if ($payload->documentId !== null) { + $condition .= 'AND documentId = ' . $payload->documentId; } $list->setCondition($condition); diff --git a/src/Handler/Email/GetEmailLogs/GetEmailLogsPayload.php b/src/Handler/Email/GetEmailLogs/GetEmailLogsPayload.php new file mode 100644 index 00000000..c425777a --- /dev/null +++ b/src/Handler/Email/GetEmailLogs/GetEmailLogsPayload.php @@ -0,0 +1,28 @@ +request->has('documentId') ? (int) $request->request->get('documentId') : null, + limit: (int) $request->request->get('limit', 50), + start: (int) $request->request->get('start', 0), + filter: $request->request->has('filter') ? $request->request->get('filter') : null, + ); + } +} diff --git a/src/Handler/Email/GetEmailLogs/GetEmailLogsResult.php b/src/Handler/Email/GetEmailLogs/GetEmailLogsResult.php new file mode 100644 index 00000000..7a26f488 --- /dev/null +++ b/src/Handler/Email/GetEmailLogs/GetEmailLogsResult.php @@ -0,0 +1,13 @@ + $fieldOverrides Keys: 'from', 'to', 'cc', 'bcc', 'replyto' - */ - public function __invoke(int $id, array $fieldOverrides): void + /** @param array $fieldOverrides Keys: 'from', 'to', 'cc', 'bcc', 'replyto' */ + public function __invoke(ResendEmailPayload $payload): void { - $emailLog = Tool\Email\Log::getById($id); + $emailLog = Tool\Email\Log::getById($payload->id); if (!$emailLog instanceof Tool\Email\Log) { - throw new NotFoundHttpException('Email log with ID ' . $id . ' not found.'); + throw new NotFoundHttpException('Email log with ID ' . $payload->id . ' not found.'); } $mail = new Mail(); $mail->preventDebugInformationAppending(); $mail->setIgnoreDebugMode(true); - if (!empty($fieldOverrides['to'])) { + if (!empty($payload->fieldOverrides['to'])) { $emailLog->setTo(null); $emailLog->setCc(null); $emailLog->setBcc(null); @@ -59,8 +44,8 @@ public function __invoke(int $id, array $fieldOverrides): void foreach (['From', 'To', 'Cc', 'Bcc', 'ReplyTo'] as $field) { $overrideKey = strtolower($field); - if (!empty($fieldOverrides[$overrideKey])) { - $values = $fieldOverrides[$overrideKey]; + if (!empty($payload->fieldOverrides[$overrideKey])) { + $values = $payload->fieldOverrides[$overrideKey]; } else { $getter = 'get' . $field; $values = $emailLog->{$getter}(); diff --git a/src/Handler/Email/ResendEmail/ResendEmailPayload.php b/src/Handler/Email/ResendEmail/ResendEmailPayload.php new file mode 100644 index 00000000..a24ee5d2 --- /dev/null +++ b/src/Handler/Email/ResendEmail/ResendEmailPayload.php @@ -0,0 +1,35 @@ + */ + public readonly array $fieldOverrides; + + public function __construct( + public readonly int $id, + ?array $fieldOverrides, + ) { + $this->fieldOverrides = $fieldOverrides ?? []; + } + + public static function fromRequest(Request $request): static + { + return new static( + id: (int) $request->request->get('id'), + fieldOverrides: [ + 'from' => $request->request->get('from') ?: null, + 'to' => $request->request->get('to') ?: null, + 'cc' => $request->request->get('cc') ?: null, + 'bcc' => $request->request->get('bcc') ?: null, + 'replyto' => $request->request->get('replyto') ?: null, + ], + ); + } +} diff --git a/src/Handler/Email/SendTestEmail/SendTestEmailHandler.php b/src/Handler/Email/SendTestEmail/SendTestEmailHandler.php new file mode 100644 index 00000000..65b51a87 --- /dev/null +++ b/src/Handler/Email/SendTestEmail/SendTestEmailHandler.php @@ -0,0 +1,57 @@ +emailType === 'text') { + $mail->text(strip_tags($payload->content ?? '')); + } elseif ($payload->emailType === 'html') { + $mail->html($payload->content ?? ''); + } elseif ($payload->emailType === 'document') { + $doc = Document::getByPath($payload->documentPath ?? ''); + + if (!$doc instanceof Document\Email) { + throw new BadRequestHttpException('Email document not found!'); + } + + $mail->setDocument($doc); + + if ($payload->mailParameters) { + foreach ($payload->mailParameters as $mailParam) { + if ($mailParam['key']) { + $mail->setParam($mailParam['key'], $mailParam['value']); + } + } + } + } + + if ($payload->from) { + $addressArray = \OpenDxp\Helper\Mail::parseEmailAddressField($payload->from); + if ($addressArray) { + [$cleanedFromAddress] = $addressArray; + $mail->from(new Address($cleanedFromAddress['email'], $cleanedFromAddress['name'])); + } + } + + $toAddresses = \OpenDxp\Helper\Mail::parseEmailAddressField($payload->to); + foreach ($toAddresses as $cleanedToAddress) { + $mail->addTo($cleanedToAddress['email'], $cleanedToAddress['name']); + } + + $mail->subject($payload->subject); + $mail->setIgnoreDebugMode(true); + $mail->send(); + } +} diff --git a/src/Handler/Email/SendTestEmail/SendTestEmailPayload.php b/src/Handler/Email/SendTestEmail/SendTestEmailPayload.php new file mode 100644 index 00000000..2015af4e --- /dev/null +++ b/src/Handler/Email/SendTestEmail/SendTestEmailPayload.php @@ -0,0 +1,44 @@ +|null */ + public readonly ?array $mailParameters; + + public function __construct( + public readonly string $emailType, + public readonly ?string $content, + public readonly ?string $documentPath, + ?array $mailParameters, + public readonly ?string $from, + public readonly string $to, + public readonly string $subject, + ) { + $this->mailParameters = $mailParameters; + } + + public static function fromRequest(Request $request): static + { + $mailParameters = null; + if ($request->request->has('mailParamaters')) { + $mailParameters = json_decode($request->request->get('mailParamaters'), true) ?: null; + } + + return new static( + emailType: (string) $request->request->get('emailType'), + content: $request->request->get('content'), + documentPath: $request->request->get('documentPath'), + mailParameters: $mailParameters, + from: $request->request->get('from'), + to: (string) $request->request->get('to'), + subject: (string) $request->request->get('subject'), + ); + } +} diff --git a/src/Handler/Email/SendTestEmailHandler.php b/src/Handler/Email/SendTestEmailHandler.php deleted file mode 100644 index bcf58349..00000000 --- a/src/Handler/Email/SendTestEmailHandler.php +++ /dev/null @@ -1,77 +0,0 @@ -text(strip_tags($content ?? '')); - } elseif ($emailType === 'html') { - $mail->html($content ?? ''); - } elseif ($emailType === 'document') { - $doc = Document::getByPath($documentPath ?? ''); - - if (!$doc instanceof Document\Email) { - throw new BadRequestHttpException('Email document not found!'); - } - - $mail->setDocument($doc); - - if ($mailParameters) { - foreach ($mailParameters as $mailParam) { - if ($mailParam['key']) { - $mail->setParam($mailParam['key'], $mailParam['value']); - } - } - } - } - - if ($from) { - $addressArray = \OpenDxp\Helper\Mail::parseEmailAddressField($from); - if ($addressArray) { - [$cleanedFromAddress] = $addressArray; - $mail->from(new Address($cleanedFromAddress['email'], $cleanedFromAddress['name'])); - } - } - - $toAddresses = \OpenDxp\Helper\Mail::parseEmailAddressField($to); - foreach ($toAddresses as $cleanedToAddress) { - $mail->addTo($cleanedToAddress['email'], $cleanedToAddress['name']); - } - - $mail->subject($subject); - $mail->setIgnoreDebugMode(true); - $mail->send(); - } -} diff --git a/src/Handler/Email/GetEmailLogParamsHandler.php b/src/Handler/Email/ShowEmailLog/GetEmailLogParams/GetEmailLogParamsHandler.php similarity index 85% rename from src/Handler/Email/GetEmailLogParamsHandler.php rename to src/Handler/Email/ShowEmailLog/GetEmailLogParams/GetEmailLogParamsHandler.php index 4350bced..36dfa928 100644 --- a/src/Handler/Email/GetEmailLogParamsHandler.php +++ b/src/Handler/Email/ShowEmailLog/GetEmailLogParams/GetEmailLogParamsHandler.php @@ -1,21 +1,8 @@ 'opendxp_icon_image', 'Video' => 'opendxp_icon_wmv', 'Text' => 'opendxp_icon_txt', diff --git a/src/Handler/Email/ShowEmailLog/GetEmailLogResult.php b/src/Handler/Email/ShowEmailLog/GetEmailLogResult.php new file mode 100644 index 00000000..15c00d2e --- /dev/null +++ b/src/Handler/Email/ShowEmailLog/GetEmailLogResult.php @@ -0,0 +1,14 @@ +getTextLog() ?: null, + htmlLog: $log->getHtmlLog() ?: null, + objectVars: $log->getObjectVars(), + ); + } +} diff --git a/src/Handler/Email/ShowEmailLog/ShowEmailLogPayload.php b/src/Handler/Email/ShowEmailLog/ShowEmailLogPayload.php new file mode 100644 index 00000000..75cfce8a --- /dev/null +++ b/src/Handler/Email/ShowEmailLog/ShowEmailLogPayload.php @@ -0,0 +1,24 @@ +query->get('type'), + id: $request->query->getInt('id', 0), + ); + } +} diff --git a/src/Handler/Email/UpdateBlocklistEntryHandler.php b/src/Handler/Email/UpdateBlocklistEntryHandler.php deleted file mode 100644 index 00272d9c..00000000 --- a/src/Handler/Email/UpdateBlocklistEntryHandler.php +++ /dev/null @@ -1,32 +0,0 @@ -data['address']); - $address->setValues($payload->data); - $address->save(); - - return $address->getObjectVars(); - } -} diff --git a/src/Handler/GDPR/Asset/ExportAsset/ExportAssetHandler.php b/src/Handler/GDPR/Asset/ExportAsset/ExportAssetHandler.php new file mode 100644 index 00000000..0bc094a0 --- /dev/null +++ b/src/Handler/GDPR/Asset/ExportAsset/ExportAssetHandler.php @@ -0,0 +1,44 @@ +id); + if (!$asset) { + throw new NotFoundHttpException('Asset not found'); + } + if (!$asset->isAllowed('view')) { + throw new AccessDeniedHttpException('Export denied'); + } + + return new ExportAssetResult($this->assets->doExportData($asset)); + } +} diff --git a/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsResult.php b/src/Handler/GDPR/Asset/ExportAsset/ExportAssetResult.php similarity index 74% rename from src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsResult.php rename to src/Handler/GDPR/Asset/ExportAsset/ExportAssetResult.php index 2503d303..9838ea12 100644 --- a/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsResult.php +++ b/src/Handler/GDPR/Asset/ExportAsset/ExportAssetResult.php @@ -15,12 +15,13 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIds; +namespace OpenDxp\Bundle\AdminBundle\Handler\GDPR\Asset\ExportAsset; -final readonly class GetDataObjectChildIdsResult +use Symfony\Component\HttpFoundation\Response; + +final readonly class ExportAssetResult { - /** @param int[] $ids */ public function __construct( - public array $ids, + public Response $response, ) {} } diff --git a/src/Handler/GDPR/Asset/SearchAssets/SearchAssetsHandler.php b/src/Handler/GDPR/Asset/SearchAssets/SearchAssetsHandler.php new file mode 100644 index 00000000..7e898d5c --- /dev/null +++ b/src/Handler/GDPR/Asset/SearchAssets/SearchAssetsHandler.php @@ -0,0 +1,43 @@ +assets->searchData( + $payload->id, + $payload->firstname, + $payload->lastname, + $payload->email, + $payload->start, + $payload->limit, + $payload->sort, + ); + + return new SearchAssetsResult($result); + } +} diff --git a/src/Handler/Email/GetBlocklistResult.php b/src/Handler/GDPR/Asset/SearchAssets/SearchAssetsResult.php similarity index 83% rename from src/Handler/Email/GetBlocklistResult.php rename to src/Handler/GDPR/Asset/SearchAssets/SearchAssetsResult.php index 523ed80b..41bd58d6 100644 --- a/src/Handler/Email/GetBlocklistResult.php +++ b/src/Handler/GDPR/Asset/SearchAssets/SearchAssetsResult.php @@ -15,12 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Email; +namespace OpenDxp\Bundle\AdminBundle\Handler\GDPR\Asset\SearchAssets; -final readonly class GetBlocklistResult +final readonly class SearchAssetsResult { public function __construct( public array $data, - public int $total, ) {} } diff --git a/src/Handler/GDPR/DataObject/ExportDataObject/ExportDataObjectHandler.php b/src/Handler/GDPR/DataObject/ExportDataObject/ExportDataObjectHandler.php new file mode 100644 index 00000000..67e1e067 --- /dev/null +++ b/src/Handler/GDPR/DataObject/ExportDataObject/ExportDataObjectHandler.php @@ -0,0 +1,44 @@ +id); + if (!$object) { + throw new NotFoundHttpException('Object not found'); + } + if (!$object->isAllowed('view')) { + throw new AccessDeniedHttpException('Export denied'); + } + + return new ExportDataObjectResult($this->dataObjects->doExportData($object), $object->getId()); + } +} diff --git a/src/Handler/GDPR/DataObject/ExportDataObject/ExportDataObjectResult.php b/src/Handler/GDPR/DataObject/ExportDataObject/ExportDataObjectResult.php new file mode 100644 index 00000000..d5ac5d2a --- /dev/null +++ b/src/Handler/GDPR/DataObject/ExportDataObject/ExportDataObjectResult.php @@ -0,0 +1,26 @@ +dataObjects->searchData( + $payload->id, + $payload->firstname, + $payload->lastname, + $payload->email, + $payload->start, + $payload->limit, + $payload->sort, + ); + + return new SearchDataObjectsResult($result); + } +} diff --git a/src/Handler/GDPR/DataObject/SearchDataObjects/SearchDataObjectsResult.php b/src/Handler/GDPR/DataObject/SearchDataObjects/SearchDataObjectsResult.php new file mode 100644 index 00000000..e4fa424a --- /dev/null +++ b/src/Handler/GDPR/DataObject/SearchDataObjects/SearchDataObjectsResult.php @@ -0,0 +1,25 @@ +manager->getServices() as $service) { + $providers[] = [ + 'name' => $service->getName(), + 'jsClass' => $service->getJsClassName(), + ]; + } + + return new GetDataProvidersResult($providers); + } +} diff --git a/src/Handler/GDPR/GetDataProviders/GetDataProvidersResult.php b/src/Handler/GDPR/GetDataProviders/GetDataProvidersResult.php new file mode 100644 index 00000000..6dc89bae --- /dev/null +++ b/src/Handler/GDPR/GetDataProviders/GetDataProvidersResult.php @@ -0,0 +1,25 @@ +openDxpUsers->getExportData($payload->id)); + } +} diff --git a/src/Handler/Element/GetNoteListResult.php b/src/Handler/GDPR/OpenDxpUsers/ExportUserData/ExportUserDataResult.php similarity index 82% rename from src/Handler/Element/GetNoteListResult.php rename to src/Handler/GDPR/OpenDxpUsers/ExportUserData/ExportUserDataResult.php index 5098e589..f711b567 100644 --- a/src/Handler/Element/GetNoteListResult.php +++ b/src/Handler/GDPR/OpenDxpUsers/ExportUserData/ExportUserDataResult.php @@ -15,12 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Handler\GDPR\OpenDxpUsers\ExportUserData; -final readonly class GetNoteListResult +final readonly class ExportUserDataResult { public function __construct( public array $data, - public int $total, ) {} } diff --git a/src/Handler/GDPR/OpenDxpUsers/SearchUsers/SearchUsersHandler.php b/src/Handler/GDPR/OpenDxpUsers/SearchUsers/SearchUsersHandler.php new file mode 100644 index 00000000..eb6b25a7 --- /dev/null +++ b/src/Handler/GDPR/OpenDxpUsers/SearchUsers/SearchUsersHandler.php @@ -0,0 +1,43 @@ +openDxpUsers->searchData( + $payload->id, + $payload->firstname, + $payload->lastname, + $payload->email, + $payload->start, + $payload->limit, + $payload->sort, + ); + + return new SearchUsersResult($result); + } +} diff --git a/src/Handler/GDPR/OpenDxpUsers/SearchUsers/SearchUsersResult.php b/src/Handler/GDPR/OpenDxpUsers/SearchUsers/SearchUsersResult.php new file mode 100644 index 00000000..a523e011 --- /dev/null +++ b/src/Handler/GDPR/OpenDxpUsers/SearchUsers/SearchUsersResult.php @@ -0,0 +1,25 @@ +query->all(); + + return new static( + id: (int)$allParams['id'], + firstname: strip_tags($allParams['firstname']), + lastname: strip_tags($allParams['lastname']), + email: strip_tags($allParams['email']), + start: (int)$allParams['start'], + limit: (int)$allParams['limit'], + sort: $allParams['sort'] ?? null, + ); + } +} diff --git a/src/Handler/Element/GetReplaceAssignmentsBatchJobsHandler.php b/src/Handler/GDPR/SentMail/ExportSentMail/ExportSentMailHandler.php similarity index 52% rename from src/Handler/Element/GetReplaceAssignmentsBatchJobsHandler.php rename to src/Handler/GDPR/SentMail/ExportSentMail/ExportSentMailHandler.php index 90b1b283..a89185e8 100644 --- a/src/Handler/Element/GetReplaceAssignmentsBatchJobsHandler.php +++ b/src/Handler/GDPR/SentMail/ExportSentMail/ExportSentMailHandler.php @@ -15,28 +15,25 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Handler\GDPR\SentMail\ExportSentMail; -use OpenDxp\Model\Element\ElementInterface; -use OpenDxp\Model\Element\Service; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; +use OpenDxp\Model\Tool\Email\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -final class GetReplaceAssignmentsBatchJobsHandler +final class ExportSentMailHandler { - public function __invoke(?int $id, ?string $type, ?string $path): array + public function __invoke(IdQueryPayload $payload): ExportSentMailResult { - $element = null; - - if ($id) { - $element = Service::getElementById($type, $id); - } elseif ($path) { - $element = Service::getElementByPath($type, $path); - } - - if (!$element instanceof ElementInterface) { + $sentMail = Log::getById($payload->id); + if (!$sentMail) { throw new NotFoundHttpException(); } - return $element->getDependencies()->getRequiredBy(); + $sentMailArray = (array)$sentMail; + $sentMailArray['htmlBody'] = $sentMail->getHtmlLog(); + $sentMailArray['textBody'] = $sentMail->getTextLog(); + + return new ExportSentMailResult($sentMailArray, $sentMail->getId()); } } diff --git a/src/Handler/GDPR/SentMail/ExportSentMail/ExportSentMailResult.php b/src/Handler/GDPR/SentMail/ExportSentMail/ExportSentMailResult.php new file mode 100644 index 00000000..41e720e8 --- /dev/null +++ b/src/Handler/GDPR/SentMail/ExportSentMail/ExportSentMailResult.php @@ -0,0 +1,26 @@ +db); + $viewParams['headless'] = $payload->headless; + + return new CheckSystemResult(viewParams: $viewParams); + } +} diff --git a/src/Handler/Install/CheckSystem/CheckSystemPayload.php b/src/Handler/Install/CheckSystem/CheckSystemPayload.php new file mode 100644 index 00000000..0474af0b --- /dev/null +++ b/src/Handler/Install/CheckSystem/CheckSystemPayload.php @@ -0,0 +1,22 @@ +query->getBoolean('headless') || $request->request->getBoolean('headless'), + ); + } +} diff --git a/src/Handler/Install/CheckSystem/CheckSystemResult.php b/src/Handler/Install/CheckSystem/CheckSystemResult.php new file mode 100644 index 00000000..a767e96d --- /dev/null +++ b/src/Handler/Install/CheckSystem/CheckSystemResult.php @@ -0,0 +1,12 @@ +deeplink) { + throw new NotFoundHttpException(); + } + + // Has token → redirect with deeplink + if (str_contains($payload->queryString, 'token')) { + $event = new LoginRedirectEvent('opendxp_admin_login', [ + 'deeplink' => $payload->deeplink, + 'perspective' => $payload->perspective, + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::LOGIN_REDIRECT); + + $url = $this->urlGenerator->generate($event->getRouteName(), $event->getRouteParams()); + + return new DeeplinkResult(redirectUrl: $url . '&' . $payload->queryString); + } + + // Has query string → render deeplink page + if ($payload->queryString) { + $event = new LoginRedirectEvent('opendxp_admin_login', [ + 'deeplink' => 'true', + 'perspective' => $payload->perspective, + ]); + $this->eventDispatcher->dispatch($event, AdminEvents::LOGIN_REDIRECT); + + $redirect = $this->urlGenerator->generate($event->getRouteName(), $event->getRouteParams()); + + return new DeeplinkResult( + template: '@OpenDxpAdmin/admin/login/deeplink.html.twig', + params: ['tab' => $payload->deeplink, 'redirect' => $redirect], + ); + } + + // No query string → not found + throw new NotFoundHttpException(); + } +} diff --git a/src/Handler/Login/Deeplink/DeeplinkPayload.php b/src/Handler/Login/Deeplink/DeeplinkPayload.php new file mode 100644 index 00000000..0b0acc2c --- /dev/null +++ b/src/Handler/Login/Deeplink/DeeplinkPayload.php @@ -0,0 +1,35 @@ +server->get('QUERY_STRING') ?? ''; + $perspective = (string) $request->query->get('perspective', ''); + $perspective = strip_tags($perspective); + + $deeplink = null; + if (preg_match('/(document|asset|object)_(\d+)_([a-z]+)/', $queryString, $matches)) { + $deeplink = $matches[0]; + } + + return new static( + queryString: $queryString, + perspective: $perspective, + deeplink: $deeplink, + ); + } +} diff --git a/src/Handler/Login/Deeplink/DeeplinkResult.php b/src/Handler/Login/Deeplink/DeeplinkResult.php new file mode 100644 index 00000000..cda14174 --- /dev/null +++ b/src/Handler/Login/Deeplink/DeeplinkResult.php @@ -0,0 +1,14 @@ +query->getString('error') ?: null, + ); + } +} diff --git a/src/Handler/Login/GenerateTwoFactorSetup/GenerateTwoFactorSetupResult.php b/src/Handler/Login/GenerateTwoFactorSetup/GenerateTwoFactorSetupResult.php new file mode 100644 index 00000000..4fe12421 --- /dev/null +++ b/src/Handler/Login/GenerateTwoFactorSetup/GenerateTwoFactorSetupResult.php @@ -0,0 +1,13 @@ +isPost) { + return new LostPasswordResult(error: null); + } + + if (!$payload->username) { return new LostPasswordResult(error: 'user_unknown'); } - $user = User::getByName($username); + $user = User::getByName($payload->username); if (!$user instanceof User) { return new LostPasswordResult(error: 'user_unknown'); } - $limiter = $this->resetPasswordLimiter->create($clientIp); + $limiter = $this->resetPasswordLimiter->create($payload->clientIp); if (false === $limiter->consume(1)->isAccepted()) { return new LostPasswordResult(error: 'user_reset_password_too_many_attempts'); } @@ -67,8 +60,9 @@ public function __invoke(string $username, string $clientIp, string $domain): Lo $token = Authentication::generateTokenByUser($user); try { + $domain = $this->hostResolver->resolve($payload->resolverContext ?? []) ?? ''; if (!$domain) { - throw new Exception('No main domain set in system settings, unable to generate reset password link'); + throw new \Exception('No main domain set in system settings, unable to generate reset password link'); } $context = $this->router->getContext(); @@ -93,7 +87,7 @@ public function __invoke(string $username, string $clientIp, string $domain): Lo error: null, eventResponse: $event->hasResponse() ? $event->getResponse() : null, ); - } catch (Exception $e) { + } catch (\Exception $e) { Logger::error('Error sending password recovery email: ' . $e->getMessage()); return new LostPasswordResult(error: 'lost_password_email_error'); diff --git a/src/Handler/Login/LostPassword/LostPasswordPayload.php b/src/Handler/Login/LostPassword/LostPasswordPayload.php new file mode 100644 index 00000000..453f13e7 --- /dev/null +++ b/src/Handler/Login/LostPassword/LostPasswordPayload.php @@ -0,0 +1,28 @@ +request->get('username'), + clientIp: (string) $request->getClientIp(), + isPost: $request->isMethod('POST') && $request->request->has('username'), + resolverContext: ['source' => $request], + ); + } +} diff --git a/src/Handler/Login/LostPassword/LostPasswordResult.php b/src/Handler/Login/LostPassword/LostPasswordResult.php new file mode 100644 index 00000000..8845e6d5 --- /dev/null +++ b/src/Handler/Login/LostPassword/LostPasswordResult.php @@ -0,0 +1,15 @@ +secret) { throw new Exception('2fa secret not found'); } @@ -38,9 +26,9 @@ public function __invoke(string $secret, string $authCode): void $user->setTwoFactorAuthentication('enabled', true); $user->setTwoFactorAuthentication('type', 'google'); - $user->setTwoFactorAuthentication('secret', $secret); + $user->setTwoFactorAuthentication('secret', $payload->secret); - if (!$this->twoFactor->checkCode($proxyUser, $authCode)) { + if (!$this->twoFactor->checkCode($proxyUser, $payload->authCode)) { throw new Exception('2fa_wrong'); } diff --git a/src/Handler/Login/SaveTwoFactorSetup/SaveTwoFactorSetupPayload.php b/src/Handler/Login/SaveTwoFactorSetup/SaveTwoFactorSetupPayload.php new file mode 100644 index 00000000..e5fa82cc --- /dev/null +++ b/src/Handler/Login/SaveTwoFactorSetup/SaveTwoFactorSetupPayload.php @@ -0,0 +1,24 @@ +getSession()->get('2fa_secret'), + authCode: (string) $request->request->get('_auth_code'), + ); + } +} diff --git a/src/Handler/Misc/AdminCss/AdminCssHandler.php b/src/Handler/Misc/AdminCss/AdminCssHandler.php new file mode 100644 index 00000000..04ae08cf --- /dev/null +++ b/src/Handler/Misc/AdminCss/AdminCssHandler.php @@ -0,0 +1,42 @@ +provider->getControllerReferences(); + + $data = array_map(static fn($controller) => [ + 'name' => $controller, + ], $controllerReferences); + + return new GetAvailableControllerReferencesResult( + data: $data, + total: count($data), + ); + } +} diff --git a/src/Handler/Element/FindUsagesResult.php b/src/Handler/Misc/GetAvailableControllerReferences/GetAvailableControllerReferencesResult.php similarity index 80% rename from src/Handler/Element/FindUsagesResult.php rename to src/Handler/Misc/GetAvailableControllerReferences/GetAvailableControllerReferencesResult.php index f27421df..7d1c536c 100644 --- a/src/Handler/Element/FindUsagesResult.php +++ b/src/Handler/Misc/GetAvailableControllerReferences/GetAvailableControllerReferencesResult.php @@ -15,13 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetAvailableControllerReferences; -final readonly class FindUsagesResult +final readonly class GetAvailableControllerReferencesResult { public function __construct( public array $data, public int $total, - public bool $hasHidden, ) {} } diff --git a/src/Handler/Element/DeleteVersionHandler.php b/src/Handler/Misc/GetAvailableLanguages/GetAvailableLanguagesHandler.php similarity index 59% rename from src/Handler/Element/DeleteVersionHandler.php rename to src/Handler/Misc/GetAvailableLanguages/GetAvailableLanguagesHandler.php index 6309466c..f66708ab 100644 --- a/src/Handler/Element/DeleteVersionHandler.php +++ b/src/Handler/Misc/GetAvailableLanguages/GetAvailableLanguagesHandler.php @@ -15,14 +15,15 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetAvailableLanguages; -use OpenDxp\Model\Version; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; +use OpenDxp\Tool; -final class DeleteVersionHandler +final class GetAvailableLanguagesHandler { - public function __invoke(int $id): void + public function __invoke(EmptyPayload $payload): GetAvailableLanguagesResult { - Version::getById($id)?->delete(); + return new GetAvailableLanguagesResult(locales: Tool::getSupportedLocales()); } } diff --git a/src/Handler/Asset/Copy/ChildIdsResult.php b/src/Handler/Misc/GetAvailableLanguages/GetAvailableLanguagesResult.php similarity index 78% rename from src/Handler/Asset/Copy/ChildIdsResult.php rename to src/Handler/Misc/GetAvailableLanguages/GetAvailableLanguagesResult.php index 31a63675..e91471d5 100644 --- a/src/Handler/Asset/Copy/ChildIdsResult.php +++ b/src/Handler/Misc/GetAvailableLanguages/GetAvailableLanguagesResult.php @@ -15,12 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Asset\Copy; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetAvailableLanguages; -final readonly class ChildIdsResult +final readonly class GetAvailableLanguagesResult { - /** @param int[] $ids */ public function __construct( - public array $ids, + public array $locales, ) {} } diff --git a/src/Handler/Misc/GetAvailableTemplates/GetAvailableTemplatesHandler.php b/src/Handler/Misc/GetAvailableTemplates/GetAvailableTemplatesHandler.php new file mode 100644 index 00000000..795742f5 --- /dev/null +++ b/src/Handler/Misc/GetAvailableTemplates/GetAvailableTemplatesHandler.php @@ -0,0 +1,41 @@ +provider->getTemplates(); + + sort($templates, SORT_NATURAL | SORT_FLAG_CASE); + + $data = array_map(static fn ($template) => [ + 'path' => $template, + ], $templates); + + return new GetAvailableTemplatesResult(data: $data); + } +} diff --git a/src/Handler/Misc/GetAvailableTemplates/GetAvailableTemplatesResult.php b/src/Handler/Misc/GetAvailableTemplates/GetAvailableTemplatesResult.php new file mode 100644 index 00000000..848861f9 --- /dev/null +++ b/src/Handler/Misc/GetAvailableTemplates/GetAvailableTemplatesResult.php @@ -0,0 +1,25 @@ +localeService->getDisplayRegions(); + asort($countries); + + $data = []; + foreach ($countries as $short => $translation) { + if (strlen($short) === 2) { + $data[] = [ + 'name' => $translation, + 'code' => $short, + ]; + } + } + + return new GetCountryListResult(data: $data); + } +} diff --git a/src/Handler/Element/AnalyzePermissionsResult.php b/src/Handler/Misc/GetCountryList/GetCountryListResult.php similarity index 84% rename from src/Handler/Element/AnalyzePermissionsResult.php rename to src/Handler/Misc/GetCountryList/GetCountryListResult.php index f7616365..04f659a8 100644 --- a/src/Handler/Element/AnalyzePermissionsResult.php +++ b/src/Handler/Misc/GetCountryList/GetCountryListResult.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetCountryList; -final readonly class AnalyzePermissionsResult +final readonly class GetCountryListResult { public function __construct( public array $data, diff --git a/src/Handler/Misc/GetIconListHandler.php b/src/Handler/Misc/GetIconList/GetIconListHandler.php similarity index 89% rename from src/Handler/Misc/GetIconListHandler.php rename to src/Handler/Misc/GetIconList/GetIconListHandler.php index 36db9d75..52688492 100644 --- a/src/Handler/Misc/GetIconListHandler.php +++ b/src/Handler/Misc/GetIconList/GetIconListHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Misc; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetIconList; use OpenDxp\Bundle\AdminBundle\Tool as AdminTool; use OpenDxp\Helper\FileSystemHelper; @@ -23,12 +23,12 @@ final class GetIconListHandler { - public function __invoke(?string $type): GetIconListResult + public function __invoke(GetIconListPayload $payload): GetIconListResult { $publicDir = OPENDXP_WEB_ROOT . '/bundles/opendxpadmin'; $iconDir = $publicDir . '/img'; - $icons = match ($type) { + $icons = match ($payload->type) { 'color' => FileSystemHelper::scanDirectory($iconDir . '/flat-color-icons/'), 'white' => FileSystemHelper::scanDirectory($iconDir . '/flat-white-icons/'), 'twemoji' => FileSystemHelper::scanDirectory($iconDir . '/twemoji/'), @@ -36,7 +36,7 @@ public function __invoke(?string $type): GetIconListResult default => [] }; - $source = match ($type) { + $source = match ($payload->type) { 'color', 'white' => 'based on the ' . 'Material Design Icons', @@ -47,7 +47,7 @@ public function __invoke(?string $type): GetIconListResult }; $extraInfo = null; - if ($type === 'twemoji') { + if ($payload->type === 'twemoji') { $extraInfo = 'ℹ Click on icon with green border to display all its related variants. Click on the letter to display flags with the clicked initial'; } @@ -56,7 +56,7 @@ public function __invoke(?string $type): GetIconListResult return new GetIconListResult( icons: $icons, iconsCss: $iconsCss !== false ? $iconsCss : '', - type: $type, + type: $payload->type, extraInfo: $extraInfo, source: $source, ); diff --git a/src/Handler/Misc/GetIconList/GetIconListPayload.php b/src/Handler/Misc/GetIconList/GetIconListPayload.php new file mode 100644 index 00000000..faf916dc --- /dev/null +++ b/src/Handler/Misc/GetIconList/GetIconListPayload.php @@ -0,0 +1,35 @@ +query->has('type') ? $request->query->getString('type') : null, + ); + } +} diff --git a/src/Handler/Misc/GetIconListResult.php b/src/Handler/Misc/GetIconList/GetIconListResult.php similarity index 92% rename from src/Handler/Misc/GetIconListResult.php rename to src/Handler/Misc/GetIconList/GetIconListResult.php index 69432876..3c6ffc96 100644 --- a/src/Handler/Misc/GetIconListResult.php +++ b/src/Handler/Misc/GetIconList/GetIconListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Misc; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetIconList; final readonly class GetIconListResult { diff --git a/src/Handler/Misc/GetJsonTranslationsHandler.php b/src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsHandler.php similarity index 57% rename from src/Handler/Misc/GetJsonTranslationsHandler.php rename to src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsHandler.php index 4d967271..c41fe208 100644 --- a/src/Handler/Misc/GetJsonTranslationsHandler.php +++ b/src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsHandler.php @@ -15,33 +15,37 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Misc; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetJsonTranslations; use Locale; use OpenDxp\Translation\Translator; final class GetJsonTranslationsHandler { - public function __invoke(Translator $translator, ?string $language): GetJsonTranslationsResult + public function __construct( + private readonly Translator $translator, + ) {} + + public function __invoke(GetJsonTranslationsPayload $payload): GetJsonTranslationsResult { - $translator->lazyInitialize('admin', $language); + $this->translator->lazyInitialize('admin', $payload->language); $translations = []; $fallbackLanguages = []; - if (null !== Locale::getRegion($language)) { - $fallbackLanguages[] = Locale::getPrimaryLanguage($language); + if (null !== Locale::getRegion($payload->language)) { + $fallbackLanguages[] = Locale::getPrimaryLanguage($payload->language); } - if ($language !== 'en') { + if ($payload->language !== 'en') { $fallbackLanguages[] = 'en'; } foreach (['admin', 'admin_ext'] as $domain) { - $translations = array_replace($translations, $translator->getCatalogue($language)->all($domain)); + $translations = array_replace($translations, $this->translator->getCatalogue($payload->language)->all($domain)); foreach ($fallbackLanguages as $fallbackLanguage) { - $translator->lazyInitialize($domain, $fallbackLanguage); - foreach ($translator->getCatalogue($fallbackLanguage)->all($domain) as $key => $value) { + $this->translator->lazyInitialize($domain, $fallbackLanguage); + foreach ($this->translator->getCatalogue($fallbackLanguage)->all($domain) as $key => $value) { if (empty($translations[$key])) { $translations[$key] = $value; } diff --git a/src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsPayload.php b/src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsPayload.php new file mode 100644 index 00000000..e68b36ed --- /dev/null +++ b/src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsPayload.php @@ -0,0 +1,35 @@ +query->has('language') ? $request->query->getString('language') : null, + ); + } +} diff --git a/src/Handler/Misc/GetJsonTranslationsResult.php b/src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsResult.php similarity index 89% rename from src/Handler/Misc/GetJsonTranslationsResult.php rename to src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsResult.php index 3b1ac99b..749dc2e7 100644 --- a/src/Handler/Misc/GetJsonTranslationsResult.php +++ b/src/Handler/Misc/GetJsonTranslations/GetJsonTranslationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Misc; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetJsonTranslations; final readonly class GetJsonTranslationsResult { diff --git a/src/Handler/Element/LockElementHandler.php b/src/Handler/Misc/GetLanguageFlag/GetLanguageFlagHandler.php similarity index 59% rename from src/Handler/Element/LockElementHandler.php rename to src/Handler/Misc/GetLanguageFlag/GetLanguageFlagHandler.php index 439f86a1..65e91c8f 100644 --- a/src/Handler/Element/LockElementHandler.php +++ b/src/Handler/Misc/GetLanguageFlag/GetLanguageFlagHandler.php @@ -15,14 +15,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetLanguageFlag; -use OpenDxp\Model\Element\Editlock; +use OpenDxp\Bundle\AdminBundle\Tool as AdminTool; -final class LockElementHandler +final class GetLanguageFlagHandler { - public function __invoke(int $id, string $type, string $sessionId): void + public function __invoke(GetLanguageFlagPayload $payload): GetLanguageFlagResult { - Editlock::lock($id, $type, $sessionId); + return new GetLanguageFlagResult( + iconPath: AdminTool::getLanguageFlagFile($payload->language), + ); } } diff --git a/src/Handler/Misc/GetLanguageFlag/GetLanguageFlagPayload.php b/src/Handler/Misc/GetLanguageFlag/GetLanguageFlagPayload.php new file mode 100644 index 00000000..50d24d14 --- /dev/null +++ b/src/Handler/Misc/GetLanguageFlag/GetLanguageFlagPayload.php @@ -0,0 +1,35 @@ +query->has('language') ? $request->query->getString('language') : null, + ); + } +} diff --git a/src/Handler/Element/GetRequiresDependenciesResult.php b/src/Handler/Misc/GetLanguageFlag/GetLanguageFlagResult.php similarity index 79% rename from src/Handler/Element/GetRequiresDependenciesResult.php rename to src/Handler/Misc/GetLanguageFlag/GetLanguageFlagResult.php index c124391b..7d53a343 100644 --- a/src/Handler/Element/GetRequiresDependenciesResult.php +++ b/src/Handler/Misc/GetLanguageFlag/GetLanguageFlagResult.php @@ -15,11 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetLanguageFlag; -final readonly class GetRequiresDependenciesResult +final readonly class GetLanguageFlagResult { public function __construct( - public array|false $data, + public string $iconPath, ) {} } diff --git a/src/Handler/Misc/GetLanguageList/GetLanguageListHandler.php b/src/Handler/Misc/GetLanguageList/GetLanguageListHandler.php new file mode 100644 index 00000000..86b9b061 --- /dev/null +++ b/src/Handler/Misc/GetLanguageList/GetLanguageListHandler.php @@ -0,0 +1,39 @@ + $translation) { + $data[] = [ + 'name' => $translation, + 'code' => $short, + ]; + } + + return new GetLanguageListResult(data: $data); + } +} diff --git a/src/Handler/Misc/GetLanguageList/GetLanguageListResult.php b/src/Handler/Misc/GetLanguageList/GetLanguageListResult.php new file mode 100644 index 00000000..46c5b065 --- /dev/null +++ b/src/Handler/Misc/GetLanguageList/GetLanguageListResult.php @@ -0,0 +1,25 @@ +unlockPropagate(); - - return true; + return new GetValidFilenameResult( + filename: Service::getValidKey($payload->value, $payload->type), + ); } } diff --git a/src/Handler/Misc/GetValidFilename/GetValidFilenamePayload.php b/src/Handler/Misc/GetValidFilename/GetValidFilenamePayload.php new file mode 100644 index 00000000..54a634b2 --- /dev/null +++ b/src/Handler/Misc/GetValidFilename/GetValidFilenamePayload.php @@ -0,0 +1,37 @@ +query->has('value') ? $request->query->getString('value') : null, + type: $request->query->has('type') ? $request->query->getString('type') : null, + ); + } +} diff --git a/src/Handler/Element/GetPredefinedPropertiesResult.php b/src/Handler/Misc/GetValidFilename/GetValidFilenameResult.php similarity index 79% rename from src/Handler/Element/GetPredefinedPropertiesResult.php rename to src/Handler/Misc/GetValidFilename/GetValidFilenameResult.php index 2f18b4df..7e5a28bc 100644 --- a/src/Handler/Element/GetPredefinedPropertiesResult.php +++ b/src/Handler/Misc/GetValidFilename/GetValidFilenameResult.php @@ -15,11 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Element; +namespace OpenDxp\Bundle\AdminBundle\Handler\Misc\GetValidFilename; -final readonly class GetPredefinedPropertiesResult +final readonly class GetValidFilenameResult { public function __construct( - public array $properties, + public mixed $filename, ) {} } diff --git a/src/Handler/Misc/Maintenance/MaintenanceHandler.php b/src/Handler/Misc/Maintenance/MaintenanceHandler.php new file mode 100644 index 00000000..a5fefa81 --- /dev/null +++ b/src/Handler/Misc/Maintenance/MaintenanceHandler.php @@ -0,0 +1,38 @@ +activate) { + $this->maintenanceModeHelper->activate($payload->sessionId); + } + + if ($payload->deactivate) { + $this->maintenanceModeHelper->deactivate(); + } + } +} diff --git a/src/Handler/Misc/Maintenance/MaintenancePayload.php b/src/Handler/Misc/Maintenance/MaintenancePayload.php new file mode 100644 index 00000000..09107bd3 --- /dev/null +++ b/src/Handler/Misc/Maintenance/MaintenancePayload.php @@ -0,0 +1,39 @@ +query->has('activate') ? $request->query->getString('activate') : null, + deactivate: $request->query->has('deactivate') ? $request->query->getString('deactivate') : null, + sessionId: $request->getSession()->getId(), + ); + } +} diff --git a/src/Handler/Misc/ScriptProxy/ScriptProxyHandler.php b/src/Handler/Misc/ScriptProxy/ScriptProxyHandler.php new file mode 100644 index 00000000..d2c89e42 --- /dev/null +++ b/src/Handler/Misc/ScriptProxy/ScriptProxyHandler.php @@ -0,0 +1,47 @@ +storageFile) { + throw new InvalidArgumentException('The parameter storageFile is required'); + } + + $fileExtension = pathinfo($payload->storageFile, PATHINFO_EXTENSION); + $storage = Storage::get('admin'); + $scriptsContent = $storage->read($payload->storageFile); + + if (empty($scriptsContent)) { + throw new NotFoundHttpException('Scripts not found'); + } + + $contentType = $fileExtension === 'css' ? 'text/css' : 'text/javascript'; + + return new ScriptProxyResult( + content: $scriptsContent, + contentType: $contentType, + ); + } +} diff --git a/src/Handler/Misc/ScriptProxy/ScriptProxyPayload.php b/src/Handler/Misc/ScriptProxy/ScriptProxyPayload.php new file mode 100644 index 00000000..ea8ce901 --- /dev/null +++ b/src/Handler/Misc/ScriptProxy/ScriptProxyPayload.php @@ -0,0 +1,35 @@ +query->has('storageFile') ? $request->query->getString('storageFile') : null, + ); + } +} diff --git a/src/Handler/Misc/ScriptProxy/ScriptProxyResult.php b/src/Handler/Misc/ScriptProxy/ScriptProxyResult.php new file mode 100644 index 00000000..4d92c72e --- /dev/null +++ b/src/Handler/Misc/ScriptProxy/ScriptProxyResult.php @@ -0,0 +1,26 @@ +deleteAll((int) $this->userContext->getAdminUser()?->getId()); + $this->notificationService->deleteAll((int) $this->userContext->getAdminUser()?->getId()); } } diff --git a/src/Handler/Notification/DeleteNotificationHandler.php b/src/Handler/Notification/DeleteNotification/DeleteNotificationHandler.php similarity index 58% rename from src/Handler/Notification/DeleteNotificationHandler.php rename to src/Handler/Notification/DeleteNotification/DeleteNotificationHandler.php index 8e568fff..d29a6df0 100644 --- a/src/Handler/Notification/DeleteNotificationHandler.php +++ b/src/Handler/Notification/DeleteNotification/DeleteNotificationHandler.php @@ -15,17 +15,21 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\DeleteNotification; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Notification\Service\NotificationService; final class DeleteNotificationHandler { - public function __construct(private readonly AdminUserContextInterface $userContext) {} + public function __construct( + private readonly AdminUserContextInterface $userContext, + private readonly NotificationService $notificationService, + ) {} - public function __invoke(NotificationService $service, int $id): void + public function __invoke(IdQueryPayload $payload): void { - $service->delete($id, (int) $this->userContext->getAdminUser()?->getId()); + $this->notificationService->delete($payload->id, (int) $this->userContext->getAdminUser()?->getId()); } } diff --git a/src/Handler/Notification/FindAllNotificationsHandler.php b/src/Handler/Notification/FindAllNotifications/FindAllNotificationsHandler.php similarity index 60% rename from src/Handler/Notification/FindAllNotificationsHandler.php rename to src/Handler/Notification/FindAllNotifications/FindAllNotificationsHandler.php index f7b2c0d2..56fa56c6 100644 --- a/src/Handler/Notification/FindAllNotificationsHandler.php +++ b/src/Handler/Notification/FindAllNotifications/FindAllNotificationsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\FindAllNotifications; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Notification\Service\NotificationService; @@ -24,31 +24,29 @@ final class FindAllNotificationsHandler { - public function __construct(private readonly AdminUserContextInterface $userContext) {} - - public function __invoke( - NotificationService $service, - Request $request, - int $offset, - int $limit, - ): FindAllNotificationsResult { + public function __construct( + private readonly AdminUserContextInterface $userContext, + private readonly NotificationService $notificationService, + ) {} + + public function __invoke(FindAllNotificationsPayload $payload): FindAllNotificationsResult + { $filter = ['recipient' => (int) $this->userContext->getAdminUser()?->getId()]; - $parser = new NotificationServiceFilterParser($request); + $syntheticRequest = new Request(request: [NotificationServiceFilterParser::KEY_FILTER => $payload->filter]); + $parser = new NotificationServiceFilterParser($syntheticRequest); foreach ($parser->parse() as $key => $val) { $filter[$key] = $val; } - $options = [ - 'offset' => $offset, - 'limit' => $limit, - ]; - - $result = $service->findAll($filter, $options); + $result = $this->notificationService->findAll($filter, [ + 'offset' => $payload->offset, + 'limit' => $payload->limit, + ]); $data = []; foreach ($result['data'] as $notification) { - $data[] = $service->format($notification); + $data[] = $this->notificationService->format($notification); } return new FindAllNotificationsResult(data: $data, total: (int) $result['total']); diff --git a/src/Handler/Notification/FindAllNotifications/FindAllNotificationsPayload.php b/src/Handler/Notification/FindAllNotifications/FindAllNotificationsPayload.php new file mode 100644 index 00000000..c0e72c4d --- /dev/null +++ b/src/Handler/Notification/FindAllNotifications/FindAllNotificationsPayload.php @@ -0,0 +1,38 @@ +request->getInt('start'), + limit: $request->request->getInt('limit', 40), + filter: $request->request->getString('filter', '[]'), + ); + } +} diff --git a/src/Handler/Notification/FindAllNotificationsResult.php b/src/Handler/Notification/FindAllNotifications/FindAllNotificationsResult.php similarity index 89% rename from src/Handler/Notification/FindAllNotificationsResult.php rename to src/Handler/Notification/FindAllNotifications/FindAllNotificationsResult.php index 78e95a81..9bddc82f 100644 --- a/src/Handler/Notification/FindAllNotificationsResult.php +++ b/src/Handler/Notification/FindAllNotifications/FindAllNotificationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\FindAllNotifications; final readonly class FindAllNotificationsResult { diff --git a/src/Handler/Notification/FindLastUnreadNotificationsHandler.php b/src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsHandler.php similarity index 62% rename from src/Handler/Notification/FindLastUnreadNotificationsHandler.php rename to src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsHandler.php index 42ecb3d1..76dcd4a6 100644 --- a/src/Handler/Notification/FindLastUnreadNotificationsHandler.php +++ b/src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsHandler.php @@ -15,28 +15,27 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\FindLastUnreadNotifications; -use OpenDxp\Model\Notification\Service\NotificationService; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; +use OpenDxp\Model\Notification\Service\NotificationService; final class FindLastUnreadNotificationsHandler { - public function __construct(private readonly AdminUserContextInterface $userContext) - { - } + public function __construct( + private readonly AdminUserContextInterface $userContext, + private readonly NotificationService $notificationService, + ) {} - public function __invoke( - NotificationService $service, - int $lastUpdate, - ): FindLastUnreadNotificationsResult { + public function __invoke(FindLastUnreadNotificationsPayload $payload): FindLastUnreadNotificationsResult + { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; - $result = $service->findLastUnread($userId, $lastUpdate); - $unread = $service->countAllUnread($userId); + $result = $this->notificationService->findLastUnread($userId, $payload->lastUpdate ?? time()); + $unread = $this->notificationService->countAllUnread($userId); $data = []; foreach ($result['data'] as $notification) { - $data[] = $service->format($notification); + $data[] = $this->notificationService->format($notification); } return new FindLastUnreadNotificationsResult( diff --git a/src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsPayload.php b/src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsPayload.php new file mode 100644 index 00000000..2ec4cb3d --- /dev/null +++ b/src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsPayload.php @@ -0,0 +1,36 @@ +query->get('lastUpdate'); + + return new static( + lastUpdate: $raw !== null ? (int) $raw : null, + ); + } +} diff --git a/src/Handler/Notification/FindLastUnreadNotificationsResult.php b/src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsResult.php similarity index 88% rename from src/Handler/Notification/FindLastUnreadNotificationsResult.php rename to src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsResult.php index 1fd3efb5..1929b906 100644 --- a/src/Handler/Notification/FindLastUnreadNotificationsResult.php +++ b/src/Handler/Notification/FindLastUnreadNotifications/FindLastUnreadNotificationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\FindLastUnreadNotifications; final readonly class FindLastUnreadNotificationsResult { diff --git a/src/Handler/Notification/FindNotificationHandler.php b/src/Handler/Notification/FindNotification/FindNotificationHandler.php similarity index 63% rename from src/Handler/Notification/FindNotificationHandler.php rename to src/Handler/Notification/FindNotification/FindNotificationHandler.php index 8b83d8c2..3fed400b 100644 --- a/src/Handler/Notification/FindNotificationHandler.php +++ b/src/Handler/Notification/FindNotification/FindNotificationHandler.php @@ -15,29 +15,31 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\FindNotification; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Notification\Service\NotificationService; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use UnexpectedValueException; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class FindNotificationHandler { - public function __construct(private readonly AdminUserContextInterface $userContext) - { - } + public function __construct( + private readonly AdminUserContextInterface $userContext, + private readonly NotificationService $notificationService, + ) {} - public function __invoke(NotificationService $service, int $id): FindNotificationResult + public function __invoke(IdQueryPayload $payload): FindNotificationResult { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; try { - $notification = $service->findAndMarkAsRead($id, $userId); + $notification = $this->notificationService->findAndMarkAsRead($payload->id, $userId); } catch (UnexpectedValueException $e) { - throw new NotFoundHttpException(sprintf('Notification with id %d not found', $id), $e); + throw new NotFoundHttpException(sprintf('Notification with id %d not found', $payload->id), $e); } - $data = $service->format($notification); + $data = $this->notificationService->format($notification); return new FindNotificationResult(data: $data); } diff --git a/src/Handler/Notification/FindNotificationResult.php b/src/Handler/Notification/FindNotification/FindNotificationResult.php similarity index 89% rename from src/Handler/Notification/FindNotificationResult.php rename to src/Handler/Notification/FindNotification/FindNotificationResult.php index 30d30178..237578c1 100644 --- a/src/Handler/Notification/FindNotificationResult.php +++ b/src/Handler/Notification/FindNotification/FindNotificationResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\FindNotification; final readonly class FindNotificationResult { diff --git a/src/Handler/Notification/GetRecipientsHandler.php b/src/Handler/Notification/GetRecipients/GetRecipientsHandler.php similarity index 66% rename from src/Handler/Notification/GetRecipientsHandler.php rename to src/Handler/Notification/GetRecipients/GetRecipientsHandler.php index 83e02984..26316fe8 100644 --- a/src/Handler/Notification/GetRecipientsHandler.php +++ b/src/Handler/Notification/GetRecipients/GetRecipientsHandler.php @@ -15,26 +15,28 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\GetRecipients; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Notification\Service\UserService; -use OpenDxp\Model\User; use Symfony\Contracts\Translation\TranslatorInterface; -use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetRecipientsHandler { - public function __construct(private readonly AdminUserContextInterface $userContext) - { - } + public function __construct( + private readonly AdminUserContextInterface $userContext, + private readonly UserService $userService, + private readonly TranslatorInterface $translator, + ) {} - public function __invoke(UserService $service, TranslatorInterface $translator): GetRecipientsResult + public function __invoke(EmptyPayload $payload): GetRecipientsResult { $adminUser = $this->userContext->getAdminUser(); $data = []; - $group = $translator->trans('group', [], 'admin'); + $group = $this->translator->trans('group', [], 'admin'); - foreach ($service->findAll($adminUser) as $recipient) { + foreach ($this->userService->findAll($adminUser) as $recipient) { $prefix = $recipient->getType() === 'role' ? $group . ' - ' : ''; $data[] = [ diff --git a/src/Handler/Notification/GetRecipientsResult.php b/src/Handler/Notification/GetRecipients/GetRecipientsResult.php similarity index 89% rename from src/Handler/Notification/GetRecipientsResult.php rename to src/Handler/Notification/GetRecipients/GetRecipientsResult.php index 87b5c09e..f50a1519 100644 --- a/src/Handler/Notification/GetRecipientsResult.php +++ b/src/Handler/Notification/GetRecipients/GetRecipientsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\GetRecipients; final readonly class GetRecipientsResult { diff --git a/src/Handler/Notification/MarkAsReadNotificationHandler.php b/src/Handler/Notification/MarkAsReadNotification/MarkAsReadNotificationHandler.php similarity index 58% rename from src/Handler/Notification/MarkAsReadNotificationHandler.php rename to src/Handler/Notification/MarkAsReadNotification/MarkAsReadNotificationHandler.php index 789180f0..7a5a1fd8 100644 --- a/src/Handler/Notification/MarkAsReadNotificationHandler.php +++ b/src/Handler/Notification/MarkAsReadNotification/MarkAsReadNotificationHandler.php @@ -15,17 +15,21 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Notification; +namespace OpenDxp\Bundle\AdminBundle\Handler\Notification\MarkAsReadNotification; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Notification\Service\NotificationService; final class MarkAsReadNotificationHandler { - public function __construct(private readonly AdminUserContextInterface $userContext) {} + public function __construct( + private readonly AdminUserContextInterface $userContext, + private readonly NotificationService $notificationService, + ) {} - public function __invoke(NotificationService $service, int $id): void + public function __invoke(IdQueryPayload $payload): void { - $service->findAndMarkAsRead($id, (int) $this->userContext->getAdminUser()?->getId()); + $this->notificationService->findAndMarkAsRead($payload->id, (int) $this->userContext->getAdminUser()?->getId()); } } diff --git a/src/Handler/Notification/SendNotification/SendNotificationHandler.php b/src/Handler/Notification/SendNotification/SendNotificationHandler.php new file mode 100644 index 00000000..65e88a6f --- /dev/null +++ b/src/Handler/Notification/SendNotification/SendNotificationHandler.php @@ -0,0 +1,47 @@ +userContext->getAdminUser()?->getId(); + + $element = null; + if ($payload->elementId && $payload->elementType) { + $element = Service::getElementById($payload->elementType, $payload->elementId); + } + + if (User::getById($payload->recipientId) instanceof User) { + $this->notificationService->sendToUser($payload->recipientId, $fromUserId, $payload->title, $payload->message, $element); + } else { + $this->notificationService->sendToGroup($payload->recipientId, $fromUserId, $payload->title, $payload->message, $element); + } + } +} diff --git a/src/Handler/Notification/SendNotification/SendNotificationPayload.php b/src/Handler/Notification/SendNotification/SendNotificationPayload.php new file mode 100644 index 00000000..c2f75d6a --- /dev/null +++ b/src/Handler/Notification/SendNotification/SendNotificationPayload.php @@ -0,0 +1,44 @@ +request->getString('recipientId'), + title: $request->request->getString('title'), + message: $request->request->getString('message'), + elementId: (int) $request->request->getString('elementId'), + elementType: $request->request->has('elementType') + ? $request->request->getString('elementType') + : null, + ); + } +} diff --git a/src/Handler/Notification/SendNotificationHandler.php b/src/Handler/Notification/SendNotificationHandler.php deleted file mode 100644 index 74aab687..00000000 --- a/src/Handler/Notification/SendNotificationHandler.php +++ /dev/null @@ -1,44 +0,0 @@ -userContext->getAdminUser()?->getId(); - - if (User::getById($recipientId) instanceof User) { - $service->sendToUser($recipientId, $fromUserId, $title, $message, $element); - } else { - $service->sendToGroup($recipientId, $fromUserId, $title, $message, $element); - } - } -} diff --git a/src/Handler/Portal/AddWidgetHandler.php b/src/Handler/Portal/AddWidget/AddWidgetHandler.php similarity index 77% rename from src/Handler/Portal/AddWidgetHandler.php rename to src/Handler/Portal/AddWidget/AddWidgetHandler.php index 2a9d501b..df829f78 100644 --- a/src/Handler/Portal/AddWidgetHandler.php +++ b/src/Handler/Portal/AddWidget/AddWidgetHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\AddWidget; use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory; @@ -24,11 +24,11 @@ public function __construct(private readonly DashboardFactory $dashboardFactory) { } - public function __invoke(string $dashboardId, string $type): AddWidgetResult + public function __invoke(AddWidgetPayload $payload): AddWidgetResult { $dashboard = $this->dashboardFactory->create(); - $config = $dashboard->getDashboard($dashboardId); + $config = $dashboard->getDashboard($payload->dashboardId); $nextId = 0; foreach ($config['positions'] as $col) { @@ -40,11 +40,11 @@ public function __invoke(string $dashboardId, string $type): AddWidgetResult $nextId += 1; $config['positions'][0][] = [ 'id' => $nextId, - 'type' => $type, + 'type' => $payload->type, 'config' => null, ]; - $dashboard->saveDashboard($dashboardId, $config); + $dashboard->saveDashboard($payload->dashboardId, $config); return new AddWidgetResult(id: $nextId); } diff --git a/src/Handler/Portal/AddWidget/AddWidgetPayload.php b/src/Handler/Portal/AddWidget/AddWidgetPayload.php new file mode 100644 index 00000000..c9947ff7 --- /dev/null +++ b/src/Handler/Portal/AddWidget/AddWidgetPayload.php @@ -0,0 +1,37 @@ +request->get('key'), + type: (string) $request->request->get('type'), + ); + } +} diff --git a/src/Handler/Portal/AddWidgetResult.php b/src/Handler/Portal/AddWidget/AddWidgetResult.php similarity index 83% rename from src/Handler/Portal/AddWidgetResult.php rename to src/Handler/Portal/AddWidget/AddWidgetResult.php index 470b09b2..6361bff7 100644 --- a/src/Handler/Portal/AddWidgetResult.php +++ b/src/Handler/Portal/AddWidget/AddWidgetResult.php @@ -14,9 +14,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\AddWidget; -final class AddWidgetResult +final readonly class AddWidgetResult { public function __construct(public readonly int $id) { diff --git a/src/Handler/Portal/CreateDashboardHandler.php b/src/Handler/Portal/CreateDashboard/CreateDashboardHandler.php similarity index 76% rename from src/Handler/Portal/CreateDashboardHandler.php rename to src/Handler/Portal/CreateDashboard/CreateDashboardHandler.php index 672b5b22..5b170b28 100644 --- a/src/Handler/Portal/CreateDashboardHandler.php +++ b/src/Handler/Portal/CreateDashboard/CreateDashboardHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\CreateDashboard; use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory; @@ -24,19 +24,19 @@ public function __construct(private readonly DashboardFactory $dashboardFactory) { } - public function __invoke(string $key): void + public function __invoke(CreateDashboardPayload $payload): void { - if (empty($key)) { + if (empty($payload->key)) { throw new \InvalidArgumentException('empty'); } $dashboard = $this->dashboardFactory->create(); $dashboards = $dashboard->getAllDashboards(); - if (isset($dashboards[$key])) { + if (isset($dashboards[$payload->key])) { throw new \InvalidArgumentException('name_already_in_use'); } - $dashboard->saveDashboard($key); + $dashboard->saveDashboard($payload->key); } } diff --git a/src/Handler/Portal/CreateDashboard/CreateDashboardPayload.php b/src/Handler/Portal/CreateDashboard/CreateDashboardPayload.php new file mode 100644 index 00000000..054b0ab1 --- /dev/null +++ b/src/Handler/Portal/CreateDashboard/CreateDashboardPayload.php @@ -0,0 +1,32 @@ +request->get('key', ''))); + } +} diff --git a/src/Handler/Portal/DeleteDashboardHandler.php b/src/Handler/Portal/DeleteDashboard/DeleteDashboardHandler.php similarity index 78% rename from src/Handler/Portal/DeleteDashboardHandler.php rename to src/Handler/Portal/DeleteDashboard/DeleteDashboardHandler.php index 5e5b5eef..ae354be3 100644 --- a/src/Handler/Portal/DeleteDashboardHandler.php +++ b/src/Handler/Portal/DeleteDashboard/DeleteDashboardHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\DeleteDashboard; use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory; @@ -24,9 +24,9 @@ public function __construct(private readonly DashboardFactory $dashboardFactory) { } - public function __invoke(string $key): void + public function __invoke(DeleteDashboardPayload $payload): void { $dashboard = $this->dashboardFactory->create(); - $dashboard->deleteDashboard($key); + $dashboard->deleteDashboard($payload->key); } } diff --git a/src/Handler/Portal/DeleteDashboard/DeleteDashboardPayload.php b/src/Handler/Portal/DeleteDashboard/DeleteDashboardPayload.php new file mode 100644 index 00000000..3a71d456 --- /dev/null +++ b/src/Handler/Portal/DeleteDashboard/DeleteDashboardPayload.php @@ -0,0 +1,32 @@ +request->get('key')); + } +} diff --git a/src/Handler/Portal/GetDashboardConfigurationHandler.php b/src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationHandler.php similarity index 76% rename from src/Handler/Portal/GetDashboardConfigurationHandler.php rename to src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationHandler.php index 8406f890..a9b88109 100644 --- a/src/Handler/Portal/GetDashboardConfigurationHandler.php +++ b/src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardConfiguration; use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory; @@ -24,10 +24,10 @@ public function __construct(private readonly DashboardFactory $dashboardFactory) { } - public function __invoke(?string $key): GetDashboardConfigurationResult + public function __invoke(GetDashboardConfigurationPayload $payload): GetDashboardConfigurationResult { $dashboard = $this->dashboardFactory->create(); - return new GetDashboardConfigurationResult(config: $dashboard->getDashboard($key ?? 'welcome')); + return new GetDashboardConfigurationResult(config: $dashboard->getDashboard($payload->key ?? 'welcome')); } } diff --git a/src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationPayload.php b/src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationPayload.php new file mode 100644 index 00000000..6bf7951f --- /dev/null +++ b/src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationPayload.php @@ -0,0 +1,34 @@ +query->has('key') ? (string) $request->query->get('key') : null, + ); + } +} diff --git a/src/Handler/Portal/GetDashboardConfigurationResult.php b/src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationResult.php similarity index 79% rename from src/Handler/Portal/GetDashboardConfigurationResult.php rename to src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationResult.php index dc836a80..24a6ea6d 100644 --- a/src/Handler/Portal/GetDashboardConfigurationResult.php +++ b/src/Handler/Portal/GetDashboardConfiguration/GetDashboardConfigurationResult.php @@ -14,9 +14,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardConfiguration; -final class GetDashboardConfigurationResult +final readonly class GetDashboardConfigurationResult { public function __construct(public readonly array $config) { diff --git a/src/Handler/Portal/GetDashboardListHandler.php b/src/Handler/Portal/GetDashboardList/GetDashboardListHandler.php similarity index 82% rename from src/Handler/Portal/GetDashboardListHandler.php rename to src/Handler/Portal/GetDashboardList/GetDashboardListHandler.php index 0a0c7a01..80fdf130 100644 --- a/src/Handler/Portal/GetDashboardListHandler.php +++ b/src/Handler/Portal/GetDashboardList/GetDashboardListHandler.php @@ -14,9 +14,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardList; use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; final class GetDashboardListHandler { @@ -24,7 +25,7 @@ public function __construct(private readonly DashboardFactory $dashboardFactory) { } - public function __invoke(): GetDashboardListResult + public function __invoke(EmptyPayload $payload): GetDashboardListResult { $dashboard = $this->dashboardFactory->create(); diff --git a/src/Handler/Portal/GetDashboardListResult.php b/src/Handler/Portal/GetDashboardList/GetDashboardListResult.php similarity index 81% rename from src/Handler/Portal/GetDashboardListResult.php rename to src/Handler/Portal/GetDashboardList/GetDashboardListResult.php index ce0178ed..dfbb5740 100644 --- a/src/Handler/Portal/GetDashboardListResult.php +++ b/src/Handler/Portal/GetDashboardList/GetDashboardListResult.php @@ -14,9 +14,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetDashboardList; -final class GetDashboardListResult +final readonly class GetDashboardListResult { public function __construct(public readonly array $dashboards) { diff --git a/src/Handler/Portal/GetModificationStatisticsHandler.php b/src/Handler/Portal/GetModificationStatistics/GetModificationStatisticsHandler.php similarity index 89% rename from src/Handler/Portal/GetModificationStatisticsHandler.php rename to src/Handler/Portal/GetModificationStatistics/GetModificationStatisticsHandler.php index beb99e15..3cef9b72 100644 --- a/src/Handler/Portal/GetModificationStatisticsHandler.php +++ b/src/Handler/Portal/GetModificationStatistics/GetModificationStatisticsHandler.php @@ -15,13 +15,14 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModificationStatistics; use DateTime; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; final class GetModificationStatisticsHandler { - public function __invoke(): GetModificationStatisticsResult + public function __invoke(EmptyPayload $payload): GetModificationStatisticsResult { $db = \OpenDxp\Db::get(); diff --git a/src/Handler/Portal/GetModificationStatisticsResult.php b/src/Handler/Portal/GetModificationStatistics/GetModificationStatisticsResult.php similarity index 87% rename from src/Handler/Portal/GetModificationStatisticsResult.php rename to src/Handler/Portal/GetModificationStatistics/GetModificationStatisticsResult.php index c64820c7..ce97ac70 100644 --- a/src/Handler/Portal/GetModificationStatisticsResult.php +++ b/src/Handler/Portal/GetModificationStatistics/GetModificationStatisticsResult.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModificationStatistics; final readonly class GetModificationStatisticsResult { public function __construct( public array $data, - ) {} + ) { + } } diff --git a/src/Handler/Portal/GetModifiedAssetsHandler.php b/src/Handler/Portal/GetModifiedAssets/GetModifiedAssetsHandler.php similarity index 87% rename from src/Handler/Portal/GetModifiedAssetsHandler.php rename to src/Handler/Portal/GetModifiedAssets/GetModifiedAssetsHandler.php index 25b16985..3cf89c01 100644 --- a/src/Handler/Portal/GetModifiedAssetsHandler.php +++ b/src/Handler/Portal/GetModifiedAssets/GetModifiedAssetsHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedAssets; -use OpenDxp\Model\Asset; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; +use OpenDxp\Model\Asset; final class GetModifiedAssetsHandler { @@ -26,7 +27,7 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(): GetModifiedAssetsResult + public function __invoke(EmptyPayload $payload): GetModifiedAssetsResult { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; $list = Asset::getList([ diff --git a/src/Handler/Portal/GetModifiedAssetsResult.php b/src/Handler/Portal/GetModifiedAssets/GetModifiedAssetsResult.php similarity index 87% rename from src/Handler/Portal/GetModifiedAssetsResult.php rename to src/Handler/Portal/GetModifiedAssets/GetModifiedAssetsResult.php index 1622ba54..d99a45a0 100644 --- a/src/Handler/Portal/GetModifiedAssetsResult.php +++ b/src/Handler/Portal/GetModifiedAssets/GetModifiedAssetsResult.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedAssets; final readonly class GetModifiedAssetsResult { public function __construct( public array $assets, - ) {} + ) { + } } diff --git a/src/Handler/Portal/GetModifiedDocumentsHandler.php b/src/Handler/Portal/GetModifiedDocuments/GetModifiedDocumentsHandler.php similarity index 87% rename from src/Handler/Portal/GetModifiedDocumentsHandler.php rename to src/Handler/Portal/GetModifiedDocuments/GetModifiedDocumentsHandler.php index 7054251d..309f5666 100644 --- a/src/Handler/Portal/GetModifiedDocumentsHandler.php +++ b/src/Handler/Portal/GetModifiedDocuments/GetModifiedDocumentsHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedDocuments; -use OpenDxp\Model\Document; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; +use OpenDxp\Model\Document; final class GetModifiedDocumentsHandler { @@ -26,7 +27,7 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(): GetModifiedDocumentsResult + public function __invoke(EmptyPayload $payload): GetModifiedDocumentsResult { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; $list = Document::getList([ diff --git a/src/Handler/Portal/GetModifiedDocumentsResult.php b/src/Handler/Portal/GetModifiedDocuments/GetModifiedDocumentsResult.php similarity index 87% rename from src/Handler/Portal/GetModifiedDocumentsResult.php rename to src/Handler/Portal/GetModifiedDocuments/GetModifiedDocumentsResult.php index c04b5282..32ca1725 100644 --- a/src/Handler/Portal/GetModifiedDocumentsResult.php +++ b/src/Handler/Portal/GetModifiedDocuments/GetModifiedDocumentsResult.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedDocuments; final readonly class GetModifiedDocumentsResult { public function __construct( public array $documents, - ) {} + ) { + } } diff --git a/src/Handler/Portal/GetModifiedObjectsHandler.php b/src/Handler/Portal/GetModifiedObjects/GetModifiedObjectsHandler.php similarity index 87% rename from src/Handler/Portal/GetModifiedObjectsHandler.php rename to src/Handler/Portal/GetModifiedObjects/GetModifiedObjectsHandler.php index 39dce2da..d470e5f2 100644 --- a/src/Handler/Portal/GetModifiedObjectsHandler.php +++ b/src/Handler/Portal/GetModifiedObjects/GetModifiedObjectsHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedObjects; -use OpenDxp\Model\DataObject; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; +use OpenDxp\Model\DataObject; final class GetModifiedObjectsHandler { @@ -26,7 +27,7 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(): GetModifiedObjectsResult + public function __invoke(EmptyPayload $payload): GetModifiedObjectsResult { $userId = $this->userContext->getAdminUser()?->getId() ?? 0; $list = DataObject::getList([ diff --git a/src/Handler/Portal/GetModifiedObjectsResult.php b/src/Handler/Portal/GetModifiedObjects/GetModifiedObjectsResult.php similarity index 87% rename from src/Handler/Portal/GetModifiedObjectsResult.php rename to src/Handler/Portal/GetModifiedObjects/GetModifiedObjectsResult.php index b8236fec..4c9c9516 100644 --- a/src/Handler/Portal/GetModifiedObjectsResult.php +++ b/src/Handler/Portal/GetModifiedObjects/GetModifiedObjectsResult.php @@ -15,11 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\GetModifiedObjects; final readonly class GetModifiedObjectsResult { public function __construct( public array $objects, - ) {} + ) { + } } diff --git a/src/Handler/Portal/RemoveWidgetHandler.php b/src/Handler/Portal/RemoveWidget/RemoveWidgetHandler.php similarity index 75% rename from src/Handler/Portal/RemoveWidgetHandler.php rename to src/Handler/Portal/RemoveWidget/RemoveWidgetHandler.php index 372ab5c8..bb95538d 100644 --- a/src/Handler/Portal/RemoveWidgetHandler.php +++ b/src/Handler/Portal/RemoveWidget/RemoveWidgetHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\RemoveWidget; use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory; @@ -24,17 +24,17 @@ public function __construct(private readonly DashboardFactory $dashboardFactory) { } - public function __invoke(string $dashboardId, ?int $widgetId): void + public function __invoke(RemoveWidgetPayload $payload): void { $dashboard = $this->dashboardFactory->create(); - $config = $dashboard->getDashboard($dashboardId); + $config = $dashboard->getDashboard($payload->dashboardId); $newConfig = [[], []]; $colCount = 0; foreach ($config['positions'] as $col) { foreach ($col as $row) { - if ($row['id'] !== $widgetId) { + if ($row['id'] !== $payload->widgetId) { $newConfig[$colCount][] = $row; } } @@ -42,6 +42,6 @@ public function __invoke(string $dashboardId, ?int $widgetId): void } $config['positions'] = $newConfig; - $dashboard->saveDashboard($dashboardId, $config); + $dashboard->saveDashboard($payload->dashboardId, $config); } } diff --git a/src/Handler/Portal/RemoveWidget/RemoveWidgetPayload.php b/src/Handler/Portal/RemoveWidget/RemoveWidgetPayload.php new file mode 100644 index 00000000..dac73169 --- /dev/null +++ b/src/Handler/Portal/RemoveWidget/RemoveWidgetPayload.php @@ -0,0 +1,37 @@ +request->get('key'), + widgetId: $request->request->has('id') ? (int) $request->request->get('id') : null, + ); + } +} diff --git a/src/Handler/Portal/ReorderWidgetHandler.php b/src/Handler/Portal/ReorderWidget/ReorderWidgetHandler.php similarity index 72% rename from src/Handler/Portal/ReorderWidgetHandler.php rename to src/Handler/Portal/ReorderWidget/ReorderWidgetHandler.php index eef4ff5b..f3059316 100644 --- a/src/Handler/Portal/ReorderWidgetHandler.php +++ b/src/Handler/Portal/ReorderWidget/ReorderWidgetHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\ReorderWidget; use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory; @@ -24,18 +24,18 @@ public function __construct(private readonly DashboardFactory $dashboardFactory) { } - public function __invoke(string $dashboardId, ?int $widgetId, int $column, int $row): void + public function __invoke(ReorderWidgetPayload $payload): void { $dashboard = $this->dashboardFactory->create(); - $config = $dashboard->getDashboard($dashboardId); + $config = $dashboard->getDashboard($payload->dashboardId); $newConfig = [[], []]; $colCount = 0; $toMove = null; foreach ($config['positions'] as $col) { foreach ($col as $item) { - if ($item['id'] !== $widgetId) { + if ($item['id'] !== $payload->widgetId) { $newConfig[$colCount][] = $item; } else { $toMove = $item; @@ -44,9 +44,9 @@ public function __invoke(string $dashboardId, ?int $widgetId, int $column, int $ $colCount++; } - array_splice($newConfig[$column], $row, 0, [$toMove]); + array_splice($newConfig[$payload->column], $payload->row, 0, [$toMove]); $config['positions'] = $newConfig; - $dashboard->saveDashboard($dashboardId, $config); + $dashboard->saveDashboard($payload->dashboardId, $config); } } diff --git a/src/Handler/Portal/ReorderWidget/ReorderWidgetPayload.php b/src/Handler/Portal/ReorderWidget/ReorderWidgetPayload.php new file mode 100644 index 00000000..e30d53e6 --- /dev/null +++ b/src/Handler/Portal/ReorderWidget/ReorderWidgetPayload.php @@ -0,0 +1,41 @@ +request->get('key'), + widgetId: $request->request->has('id') ? (int) $request->request->get('id') : null, + column: $request->request->getInt('column'), + row: $request->request->getInt('row'), + ); + } +} diff --git a/src/Handler/Portal/UpdatePortletConfigHandler.php b/src/Handler/Portal/UpdatePortletConfig/UpdatePortletConfigHandler.php similarity index 67% rename from src/Handler/Portal/UpdatePortletConfigHandler.php rename to src/Handler/Portal/UpdatePortletConfig/UpdatePortletConfigHandler.php index b91b3274..ce8bced0 100644 --- a/src/Handler/Portal/UpdatePortletConfigHandler.php +++ b/src/Handler/Portal/UpdatePortletConfig/UpdatePortletConfigHandler.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Portal; +namespace OpenDxp\Bundle\AdminBundle\Handler\Portal\UpdatePortletConfig; use OpenDxp\Bundle\AdminBundle\Factory\DashboardFactory; @@ -24,20 +24,20 @@ public function __construct(private readonly DashboardFactory $dashboardFactory) { } - public function __invoke(string $dashboardKey, ?int $portletId, mixed $configuration): void + public function __invoke(UpdatePortletConfigPayload $payload): void { $dashboard = $this->dashboardFactory->create(); - $config = $dashboard->getDashboard($dashboardKey); + $config = $dashboard->getDashboard($payload->dashboardKey); foreach ($config['positions'] as &$col) { foreach ($col as &$portlet) { - if ($portlet['id'] === $portletId) { - $portlet['config'] = $configuration; + if ($portlet['id'] === $payload->portletId) { + $portlet['config'] = $payload->configuration; break; } } } - $dashboard->saveDashboard($dashboardKey, $config); + $dashboard->saveDashboard($payload->dashboardKey, $config); } } diff --git a/src/Handler/Portal/UpdatePortletConfig/UpdatePortletConfigPayload.php b/src/Handler/Portal/UpdatePortletConfig/UpdatePortletConfigPayload.php new file mode 100644 index 00000000..11760aca --- /dev/null +++ b/src/Handler/Portal/UpdatePortletConfig/UpdatePortletConfigPayload.php @@ -0,0 +1,39 @@ +request->get('key'), + portletId: $request->request->has('id') ? (int) $request->request->get('id') : null, + configuration: $request->request->get('config'), + ); + } +} diff --git a/src/Handler/Recyclebin/AddToRecyclebinHandler.php b/src/Handler/Recyclebin/AddToRecyclebin/AddToRecyclebinHandler.php similarity index 85% rename from src/Handler/Recyclebin/AddToRecyclebinHandler.php rename to src/Handler/Recyclebin/AddToRecyclebin/AddToRecyclebinHandler.php index f12c85cb..6057916c 100644 --- a/src/Handler/Recyclebin/AddToRecyclebinHandler.php +++ b/src/Handler/Recyclebin/AddToRecyclebin/AddToRecyclebinHandler.php @@ -15,11 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin; +namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\AddToRecyclebin; use OpenDxp\Model\Element\Recyclebin; use OpenDxp\Model\Element\Service; -use OpenDxp\Model\User; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class AddToRecyclebinHandler @@ -28,10 +27,10 @@ public function __construct(private readonly AdminUserContextInterface $userCont { } - public function __invoke(string $type, int $id): void + public function __invoke(AddToRecyclebinPayload $payload): void { $adminUser = $this->userContext->getAdminUser(); - $element = Service::getElementById($type, $id); + $element = Service::getElementById($payload->type, $payload->id); if (!$element) { return; diff --git a/src/Handler/Recyclebin/AddToRecyclebin/AddToRecyclebinPayload.php b/src/Handler/Recyclebin/AddToRecyclebin/AddToRecyclebinPayload.php new file mode 100644 index 00000000..22fd1c6a --- /dev/null +++ b/src/Handler/Recyclebin/AddToRecyclebin/AddToRecyclebinPayload.php @@ -0,0 +1,36 @@ +request->get('type'), + id: $request->request->getInt('id'), + ); + } +} diff --git a/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php b/src/Handler/Recyclebin/DeleteRecyclebinItem/DeleteRecyclebinItemHandler.php similarity index 84% rename from src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php rename to src/Handler/Recyclebin/DeleteRecyclebinItem/DeleteRecyclebinItemHandler.php index eff75ba6..ffe07c07 100644 --- a/src/Handler/Recyclebin/DeleteRecyclebinItemHandler.php +++ b/src/Handler/Recyclebin/DeleteRecyclebinItem/DeleteRecyclebinItemHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin; +namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\DeleteRecyclebinItem; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RecyclebinPayload; use OpenDxp\Model\Element\Recyclebin; final class DeleteRecyclebinItemHandler diff --git a/src/Handler/Login/LostPasswordResult.php b/src/Handler/Recyclebin/FlushRecyclebin/FlushRecyclebinHandler.php similarity index 56% rename from src/Handler/Login/LostPasswordResult.php rename to src/Handler/Recyclebin/FlushRecyclebin/FlushRecyclebinHandler.php index 71db170b..df6b997e 100644 --- a/src/Handler/Login/LostPasswordResult.php +++ b/src/Handler/Recyclebin/FlushRecyclebin/FlushRecyclebinHandler.php @@ -14,14 +14,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Login; +namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\FlushRecyclebin; -use Symfony\Component\HttpFoundation\Response; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; +use OpenDxp\Model\Element\Recyclebin; -final class LostPasswordResult +final class FlushRecyclebinHandler { - public function __construct( - public readonly ?string $error, - public readonly ?Response $eventResponse = null, - ) {} + public function __invoke(EmptyPayload $payload): void + { + $bin = new Recyclebin(); + $bin->flush(); + } } diff --git a/src/Handler/Recyclebin/ListRecyclebinHandler.php b/src/Handler/Recyclebin/ListRecyclebin/ListRecyclebinHandler.php similarity index 96% rename from src/Handler/Recyclebin/ListRecyclebinHandler.php rename to src/Handler/Recyclebin/ListRecyclebin/ListRecyclebinHandler.php index 7c669235..19a5c6b0 100644 --- a/src/Handler/Recyclebin/ListRecyclebinHandler.php +++ b/src/Handler/Recyclebin/ListRecyclebin/ListRecyclebinHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin; +namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\ListRecyclebin; +use OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RecyclebinPayload; use OpenDxp\Model\Element\Recyclebin; final class ListRecyclebinHandler diff --git a/src/Handler/Recyclebin/ListRecyclebinResult.php b/src/Handler/Recyclebin/ListRecyclebin/ListRecyclebinResult.php similarity index 90% rename from src/Handler/Recyclebin/ListRecyclebinResult.php rename to src/Handler/Recyclebin/ListRecyclebin/ListRecyclebinResult.php index c5596407..f335b425 100644 --- a/src/Handler/Recyclebin/ListRecyclebinResult.php +++ b/src/Handler/Recyclebin/ListRecyclebin/ListRecyclebinResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin; +namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\ListRecyclebin; final readonly class ListRecyclebinResult { diff --git a/src/Handler/Recyclebin/RestoreRecyclebinItemHandler.php b/src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemHandler.php similarity index 79% rename from src/Handler/Recyclebin/RestoreRecyclebinItemHandler.php rename to src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemHandler.php index 731b0159..e27b7ae7 100644 --- a/src/Handler/Recyclebin/RestoreRecyclebinItemHandler.php +++ b/src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemHandler.php @@ -15,16 +15,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin; +namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RestoreRecyclebinItem; use OpenDxp\Model\Element\Recyclebin; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class RestoreRecyclebinItemHandler { - public function __invoke(int $id): void + public function __invoke(RestoreRecyclebinItemPayload $payload): void { - $item = Recyclebin\Item::getById($id); + $item = Recyclebin\Item::getById($payload->id); if (!$item) { throw new NotFoundHttpException(sprintf('Recyclebin item with id %d not found', $id)); } diff --git a/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsPayload.php b/src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemPayload.php similarity index 68% rename from src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsPayload.php rename to src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemPayload.php index b97bc2d5..2c1542f5 100644 --- a/src/Handler/DataObject/Copy/GetDataObjectChildIds/GetDataObjectChildIdsPayload.php +++ b/src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemPayload.php @@ -14,17 +14,17 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Copy\GetDataObjectChildIds; +namespace OpenDxp\Bundle\AdminBundle\Handler\Recyclebin\RestoreRecyclebinItem; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -final readonly class GetDataObjectChildIdsPayload implements ExtJsPayloadInterface +final readonly class RestoreRecyclebinItemPayload implements ExtJsPayloadInterface { - public function __construct(public readonly int $sourceId = 0) {} + public function __construct(public readonly int $id = 0) {} public static function fromRequest(Request $request): static { - return new static(sourceId: $request->query->getInt('sourceId')); + return new static(id: (int) $request->request->get('id')); } } diff --git a/src/Handler/Settings/AddThumbnailHandler.php b/src/Handler/Settings/AddThumbnail/AddThumbnailHandler.php similarity index 76% rename from src/Handler/Settings/AddThumbnailHandler.php rename to src/Handler/Settings/AddThumbnail/AddThumbnailHandler.php index ace75079..1b5d7e13 100644 --- a/src/Handler/Settings/AddThumbnailHandler.php +++ b/src/Handler/Settings/AddThumbnail/AddThumbnailHandler.php @@ -15,23 +15,24 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\AddThumbnail; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\AddThumbnail\AddThumbnailPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Exception\ConfigWriteException; final class AddThumbnailHandler { - public function __invoke(string $name): AddThumbnailResult + public function __invoke(AddThumbnailPayload $payload): AddThumbnailResult { - $pipe = Asset\Image\Thumbnail\Config::getByName($name); + $pipe = Asset\Image\Thumbnail\Config::getByName($payload->name); if (!$pipe) { $pipe = new Asset\Image\Thumbnail\Config(); if (!$pipe->isWriteable()) { throw new ConfigWriteException(); } - $pipe->setName($name); + $pipe->setName($payload->name); $pipe->save(); return new AddThumbnailResult(id: $pipe->getName(), created: true); diff --git a/src/Handler/Settings/AddThumbnail/AddThumbnailPayload.php b/src/Handler/Settings/AddThumbnail/AddThumbnailPayload.php new file mode 100644 index 00000000..47062c07 --- /dev/null +++ b/src/Handler/Settings/AddThumbnail/AddThumbnailPayload.php @@ -0,0 +1,34 @@ +request->getString('name'), + ); + } +} diff --git a/src/Handler/Settings/AddThumbnailResult.php b/src/Handler/Settings/AddThumbnail/AddThumbnailResult.php similarity index 90% rename from src/Handler/Settings/AddThumbnailResult.php rename to src/Handler/Settings/AddThumbnail/AddThumbnailResult.php index 58393b44..f424164e 100644 --- a/src/Handler/Settings/AddThumbnailResult.php +++ b/src/Handler/Settings/AddThumbnail/AddThumbnailResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\AddThumbnail; final readonly class AddThumbnailResult { diff --git a/src/Handler/Settings/AddVideoThumbnailHandler.php b/src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailHandler.php similarity index 75% rename from src/Handler/Settings/AddVideoThumbnailHandler.php rename to src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailHandler.php index 1c0c19d3..73f0ad50 100644 --- a/src/Handler/Settings/AddVideoThumbnailHandler.php +++ b/src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailHandler.php @@ -15,23 +15,24 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\AddVideoThumbnail; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\AddVideoThumbnail\AddVideoThumbnailPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Exception\ConfigWriteException; final class AddVideoThumbnailHandler { - public function __invoke(string $name): AddVideoThumbnailResult + public function __invoke(AddVideoThumbnailPayload $payload): AddVideoThumbnailResult { - $pipe = Asset\Video\Thumbnail\Config::getByName($name); + $pipe = Asset\Video\Thumbnail\Config::getByName($payload->name); if (!$pipe) { $pipe = new Asset\Video\Thumbnail\Config(); if (!$pipe->isWriteable()) { throw new ConfigWriteException(); } - $pipe->setName($name); + $pipe->setName($payload->name); $pipe->save(); return new AddVideoThumbnailResult(id: $pipe->getName(), created: true); diff --git a/src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailPayload.php b/src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailPayload.php new file mode 100644 index 00000000..4f4a57ec --- /dev/null +++ b/src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailPayload.php @@ -0,0 +1,34 @@ +request->getString('name'), + ); + } +} diff --git a/src/Handler/Settings/AddVideoThumbnailResult.php b/src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailResult.php similarity index 90% rename from src/Handler/Settings/AddVideoThumbnailResult.php rename to src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailResult.php index 04902dc4..64213f1c 100644 --- a/src/Handler/Settings/AddVideoThumbnailResult.php +++ b/src/Handler/Settings/AddVideoThumbnail/AddVideoThumbnailResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\AddVideoThumbnail; final readonly class AddVideoThumbnailResult { diff --git a/src/Handler/Settings/ClearCache/ClearCacheHandler.php b/src/Handler/Settings/ClearCache/ClearCacheHandler.php new file mode 100644 index 00000000..4e51929d --- /dev/null +++ b/src/Handler/Settings/ClearCache/ClearCacheHandler.php @@ -0,0 +1,43 @@ +env ?: $this->kernel->getEnvironment(); + + if (!$payload->onlySymfonyCache) { + $this->openDxpCache->clear(); + } + + if (!$payload->onlyOpendxpCache) { + $this->symfonyCache->clear($env); + } + } +} diff --git a/src/Handler/Settings/ClearCache/ClearCachePayload.php b/src/Handler/Settings/ClearCache/ClearCachePayload.php new file mode 100644 index 00000000..b37c287b --- /dev/null +++ b/src/Handler/Settings/ClearCache/ClearCachePayload.php @@ -0,0 +1,38 @@ +request->getString('only_symfony_cache'), + onlyOpendxpCache: (bool) $request->request->getString('only_opendxp_cache'), + env: $request->request->getString('env'), + ); + } +} diff --git a/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php b/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php new file mode 100644 index 00000000..97b6a876 --- /dev/null +++ b/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php @@ -0,0 +1,30 @@ +service->clear(); + } +} diff --git a/src/Handler/Settings/ClearOutputCacheHandler.php b/src/Handler/Settings/ClearOutputCache/ClearOutputCacheHandler.php similarity index 93% rename from src/Handler/Settings/ClearOutputCacheHandler.php rename to src/Handler/Settings/ClearOutputCache/ClearOutputCacheHandler.php index d1e69007..47cf6ac1 100644 --- a/src/Handler/Settings/ClearOutputCacheHandler.php +++ b/src/Handler/Settings/ClearOutputCache/ClearOutputCacheHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearOutputCache; use OpenDxp\Cache; use OpenDxp\Event\SystemEvents; diff --git a/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php b/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php new file mode 100644 index 00000000..3e6afbf9 --- /dev/null +++ b/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php @@ -0,0 +1,30 @@ +service->clear($environment); + } +} diff --git a/src/Handler/Settings/ClearTemporaryFilesHandler.php b/src/Handler/Settings/ClearTemporaryFiles/ClearTemporaryFilesHandler.php similarity index 94% rename from src/Handler/Settings/ClearTemporaryFilesHandler.php rename to src/Handler/Settings/ClearTemporaryFiles/ClearTemporaryFilesHandler.php index ad7f9d49..59808b23 100644 --- a/src/Handler/Settings/ClearTemporaryFilesHandler.php +++ b/src/Handler/Settings/ClearTemporaryFiles/ClearTemporaryFilesHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearTemporaryFiles; use OpenDxp\Db; use OpenDxp\Event\SystemEvents; diff --git a/src/Handler/Settings/CreatePredefinedMetadataHandler.php b/src/Handler/Settings/CreatePredefinedMetadata/CreatePredefinedMetadataHandler.php similarity index 91% rename from src/Handler/Settings/CreatePredefinedMetadataHandler.php rename to src/Handler/Settings/CreatePredefinedMetadata/CreatePredefinedMetadataHandler.php index 4a26f5b3..2fddb509 100644 --- a/src/Handler/Settings/CreatePredefinedMetadataHandler.php +++ b/src/Handler/Settings/CreatePredefinedMetadata/CreatePredefinedMetadataHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\CreatePredefinedMetadata; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedMetadataPayload; use OpenDxp\Model\Exception\ConfigWriteException; use OpenDxp\Model\Metadata; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; diff --git a/src/Handler/Settings/CreatePredefinedMetadataResult.php b/src/Handler/Settings/CreatePredefinedMetadata/CreatePredefinedMetadataResult.php similarity index 88% rename from src/Handler/Settings/CreatePredefinedMetadataResult.php rename to src/Handler/Settings/CreatePredefinedMetadata/CreatePredefinedMetadataResult.php index ecfe2bea..90a87383 100644 --- a/src/Handler/Settings/CreatePredefinedMetadataResult.php +++ b/src/Handler/Settings/CreatePredefinedMetadata/CreatePredefinedMetadataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\CreatePredefinedMetadata; final readonly class CreatePredefinedMetadataResult { diff --git a/src/Handler/Settings/CreatePredefinedPropertyHandler.php b/src/Handler/Settings/CreatePredefinedProperty/CreatePredefinedPropertyHandler.php similarity index 88% rename from src/Handler/Settings/CreatePredefinedPropertyHandler.php rename to src/Handler/Settings/CreatePredefinedProperty/CreatePredefinedPropertyHandler.php index 734b2dd9..ce462ed0 100644 --- a/src/Handler/Settings/CreatePredefinedPropertyHandler.php +++ b/src/Handler/Settings/CreatePredefinedProperty/CreatePredefinedPropertyHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\CreatePredefinedProperty; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedPropertyPayload; use OpenDxp\Model\Exception\ConfigWriteException; use OpenDxp\Model\Property; diff --git a/src/Handler/Settings/CreatePredefinedPropertyResult.php b/src/Handler/Settings/CreatePredefinedProperty/CreatePredefinedPropertyResult.php similarity index 88% rename from src/Handler/Settings/CreatePredefinedPropertyResult.php rename to src/Handler/Settings/CreatePredefinedProperty/CreatePredefinedPropertyResult.php index 919de52e..77b03e61 100644 --- a/src/Handler/Settings/CreatePredefinedPropertyResult.php +++ b/src/Handler/Settings/CreatePredefinedProperty/CreatePredefinedPropertyResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\CreatePredefinedProperty; final readonly class CreatePredefinedPropertyResult { diff --git a/src/Handler/Settings/CreateWebsiteSettingHandler.php b/src/Handler/Settings/CreateWebsiteSetting/CreateWebsiteSettingHandler.php similarity index 86% rename from src/Handler/Settings/CreateWebsiteSettingHandler.php rename to src/Handler/Settings/CreateWebsiteSetting/CreateWebsiteSettingHandler.php index 38608b56..8f36c57b 100644 --- a/src/Handler/Settings/CreateWebsiteSettingHandler.php +++ b/src/Handler/Settings/CreateWebsiteSetting/CreateWebsiteSettingHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\CreateWebsiteSetting; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\WebsiteSettingPayload; use OpenDxp\Model\WebsiteSetting; final class CreateWebsiteSettingHandler diff --git a/src/Handler/Settings/CreateWebsiteSettingResult.php b/src/Handler/Settings/CreateWebsiteSetting/CreateWebsiteSettingResult.php similarity index 89% rename from src/Handler/Settings/CreateWebsiteSettingResult.php rename to src/Handler/Settings/CreateWebsiteSetting/CreateWebsiteSettingResult.php index 4d921c58..fa63c719 100644 --- a/src/Handler/Settings/CreateWebsiteSettingResult.php +++ b/src/Handler/Settings/CreateWebsiteSetting/CreateWebsiteSettingResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\CreateWebsiteSetting; final readonly class CreateWebsiteSettingResult { diff --git a/src/Handler/Settings/DeleteCustomLogoHandler.php b/src/Handler/Settings/DeleteCustomLogo/DeleteCustomLogoHandler.php similarity index 92% rename from src/Handler/Settings/DeleteCustomLogoHandler.php rename to src/Handler/Settings/DeleteCustomLogo/DeleteCustomLogoHandler.php index 94535b94..6c41173a 100644 --- a/src/Handler/Settings/DeleteCustomLogoHandler.php +++ b/src/Handler/Settings/DeleteCustomLogo/DeleteCustomLogoHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteCustomLogo; use OpenDxp\Tool; diff --git a/src/Handler/Settings/DeletePredefinedMetadataHandler.php b/src/Handler/Settings/DeletePredefinedMetadata/DeletePredefinedMetadataHandler.php similarity index 85% rename from src/Handler/Settings/DeletePredefinedMetadataHandler.php rename to src/Handler/Settings/DeletePredefinedMetadata/DeletePredefinedMetadataHandler.php index 23d06a0f..b23ef57d 100644 --- a/src/Handler/Settings/DeletePredefinedMetadataHandler.php +++ b/src/Handler/Settings/DeletePredefinedMetadata/DeletePredefinedMetadataHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\DeletePredefinedMetadata; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedMetadataPayload; use OpenDxp\Model\Exception\ConfigWriteException; use OpenDxp\Model\Metadata; diff --git a/src/Handler/Settings/DeletePredefinedPropertyHandler.php b/src/Handler/Settings/DeletePredefinedProperty/DeletePredefinedPropertyHandler.php similarity index 85% rename from src/Handler/Settings/DeletePredefinedPropertyHandler.php rename to src/Handler/Settings/DeletePredefinedProperty/DeletePredefinedPropertyHandler.php index f1e3ebe1..67d12acb 100644 --- a/src/Handler/Settings/DeletePredefinedPropertyHandler.php +++ b/src/Handler/Settings/DeletePredefinedProperty/DeletePredefinedPropertyHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\DeletePredefinedProperty; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedPropertyPayload; use OpenDxp\Model\Exception\ConfigWriteException; use OpenDxp\Model\Property; diff --git a/src/Handler/Settings/DeleteThumbnailHandler.php b/src/Handler/Settings/DeleteThumbnail/DeleteThumbnailHandler.php similarity index 71% rename from src/Handler/Settings/DeleteThumbnailHandler.php rename to src/Handler/Settings/DeleteThumbnail/DeleteThumbnailHandler.php index 4fb64a68..6bfe0b54 100644 --- a/src/Handler/Settings/DeleteThumbnailHandler.php +++ b/src/Handler/Settings/DeleteThumbnail/DeleteThumbnailHandler.php @@ -15,16 +15,17 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteThumbnail; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteThumbnail\DeleteThumbnailPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Exception\ConfigWriteException; final class DeleteThumbnailHandler { - public function __invoke(string $name): void + public function __invoke(DeleteThumbnailPayload $payload): void { - $pipe = Asset\Image\Thumbnail\Config::getByName($name); + $pipe = Asset\Image\Thumbnail\Config::getByName($payload->name); if (!$pipe->isWriteable()) { throw new ConfigWriteException(); diff --git a/src/Handler/Settings/DeleteThumbnail/DeleteThumbnailPayload.php b/src/Handler/Settings/DeleteThumbnail/DeleteThumbnailPayload.php new file mode 100644 index 00000000..05d05b4c --- /dev/null +++ b/src/Handler/Settings/DeleteThumbnail/DeleteThumbnailPayload.php @@ -0,0 +1,34 @@ +request->getString('name'), + ); + } +} diff --git a/src/Handler/Settings/DeleteVideoThumbnailHandler.php b/src/Handler/Settings/DeleteVideoThumbnail/DeleteVideoThumbnailHandler.php similarity index 70% rename from src/Handler/Settings/DeleteVideoThumbnailHandler.php rename to src/Handler/Settings/DeleteVideoThumbnail/DeleteVideoThumbnailHandler.php index abcba483..9258f8c9 100644 --- a/src/Handler/Settings/DeleteVideoThumbnailHandler.php +++ b/src/Handler/Settings/DeleteVideoThumbnail/DeleteVideoThumbnailHandler.php @@ -15,16 +15,17 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteVideoThumbnail; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteVideoThumbnail\DeleteVideoThumbnailPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Exception\ConfigWriteException; final class DeleteVideoThumbnailHandler { - public function __invoke(string $name): void + public function __invoke(DeleteVideoThumbnailPayload $payload): void { - $pipe = Asset\Video\Thumbnail\Config::getByName($name); + $pipe = Asset\Video\Thumbnail\Config::getByName($payload->name); if (!$pipe->isWriteable()) { throw new ConfigWriteException(); diff --git a/src/Handler/Settings/DeleteVideoThumbnail/DeleteVideoThumbnailPayload.php b/src/Handler/Settings/DeleteVideoThumbnail/DeleteVideoThumbnailPayload.php new file mode 100644 index 00000000..4801aa47 --- /dev/null +++ b/src/Handler/Settings/DeleteVideoThumbnail/DeleteVideoThumbnailPayload.php @@ -0,0 +1,34 @@ +request->getString('name'), + ); + } +} diff --git a/src/Handler/Settings/DeleteWebsiteSettingHandler.php b/src/Handler/Settings/DeleteWebsiteSetting/DeleteWebsiteSettingHandler.php similarity index 85% rename from src/Handler/Settings/DeleteWebsiteSettingHandler.php rename to src/Handler/Settings/DeleteWebsiteSetting/DeleteWebsiteSettingHandler.php index fd204112..4e80f046 100644 --- a/src/Handler/Settings/DeleteWebsiteSettingHandler.php +++ b/src/Handler/Settings/DeleteWebsiteSetting/DeleteWebsiteSettingHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\DeleteWebsiteSetting; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\WebsiteSettingPayload; use OpenDxp\Model\WebsiteSetting; final class DeleteWebsiteSettingHandler diff --git a/src/Handler/Settings/DisplayCustomLogoHandler.php b/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoHandler.php similarity index 94% rename from src/Handler/Settings/DisplayCustomLogoHandler.php rename to src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoHandler.php index a6d08234..a2b62c9e 100644 --- a/src/Handler/Settings/DisplayCustomLogoHandler.php +++ b/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\DisplayCustomLogo; use Exception; use OpenDxp\Tool; diff --git a/src/Handler/Settings/DisplayCustomLogoResult.php b/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoResult.php similarity index 90% rename from src/Handler/Settings/DisplayCustomLogoResult.php rename to src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoResult.php index acfae3ce..438021e2 100644 --- a/src/Handler/Settings/DisplayCustomLogoResult.php +++ b/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\DisplayCustomLogo; final class DisplayCustomLogoResult { diff --git a/src/Handler/Settings/GetAppearanceSettingsHandler.php b/src/Handler/Settings/GetAppearanceSettings/GetAppearanceSettingsHandler.php similarity index 91% rename from src/Handler/Settings/GetAppearanceSettingsHandler.php rename to src/Handler/Settings/GetAppearanceSettings/GetAppearanceSettingsHandler.php index 50f1d323..e20b4d9c 100644 --- a/src/Handler/Settings/GetAppearanceSettingsHandler.php +++ b/src/Handler/Settings/GetAppearanceSettings/GetAppearanceSettingsHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAppearanceSettings; use OpenDxp\Bundle\AdminBundle\System\AdminConfig; diff --git a/src/Handler/Settings/GetAppearanceSettingsResult.php b/src/Handler/Settings/GetAppearanceSettings/GetAppearanceSettingsResult.php similarity index 88% rename from src/Handler/Settings/GetAppearanceSettingsResult.php rename to src/Handler/Settings/GetAppearanceSettings/GetAppearanceSettingsResult.php index ed3a3905..a762b823 100644 --- a/src/Handler/Settings/GetAppearanceSettingsResult.php +++ b/src/Handler/Settings/GetAppearanceSettings/GetAppearanceSettingsResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAppearanceSettings; final class GetAppearanceSettingsResult { diff --git a/src/Handler/Settings/GetAvailableAdminLanguagesHandler.php b/src/Handler/Settings/GetAvailableAdminLanguages/GetAvailableAdminLanguagesHandler.php similarity index 93% rename from src/Handler/Settings/GetAvailableAdminLanguagesHandler.php rename to src/Handler/Settings/GetAvailableAdminLanguages/GetAvailableAdminLanguagesHandler.php index 48936e18..a8818df7 100644 --- a/src/Handler/Settings/GetAvailableAdminLanguagesHandler.php +++ b/src/Handler/Settings/GetAvailableAdminLanguages/GetAvailableAdminLanguagesHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAdminLanguages; use OpenDxp\Tool; diff --git a/src/Handler/Settings/GetAvailableAdminLanguagesResult.php b/src/Handler/Settings/GetAvailableAdminLanguages/GetAvailableAdminLanguagesResult.php similarity index 88% rename from src/Handler/Settings/GetAvailableAdminLanguagesResult.php rename to src/Handler/Settings/GetAvailableAdminLanguages/GetAvailableAdminLanguagesResult.php index bdec0cb8..c986ea43 100644 --- a/src/Handler/Settings/GetAvailableAdminLanguagesResult.php +++ b/src/Handler/Settings/GetAvailableAdminLanguages/GetAvailableAdminLanguagesResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAdminLanguages; final class GetAvailableAdminLanguagesResult { diff --git a/src/Handler/Settings/GetAvailableAlgorithmsHandler.php b/src/Handler/Settings/GetAvailableAlgorithms/GetAvailableAlgorithmsHandler.php similarity index 93% rename from src/Handler/Settings/GetAvailableAlgorithmsHandler.php rename to src/Handler/Settings/GetAvailableAlgorithms/GetAvailableAlgorithmsHandler.php index 9c2bf00c..7c6e63e8 100644 --- a/src/Handler/Settings/GetAvailableAlgorithmsHandler.php +++ b/src/Handler/Settings/GetAvailableAlgorithms/GetAvailableAlgorithmsHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAlgorithms; use Symfony\Contracts\Translation\TranslatorInterface; diff --git a/src/Handler/Settings/GetAvailableAlgorithmsResult.php b/src/Handler/Settings/GetAvailableAlgorithms/GetAvailableAlgorithmsResult.php similarity index 88% rename from src/Handler/Settings/GetAvailableAlgorithmsResult.php rename to src/Handler/Settings/GetAvailableAlgorithms/GetAvailableAlgorithmsResult.php index 06b49a50..849e6f10 100644 --- a/src/Handler/Settings/GetAvailableAlgorithmsResult.php +++ b/src/Handler/Settings/GetAvailableAlgorithms/GetAvailableAlgorithmsResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAlgorithms; final class GetAvailableAlgorithmsResult { diff --git a/src/Handler/Settings/GetAvailableCountriesHandler.php b/src/Handler/Settings/GetAvailableCountries/GetAvailableCountriesHandler.php similarity index 93% rename from src/Handler/Settings/GetAvailableCountriesHandler.php rename to src/Handler/Settings/GetAvailableCountries/GetAvailableCountriesHandler.php index 50e35300..52322d84 100644 --- a/src/Handler/Settings/GetAvailableCountriesHandler.php +++ b/src/Handler/Settings/GetAvailableCountries/GetAvailableCountriesHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableCountries; use OpenDxp\Localization\LocaleServiceInterface; diff --git a/src/Handler/Settings/GetAvailableCountriesResult.php b/src/Handler/Settings/GetAvailableCountries/GetAvailableCountriesResult.php similarity index 88% rename from src/Handler/Settings/GetAvailableCountriesResult.php rename to src/Handler/Settings/GetAvailableCountries/GetAvailableCountriesResult.php index c3042288..f086832d 100644 --- a/src/Handler/Settings/GetAvailableCountriesResult.php +++ b/src/Handler/Settings/GetAvailableCountries/GetAvailableCountriesResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableCountries; final class GetAvailableCountriesResult { diff --git a/src/Handler/Settings/GetAvailableSitesHandler.php b/src/Handler/Settings/GetAvailableSites/GetAvailableSitesHandler.php similarity index 96% rename from src/Handler/Settings/GetAvailableSitesHandler.php rename to src/Handler/Settings/GetAvailableSites/GetAvailableSitesHandler.php index 40921bb9..962a372a 100644 --- a/src/Handler/Settings/GetAvailableSitesHandler.php +++ b/src/Handler/Settings/GetAvailableSites/GetAvailableSitesHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableSites; use OpenDxp\Model; use Symfony\Contracts\Translation\TranslatorInterface; diff --git a/src/Handler/Settings/GetAvailableSitesResult.php b/src/Handler/Settings/GetAvailableSites/GetAvailableSitesResult.php similarity index 89% rename from src/Handler/Settings/GetAvailableSitesResult.php rename to src/Handler/Settings/GetAvailableSites/GetAvailableSitesResult.php index fef3e4fb..cb01b61c 100644 --- a/src/Handler/Settings/GetAvailableSitesResult.php +++ b/src/Handler/Settings/GetAvailableSites/GetAvailableSitesResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableSites; final class GetAvailableSitesResult { diff --git a/src/Handler/Settings/GetDownloadableThumbnailsHandler.php b/src/Handler/Settings/GetDownloadableThumbnails/GetDownloadableThumbnailsHandler.php similarity index 93% rename from src/Handler/Settings/GetDownloadableThumbnailsHandler.php rename to src/Handler/Settings/GetDownloadableThumbnails/GetDownloadableThumbnailsHandler.php index b0230a3f..7bc8dea4 100644 --- a/src/Handler/Settings/GetDownloadableThumbnailsHandler.php +++ b/src/Handler/Settings/GetDownloadableThumbnails/GetDownloadableThumbnailsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetDownloadableThumbnails; use OpenDxp\Model\Asset; diff --git a/src/Handler/Settings/GetDownloadableThumbnailsResult.php b/src/Handler/Settings/GetDownloadableThumbnails/GetDownloadableThumbnailsResult.php similarity index 88% rename from src/Handler/Settings/GetDownloadableThumbnailsResult.php rename to src/Handler/Settings/GetDownloadableThumbnails/GetDownloadableThumbnailsResult.php index 5740215b..596fc7c0 100644 --- a/src/Handler/Settings/GetDownloadableThumbnailsResult.php +++ b/src/Handler/Settings/GetDownloadableThumbnails/GetDownloadableThumbnailsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetDownloadableThumbnails; final readonly class GetDownloadableThumbnailsResult { diff --git a/src/Handler/Settings/GetFilteredPredefinedMetadataHandler.php b/src/Handler/Settings/GetFilteredPredefinedMetadata/GetFilteredPredefinedMetadataHandler.php similarity index 93% rename from src/Handler/Settings/GetFilteredPredefinedMetadataHandler.php rename to src/Handler/Settings/GetFilteredPredefinedMetadata/GetFilteredPredefinedMetadataHandler.php index 56c5bf81..37d453fa 100644 --- a/src/Handler/Settings/GetFilteredPredefinedMetadataHandler.php +++ b/src/Handler/Settings/GetFilteredPredefinedMetadata/GetFilteredPredefinedMetadataHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetFilteredPredefinedMetadata; use OpenDxp\Model\Metadata; diff --git a/src/Handler/Settings/GetFilteredPredefinedMetadataResult.php b/src/Handler/Settings/GetFilteredPredefinedMetadata/GetFilteredPredefinedMetadataResult.php similarity index 88% rename from src/Handler/Settings/GetFilteredPredefinedMetadataResult.php rename to src/Handler/Settings/GetFilteredPredefinedMetadata/GetFilteredPredefinedMetadataResult.php index 19bcc2fc..b2bf13ff 100644 --- a/src/Handler/Settings/GetFilteredPredefinedMetadataResult.php +++ b/src/Handler/Settings/GetFilteredPredefinedMetadata/GetFilteredPredefinedMetadataResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetFilteredPredefinedMetadata; final class GetFilteredPredefinedMetadataResult { diff --git a/src/Handler/Settings/GetPredefinedMetadataListHandler.php b/src/Handler/Settings/GetPredefinedMetadataList/GetPredefinedMetadataListHandler.php similarity index 90% rename from src/Handler/Settings/GetPredefinedMetadataListHandler.php rename to src/Handler/Settings/GetPredefinedMetadataList/GetPredefinedMetadataListHandler.php index f36c36df..748c7f99 100644 --- a/src/Handler/Settings/GetPredefinedMetadataListHandler.php +++ b/src/Handler/Settings/GetPredefinedMetadataList/GetPredefinedMetadataListHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetPredefinedMetadataList; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedMetadataPayload; use OpenDxp\Model\Metadata; final class GetPredefinedMetadataListHandler diff --git a/src/Handler/Settings/GetPredefinedMetadataListResult.php b/src/Handler/Settings/GetPredefinedMetadataList/GetPredefinedMetadataListResult.php similarity index 89% rename from src/Handler/Settings/GetPredefinedMetadataListResult.php rename to src/Handler/Settings/GetPredefinedMetadataList/GetPredefinedMetadataListResult.php index c5176613..21563615 100644 --- a/src/Handler/Settings/GetPredefinedMetadataListResult.php +++ b/src/Handler/Settings/GetPredefinedMetadataList/GetPredefinedMetadataListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetPredefinedMetadataList; final readonly class GetPredefinedMetadataListResult { diff --git a/src/Handler/Settings/GetPredefinedPropertiesListHandler.php b/src/Handler/Settings/GetPredefinedPropertiesList/GetPredefinedPropertiesListHandler.php similarity index 91% rename from src/Handler/Settings/GetPredefinedPropertiesListHandler.php rename to src/Handler/Settings/GetPredefinedPropertiesList/GetPredefinedPropertiesListHandler.php index d80e2650..29b3a607 100644 --- a/src/Handler/Settings/GetPredefinedPropertiesListHandler.php +++ b/src/Handler/Settings/GetPredefinedPropertiesList/GetPredefinedPropertiesListHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetPredefinedPropertiesList; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedPropertyPayload; use OpenDxp\Model\Property; final class GetPredefinedPropertiesListHandler diff --git a/src/Handler/Settings/GetPredefinedPropertiesListResult.php b/src/Handler/Settings/GetPredefinedPropertiesList/GetPredefinedPropertiesListResult.php similarity index 88% rename from src/Handler/Settings/GetPredefinedPropertiesListResult.php rename to src/Handler/Settings/GetPredefinedPropertiesList/GetPredefinedPropertiesListResult.php index c7e5cd2a..d04f2024 100644 --- a/src/Handler/Settings/GetPredefinedPropertiesListResult.php +++ b/src/Handler/Settings/GetPredefinedPropertiesList/GetPredefinedPropertiesListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetPredefinedPropertiesList; final readonly class GetPredefinedPropertiesListResult { diff --git a/src/Handler/Settings/GetSystemSettingsHandler.php b/src/Handler/Settings/GetSystemSettings/GetSystemSettingsHandler.php similarity index 96% rename from src/Handler/Settings/GetSystemSettingsHandler.php rename to src/Handler/Settings/GetSystemSettings/GetSystemSettingsHandler.php index 411599d8..9fda1541 100644 --- a/src/Handler/Settings/GetSystemSettingsHandler.php +++ b/src/Handler/Settings/GetSystemSettings/GetSystemSettingsHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetSystemSettings; use OpenDxp\SystemSettingsConfig; use OpenDxp\Tool; diff --git a/src/Handler/Settings/GetSystemSettingsResult.php b/src/Handler/Settings/GetSystemSettings/GetSystemSettingsResult.php similarity index 90% rename from src/Handler/Settings/GetSystemSettingsResult.php rename to src/Handler/Settings/GetSystemSettings/GetSystemSettingsResult.php index 6757e31c..aa0370ba 100644 --- a/src/Handler/Settings/GetSystemSettingsResult.php +++ b/src/Handler/Settings/GetSystemSettings/GetSystemSettingsResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetSystemSettings; final class GetSystemSettingsResult { diff --git a/src/Handler/Settings/GetThumbnailHandler.php b/src/Handler/Settings/GetThumbnail/GetThumbnailHandler.php similarity index 92% rename from src/Handler/Settings/GetThumbnailHandler.php rename to src/Handler/Settings/GetThumbnail/GetThumbnailHandler.php index c6c2483e..27347182 100644 --- a/src/Handler/Settings/GetThumbnailHandler.php +++ b/src/Handler/Settings/GetThumbnail/GetThumbnailHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetThumbnail; use OpenDxp\Model\Asset; diff --git a/src/Handler/Settings/GetThumbnailResult.php b/src/Handler/Settings/GetThumbnail/GetThumbnailResult.php similarity index 90% rename from src/Handler/Settings/GetThumbnailResult.php rename to src/Handler/Settings/GetThumbnail/GetThumbnailResult.php index 21e9eea0..ee00700f 100644 --- a/src/Handler/Settings/GetThumbnailResult.php +++ b/src/Handler/Settings/GetThumbnail/GetThumbnailResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetThumbnail; final readonly class GetThumbnailResult { diff --git a/src/Handler/Settings/GetThumbnailTreeHandler.php b/src/Handler/Settings/GetThumbnailTree/GetThumbnailTreeHandler.php similarity index 97% rename from src/Handler/Settings/GetThumbnailTreeHandler.php rename to src/Handler/Settings/GetThumbnailTree/GetThumbnailTreeHandler.php index 22d67ddb..2b7fae83 100644 --- a/src/Handler/Settings/GetThumbnailTreeHandler.php +++ b/src/Handler/Settings/GetThumbnailTree/GetThumbnailTreeHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetThumbnailTree; use OpenDxp\Model\Asset; diff --git a/src/Handler/Settings/GetThumbnailTreeResult.php b/src/Handler/Settings/GetThumbnailTree/GetThumbnailTreeResult.php similarity index 89% rename from src/Handler/Settings/GetThumbnailTreeResult.php rename to src/Handler/Settings/GetThumbnailTree/GetThumbnailTreeResult.php index df8eaadc..2939197c 100644 --- a/src/Handler/Settings/GetThumbnailTreeResult.php +++ b/src/Handler/Settings/GetThumbnailTree/GetThumbnailTreeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetThumbnailTree; final readonly class GetThumbnailTreeResult { diff --git a/src/Handler/Settings/GetVideoThumbnailHandler.php b/src/Handler/Settings/GetVideoThumbnail/GetVideoThumbnailHandler.php similarity index 92% rename from src/Handler/Settings/GetVideoThumbnailHandler.php rename to src/Handler/Settings/GetVideoThumbnail/GetVideoThumbnailHandler.php index 23e5b86f..083c1b1c 100644 --- a/src/Handler/Settings/GetVideoThumbnailHandler.php +++ b/src/Handler/Settings/GetVideoThumbnail/GetVideoThumbnailHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnail; use OpenDxp\Model\Asset; diff --git a/src/Handler/Settings/GetVideoThumbnailResult.php b/src/Handler/Settings/GetVideoThumbnail/GetVideoThumbnailResult.php similarity index 89% rename from src/Handler/Settings/GetVideoThumbnailResult.php rename to src/Handler/Settings/GetVideoThumbnail/GetVideoThumbnailResult.php index 1fd2682c..8e43dec1 100644 --- a/src/Handler/Settings/GetVideoThumbnailResult.php +++ b/src/Handler/Settings/GetVideoThumbnail/GetVideoThumbnailResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnail; final readonly class GetVideoThumbnailResult { diff --git a/src/Handler/Settings/GetVideoThumbnailListHandler.php b/src/Handler/Settings/GetVideoThumbnailList/GetVideoThumbnailListHandler.php similarity index 93% rename from src/Handler/Settings/GetVideoThumbnailListHandler.php rename to src/Handler/Settings/GetVideoThumbnailList/GetVideoThumbnailListHandler.php index 8ac1cb0f..cac95294 100644 --- a/src/Handler/Settings/GetVideoThumbnailListHandler.php +++ b/src/Handler/Settings/GetVideoThumbnailList/GetVideoThumbnailListHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailList; use OpenDxp\Model\Asset; diff --git a/src/Handler/Settings/GetVideoThumbnailListResult.php b/src/Handler/Settings/GetVideoThumbnailList/GetVideoThumbnailListResult.php similarity index 89% rename from src/Handler/Settings/GetVideoThumbnailListResult.php rename to src/Handler/Settings/GetVideoThumbnailList/GetVideoThumbnailListResult.php index 47d6a81e..27bbe5e6 100644 --- a/src/Handler/Settings/GetVideoThumbnailListResult.php +++ b/src/Handler/Settings/GetVideoThumbnailList/GetVideoThumbnailListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailList; final readonly class GetVideoThumbnailListResult { diff --git a/src/Handler/Settings/GetVideoThumbnailTreeHandler.php b/src/Handler/Settings/GetVideoThumbnailTree/GetVideoThumbnailTreeHandler.php similarity index 96% rename from src/Handler/Settings/GetVideoThumbnailTreeHandler.php rename to src/Handler/Settings/GetVideoThumbnailTree/GetVideoThumbnailTreeHandler.php index 11fed3eb..f4e513a5 100644 --- a/src/Handler/Settings/GetVideoThumbnailTreeHandler.php +++ b/src/Handler/Settings/GetVideoThumbnailTree/GetVideoThumbnailTreeHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailTree; use OpenDxp\Model\Asset; diff --git a/src/Handler/Settings/GetVideoThumbnailTreeResult.php b/src/Handler/Settings/GetVideoThumbnailTree/GetVideoThumbnailTreeResult.php similarity index 89% rename from src/Handler/Settings/GetVideoThumbnailTreeResult.php rename to src/Handler/Settings/GetVideoThumbnailTree/GetVideoThumbnailTreeResult.php index 311cf058..aac52476 100644 --- a/src/Handler/Settings/GetVideoThumbnailTreeResult.php +++ b/src/Handler/Settings/GetVideoThumbnailTree/GetVideoThumbnailTreeResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetVideoThumbnailTree; final readonly class GetVideoThumbnailTreeResult { diff --git a/src/Handler/Settings/GetWebsiteSettingsListHandler.php b/src/Handler/Settings/GetWebsiteSettingsList/GetWebsiteSettingsListHandler.php similarity index 94% rename from src/Handler/Settings/GetWebsiteSettingsListHandler.php rename to src/Handler/Settings/GetWebsiteSettingsList/GetWebsiteSettingsListHandler.php index e3065bd8..3d4616bd 100644 --- a/src/Handler/Settings/GetWebsiteSettingsListHandler.php +++ b/src/Handler/Settings/GetWebsiteSettingsList/GetWebsiteSettingsListHandler.php @@ -15,9 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetWebsiteSettingsList; -use OpenDxp\Model\Element; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\WebsiteSettingPayload; use OpenDxp\Model\WebsiteSetting; final class GetWebsiteSettingsListHandler diff --git a/src/Handler/Settings/GetWebsiteSettingsListResult.php b/src/Handler/Settings/GetWebsiteSettingsList/GetWebsiteSettingsListResult.php similarity index 89% rename from src/Handler/Settings/GetWebsiteSettingsListResult.php rename to src/Handler/Settings/GetWebsiteSettingsList/GetWebsiteSettingsListResult.php index 08a4f567..35fe1c88 100644 --- a/src/Handler/Settings/GetWebsiteSettingsListResult.php +++ b/src/Handler/Settings/GetWebsiteSettingsList/GetWebsiteSettingsListResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\GetWebsiteSettingsList; final readonly class GetWebsiteSettingsListResult { diff --git a/src/Handler/Settings/SaveAppearanceSettingsHandler.php b/src/Handler/Settings/SaveAppearanceSettings/SaveAppearanceSettingsHandler.php similarity index 59% rename from src/Handler/Settings/SaveAppearanceSettingsHandler.php rename to src/Handler/Settings/SaveAppearanceSettings/SaveAppearanceSettingsHandler.php index d8d25c56..4c41cb1a 100644 --- a/src/Handler/Settings/SaveAppearanceSettingsHandler.php +++ b/src/Handler/Settings/SaveAppearanceSettings/SaveAppearanceSettingsHandler.php @@ -15,13 +15,17 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveAppearanceSettings; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSettingsPayload; use OpenDxp\Bundle\AdminBundle\System\AdminConfig; use OpenDxp\Helper\StopMessengerWorkersTrait; +use OpenDxp\Service\Cache\OpenDxpCacheClearingService; +use OpenDxp\Service\Cache\SymfonyCacheClearingService; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\Event\TerminateEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\KernelInterface; final class SaveAppearanceSettingsHandler { @@ -29,22 +33,25 @@ final class SaveAppearanceSettingsHandler public function __construct( private readonly AdminConfig $config, - private readonly ClearSymfonyCacheHandler $clearSymfonyCache, - private readonly ClearOpenDxpCacheHandler $clearOpenDxpCache, + private readonly SymfonyCacheClearingService $symfonyCache, + private readonly OpenDxpCacheClearingService $openDxpCache, private readonly EventDispatcherInterface $eventDispatcher, + private readonly KernelInterface $kernel, ) {} - public function __invoke(array $values, string $env): void + public function __invoke(SaveSettingsPayload $payload): void { - $this->config->save($values); - ($this->clearSymfonyCache)($env); + $env = $payload->env ?: $this->kernel->getEnvironment(); + + $this->config->save($payload->values); + $this->symfonyCache->clear($env); $this->stopMessengerWorkers(); - $clearOpenDxpCache = $this->clearOpenDxpCache; - $this->eventDispatcher->addListener(KernelEvents::TERMINATE, static function (TerminateEvent $event) use ($clearOpenDxpCache): void { + $openDxpCache = $this->openDxpCache; + $this->eventDispatcher->addListener(KernelEvents::TERMINATE, static function (TerminateEvent $event) use ($openDxpCache): void { // delay to ensure messenger:stop-workers signal has been processed before cache is cleared sleep(2); - $clearOpenDxpCache(); + $openDxpCache->clear(); }); } } diff --git a/src/Handler/Settings/SaveSettingsPayload.php b/src/Handler/Settings/SaveSettingsPayload.php new file mode 100644 index 00000000..62fdb50e --- /dev/null +++ b/src/Handler/Settings/SaveSettingsPayload.php @@ -0,0 +1,36 @@ +request->getString('data'), true), + env: $request->request->getString('env'), + ); + } +} diff --git a/src/Handler/Settings/SaveSystemSettingsHandler.php b/src/Handler/Settings/SaveSystemSettings/SaveSystemSettingsHandler.php similarity index 59% rename from src/Handler/Settings/SaveSystemSettingsHandler.php rename to src/Handler/Settings/SaveSystemSettings/SaveSystemSettingsHandler.php index cfe167f8..49f5bd64 100644 --- a/src/Handler/Settings/SaveSystemSettingsHandler.php +++ b/src/Handler/Settings/SaveSystemSettings/SaveSystemSettingsHandler.php @@ -15,13 +15,17 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSystemSettings; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSettingsPayload; use OpenDxp\Helper\StopMessengerWorkersTrait; +use OpenDxp\Service\Cache\OpenDxpCacheClearingService; +use OpenDxp\Service\Cache\SymfonyCacheClearingService; use OpenDxp\SystemSettingsConfig; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\Event\TerminateEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\KernelInterface; final class SaveSystemSettingsHandler { @@ -29,22 +33,25 @@ final class SaveSystemSettingsHandler public function __construct( private readonly SystemSettingsConfig $config, - private readonly ClearSymfonyCacheHandler $clearSymfonyCache, - private readonly ClearOpenDxpCacheHandler $clearOpenDxpCache, + private readonly SymfonyCacheClearingService $symfonyCache, + private readonly OpenDxpCacheClearingService $openDxpCache, private readonly EventDispatcherInterface $eventDispatcher, + private readonly KernelInterface $kernel, ) {} - public function __invoke(array $values, string $env): void + public function __invoke(SaveSettingsPayload $payload): void { - $this->config->save($values); - ($this->clearSymfonyCache)($env); + $env = $payload->env ?: $this->kernel->getEnvironment(); + + $this->config->save($payload->values); + $this->symfonyCache->clear($env); $this->stopMessengerWorkers(); - $clearOpenDxpCache = $this->clearOpenDxpCache; - $this->eventDispatcher->addListener(KernelEvents::TERMINATE, static function (TerminateEvent $event) use ($clearOpenDxpCache): void { + $openDxpCache = $this->openDxpCache; + $this->eventDispatcher->addListener(KernelEvents::TERMINATE, static function (TerminateEvent $event) use ($openDxpCache): void { // delay to ensure messenger:stop-workers signal has been processed before cache is cleared sleep(2); - $clearOpenDxpCache(); + $openDxpCache->clear(); }); } } diff --git a/src/Handler/Settings/ThumbnailAdapterCheckHandler.php b/src/Handler/Settings/ThumbnailAdapterCheck/ThumbnailAdapterCheckHandler.php similarity index 94% rename from src/Handler/Settings/ThumbnailAdapterCheckHandler.php rename to src/Handler/Settings/ThumbnailAdapterCheck/ThumbnailAdapterCheckHandler.php index 039fa46f..ae7c769b 100644 --- a/src/Handler/Settings/ThumbnailAdapterCheckHandler.php +++ b/src/Handler/Settings/ThumbnailAdapterCheck/ThumbnailAdapterCheckHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\ThumbnailAdapterCheck; use OpenDxp\Image; use OpenDxp\Image\Adapter\GD; diff --git a/src/Handler/Settings/ThumbnailAdapterCheckResult.php b/src/Handler/Settings/ThumbnailAdapterCheck/ThumbnailAdapterCheckResult.php similarity index 88% rename from src/Handler/Settings/ThumbnailAdapterCheckResult.php rename to src/Handler/Settings/ThumbnailAdapterCheck/ThumbnailAdapterCheckResult.php index a8553998..42b55b3b 100644 --- a/src/Handler/Settings/ThumbnailAdapterCheckResult.php +++ b/src/Handler/Settings/ThumbnailAdapterCheck/ThumbnailAdapterCheckResult.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\ThumbnailAdapterCheck; final class ThumbnailAdapterCheckResult { diff --git a/src/Handler/Settings/UpdatePredefinedMetadataHandler.php b/src/Handler/Settings/UpdatePredefinedMetadata/UpdatePredefinedMetadataHandler.php similarity index 91% rename from src/Handler/Settings/UpdatePredefinedMetadataHandler.php rename to src/Handler/Settings/UpdatePredefinedMetadata/UpdatePredefinedMetadataHandler.php index 07957edb..5cf37cc5 100644 --- a/src/Handler/Settings/UpdatePredefinedMetadataHandler.php +++ b/src/Handler/Settings/UpdatePredefinedMetadata/UpdatePredefinedMetadataHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedMetadata; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedMetadataPayload; use OpenDxp\Model\Exception\ConfigWriteException; use OpenDxp\Model\Metadata; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; diff --git a/src/Handler/Settings/UpdatePredefinedMetadataResult.php b/src/Handler/Settings/UpdatePredefinedMetadata/UpdatePredefinedMetadataResult.php similarity index 88% rename from src/Handler/Settings/UpdatePredefinedMetadataResult.php rename to src/Handler/Settings/UpdatePredefinedMetadata/UpdatePredefinedMetadataResult.php index 2735f8df..3389653a 100644 --- a/src/Handler/Settings/UpdatePredefinedMetadataResult.php +++ b/src/Handler/Settings/UpdatePredefinedMetadata/UpdatePredefinedMetadataResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedMetadata; final readonly class UpdatePredefinedMetadataResult { diff --git a/src/Handler/Settings/UpdatePredefinedPropertyHandler.php b/src/Handler/Settings/UpdatePredefinedProperty/UpdatePredefinedPropertyHandler.php similarity index 89% rename from src/Handler/Settings/UpdatePredefinedPropertyHandler.php rename to src/Handler/Settings/UpdatePredefinedProperty/UpdatePredefinedPropertyHandler.php index 923a6a36..18eec4e3 100644 --- a/src/Handler/Settings/UpdatePredefinedPropertyHandler.php +++ b/src/Handler/Settings/UpdatePredefinedProperty/UpdatePredefinedPropertyHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedProperty; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedPropertyPayload; use OpenDxp\Model\Exception\ConfigWriteException; use OpenDxp\Model\Property; diff --git a/src/Handler/Settings/UpdatePredefinedPropertyResult.php b/src/Handler/Settings/UpdatePredefinedProperty/UpdatePredefinedPropertyResult.php similarity index 88% rename from src/Handler/Settings/UpdatePredefinedPropertyResult.php rename to src/Handler/Settings/UpdatePredefinedProperty/UpdatePredefinedPropertyResult.php index 30c3f200..6307598e 100644 --- a/src/Handler/Settings/UpdatePredefinedPropertyResult.php +++ b/src/Handler/Settings/UpdatePredefinedProperty/UpdatePredefinedPropertyResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdatePredefinedProperty; final readonly class UpdatePredefinedPropertyResult { diff --git a/src/Handler/Settings/UpdateThumbnailHandler.php b/src/Handler/Settings/UpdateThumbnail/UpdateThumbnailHandler.php similarity index 77% rename from src/Handler/Settings/UpdateThumbnailHandler.php rename to src/Handler/Settings/UpdateThumbnail/UpdateThumbnailHandler.php index 5f0dd0dc..be85d2ae 100644 --- a/src/Handler/Settings/UpdateThumbnailHandler.php +++ b/src/Handler/Settings/UpdateThumbnail/UpdateThumbnailHandler.php @@ -15,27 +15,24 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateThumbnail; use Exception; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateThumbnail\UpdateThumbnailPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Exception\ConfigWriteException; final class UpdateThumbnailHandler { - public function __invoke( - string $name, - array $settingsData, - array $mediaData, - array $mediaOrder, - ): void { - $pipe = Asset\Image\Thumbnail\Config::getByName($name); + public function __invoke(UpdateThumbnailPayload $payload): void + { + $pipe = Asset\Image\Thumbnail\Config::getByName($payload->name); if (!$pipe->isWriteable()) { throw new ConfigWriteException(); } - foreach ($settingsData as $key => $value) { + foreach ($payload->settingsData as $key => $value) { $setter = 'set' . ucfirst($key); if (method_exists($pipe, $setter)) { $pipe->$setter($value); @@ -44,6 +41,9 @@ public function __invoke( $pipe->resetItems(); + $mediaData = $payload->mediaData; + $mediaOrder = $payload->mediaOrder; + uksort($mediaData, static function ($a, $b) use ($mediaOrder) { if ($a === 'default') { return -1; diff --git a/src/Handler/Settings/UpdateThumbnail/UpdateThumbnailPayload.php b/src/Handler/Settings/UpdateThumbnail/UpdateThumbnailPayload.php new file mode 100644 index 00000000..5f93a53d --- /dev/null +++ b/src/Handler/Settings/UpdateThumbnail/UpdateThumbnailPayload.php @@ -0,0 +1,40 @@ +request->getString('name'), + settingsData: json_decode($request->request->getString('settings'), true), + mediaData: json_decode($request->request->getString('medias'), true), + mediaOrder: json_decode($request->request->getString('mediaOrder'), true), + ); + } +} diff --git a/src/Handler/Settings/UpdateVideoThumbnailHandler.php b/src/Handler/Settings/UpdateVideoThumbnail/UpdateVideoThumbnailHandler.php similarity index 75% rename from src/Handler/Settings/UpdateVideoThumbnailHandler.php rename to src/Handler/Settings/UpdateVideoThumbnail/UpdateVideoThumbnailHandler.php index cdd1892c..84fe5f56 100644 --- a/src/Handler/Settings/UpdateVideoThumbnailHandler.php +++ b/src/Handler/Settings/UpdateVideoThumbnail/UpdateVideoThumbnailHandler.php @@ -15,26 +15,23 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateVideoThumbnail; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateVideoThumbnail\UpdateVideoThumbnailPayload; use OpenDxp\Model\Asset; use OpenDxp\Model\Exception\ConfigWriteException; final class UpdateVideoThumbnailHandler { - public function __invoke( - string $name, - array $settingsData, - array $mediaData, - array $mediaOrder, - ): void { - $pipe = Asset\Video\Thumbnail\Config::getByName($name); + public function __invoke(UpdateVideoThumbnailPayload $payload): void + { + $pipe = Asset\Video\Thumbnail\Config::getByName($payload->name); if (!$pipe->isWriteable()) { throw new ConfigWriteException(); } - foreach ($settingsData as $key => $value) { + foreach ($payload->settingsData as $key => $value) { $setter = 'set' . ucfirst($key); if (method_exists($pipe, $setter)) { $pipe->$setter($value); @@ -43,6 +40,9 @@ public function __invoke( $pipe->resetItems(); + $mediaData = $payload->mediaData; + $mediaOrder = $payload->mediaOrder; + uksort($mediaData, static function ($a, $b) use ($mediaOrder) { if ($a === 'default') { return -1; diff --git a/src/Handler/Settings/UpdateVideoThumbnail/UpdateVideoThumbnailPayload.php b/src/Handler/Settings/UpdateVideoThumbnail/UpdateVideoThumbnailPayload.php new file mode 100644 index 00000000..77192452 --- /dev/null +++ b/src/Handler/Settings/UpdateVideoThumbnail/UpdateVideoThumbnailPayload.php @@ -0,0 +1,40 @@ +request->getString('name'), + settingsData: json_decode($request->request->getString('settings'), true), + mediaData: json_decode($request->request->getString('medias'), true), + mediaOrder: json_decode($request->request->getString('mediaOrder'), true), + ); + } +} diff --git a/src/Handler/Settings/UpdateWebsiteSettingHandler.php b/src/Handler/Settings/UpdateWebsiteSetting/UpdateWebsiteSettingHandler.php similarity index 94% rename from src/Handler/Settings/UpdateWebsiteSettingHandler.php rename to src/Handler/Settings/UpdateWebsiteSetting/UpdateWebsiteSettingHandler.php index a4faa651..5ad28d72 100644 --- a/src/Handler/Settings/UpdateWebsiteSettingHandler.php +++ b/src/Handler/Settings/UpdateWebsiteSetting/UpdateWebsiteSettingHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateWebsiteSetting; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\WebsiteSettingPayload; use OpenDxp\Model\Element; use OpenDxp\Model\WebsiteSetting; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/src/Handler/Settings/UpdateWebsiteSettingResult.php b/src/Handler/Settings/UpdateWebsiteSetting/UpdateWebsiteSettingResult.php similarity index 89% rename from src/Handler/Settings/UpdateWebsiteSettingResult.php rename to src/Handler/Settings/UpdateWebsiteSetting/UpdateWebsiteSettingResult.php index 9a681aca..accbad6c 100644 --- a/src/Handler/Settings/UpdateWebsiteSettingResult.php +++ b/src/Handler/Settings/UpdateWebsiteSetting/UpdateWebsiteSettingResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UpdateWebsiteSetting; final readonly class UpdateWebsiteSettingResult { diff --git a/src/Handler/Settings/UploadCustomLogoHandler.php b/src/Handler/Settings/UploadCustomLogo/UploadCustomLogoHandler.php similarity index 93% rename from src/Handler/Settings/UploadCustomLogoHandler.php rename to src/Handler/Settings/UploadCustomLogo/UploadCustomLogoHandler.php index fa874425..5576c379 100644 --- a/src/Handler/Settings/UploadCustomLogoHandler.php +++ b/src/Handler/Settings/UploadCustomLogo/UploadCustomLogoHandler.php @@ -15,7 +15,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\UploadCustomLogo; use Exception; use OpenDxp\Tool; diff --git a/src/Handler/Tags/AddTagHandler.php b/src/Handler/Tags/AddTag/AddTagHandler.php similarity index 76% rename from src/Handler/Tags/AddTagHandler.php rename to src/Handler/Tags/AddTag/AddTagHandler.php index 37f1322f..384fcca8 100644 --- a/src/Handler/Tags/AddTagHandler.php +++ b/src/Handler/Tags/AddTag/AddTagHandler.php @@ -15,17 +15,17 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTag; use OpenDxp\Model\Element\Tag; final class AddTagHandler { - public function __invoke(string $name, int $parentId): AddTagResult + public function __invoke(AddTagPayload $payload): AddTagResult { $tag = new Tag(); - $tag->setName($name); - $tag->setParentId($parentId); + $tag->setName($payload->text); + $tag->setParentId($payload->parentId); $tag->save(); return new AddTagResult(id: $tag->getId()); diff --git a/src/Handler/Tags/AddTag/AddTagPayload.php b/src/Handler/Tags/AddTag/AddTagPayload.php new file mode 100644 index 00000000..2d2498a1 --- /dev/null +++ b/src/Handler/Tags/AddTag/AddTagPayload.php @@ -0,0 +1,37 @@ +request->get('text', '')), + parentId: (int) $request->request->get('parentId'), + ); + } +} diff --git a/src/Handler/Tags/AddTagResult.php b/src/Handler/Tags/AddTag/AddTagResult.php similarity index 91% rename from src/Handler/Tags/AddTagResult.php rename to src/Handler/Tags/AddTag/AddTagResult.php index fc89a5bb..74db5166 100644 --- a/src/Handler/Tags/AddTagResult.php +++ b/src/Handler/Tags/AddTag/AddTagResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTag; final readonly class AddTagResult { diff --git a/src/Handler/Tags/AddTagToElementHandler.php b/src/Handler/Tags/AddTagToElement/AddTagToElementHandler.php similarity index 66% rename from src/Handler/Tags/AddTagToElementHandler.php rename to src/Handler/Tags/AddTagToElement/AddTagToElementHandler.php index 7184fce5..4d2c29d3 100644 --- a/src/Handler/Tags/AddTagToElementHandler.php +++ b/src/Handler/Tags/AddTagToElement/AddTagToElementHandler.php @@ -15,21 +15,21 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTagToElement; use OpenDxp\Model\Element\Tag; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class AddTagToElementHandler { - public function __invoke(int $tagId, string $elementType, int $elementId): AddTagToElementResult + public function __invoke(AddTagToElementPayload $payload): AddTagToElementResult { - $tag = Tag::getById($tagId); + $tag = Tag::getById($payload->tagId); if (!$tag) { - throw new NotFoundHttpException('Tag with ID ' . $tagId . ' not found.'); + throw new NotFoundHttpException('Tag with ID ' . $payload->tagId . ' not found.'); } - Tag::addTagToElement($elementType, $elementId, $tag); + Tag::addTagToElement($payload->elementType, $payload->elementId, $tag); return new AddTagToElementResult(id: $tag->getId()); } diff --git a/src/Handler/Tags/AddTagToElement/AddTagToElementPayload.php b/src/Handler/Tags/AddTagToElement/AddTagToElementPayload.php new file mode 100644 index 00000000..115cff05 --- /dev/null +++ b/src/Handler/Tags/AddTagToElement/AddTagToElementPayload.php @@ -0,0 +1,39 @@ +request->get('tagId'), + elementType: strip_tags($request->request->get('assignmentElementType', '')), + elementId: (int) $request->request->get('assignmentElementId'), + ); + } +} diff --git a/src/Handler/Tags/AddTagToElementResult.php b/src/Handler/Tags/AddTagToElement/AddTagToElementResult.php similarity index 90% rename from src/Handler/Tags/AddTagToElementResult.php rename to src/Handler/Tags/AddTagToElement/AddTagToElementResult.php index 0ea01245..10075c1d 100644 --- a/src/Handler/Tags/AddTagToElementResult.php +++ b/src/Handler/Tags/AddTagToElement/AddTagToElementResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\AddTagToElement; final readonly class AddTagToElementResult { diff --git a/src/Handler/Tags/DeleteTagHandler.php b/src/Handler/Tags/DeleteTag/DeleteTagHandler.php similarity index 73% rename from src/Handler/Tags/DeleteTagHandler.php rename to src/Handler/Tags/DeleteTag/DeleteTagHandler.php index a303f58e..7364df55 100644 --- a/src/Handler/Tags/DeleteTagHandler.php +++ b/src/Handler/Tags/DeleteTag/DeleteTagHandler.php @@ -15,18 +15,18 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\DeleteTag; use OpenDxp\Model\Element\Tag; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class DeleteTagHandler { - public function __invoke(int $id): void + public function __invoke(DeleteTagPayload $payload): void { - $tag = Tag::getById($id); + $tag = Tag::getById($payload->id); if (!$tag) { - throw new NotFoundHttpException('Tag with ID ' . $id . ' not found.'); + throw new NotFoundHttpException('Tag with ID ' . $payload->id . ' not found.'); } $tag->delete(); diff --git a/src/Handler/Asset/Copy/GetAssetChildIds/GetAssetChildIdsPayload.php b/src/Handler/Tags/DeleteTag/DeleteTagPayload.php similarity index 75% rename from src/Handler/Asset/Copy/GetAssetChildIds/GetAssetChildIdsPayload.php rename to src/Handler/Tags/DeleteTag/DeleteTagPayload.php index 851088bc..76845219 100644 --- a/src/Handler/Asset/Copy/GetAssetChildIds/GetAssetChildIdsPayload.php +++ b/src/Handler/Tags/DeleteTag/DeleteTagPayload.php @@ -1,7 +1,5 @@ query->getInt('sourceId'), + id: (int) $request->request->get('id'), ); } } diff --git a/src/Handler/Tags/DoBatchAssignmentHandler.php b/src/Handler/Tags/DoBatchAssignment/DoBatchAssignmentHandler.php similarity index 68% rename from src/Handler/Tags/DoBatchAssignmentHandler.php rename to src/Handler/Tags/DoBatchAssignment/DoBatchAssignmentHandler.php index 4a742284..1eae25a0 100644 --- a/src/Handler/Tags/DoBatchAssignmentHandler.php +++ b/src/Handler/Tags/DoBatchAssignment/DoBatchAssignmentHandler.php @@ -15,14 +15,14 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\DoBatchAssignment; use OpenDxp\Model\Element\Tag; final class DoBatchAssignmentHandler { - public function __invoke(string $elementType, array $elementIds, array $assignedTags, bool $doCleanupTags): void + public function __invoke(DoBatchAssignmentPayload $payload): void { - Tag::batchAssignTagsToElement($elementType, $elementIds, $assignedTags, $doCleanupTags); + Tag::batchAssignTagsToElement($payload->elementType, $payload->childrenIds, $payload->assignedTags, $payload->doCleanupTags); } } diff --git a/src/Handler/Tags/DoBatchAssignment/DoBatchAssignmentPayload.php b/src/Handler/Tags/DoBatchAssignment/DoBatchAssignmentPayload.php new file mode 100644 index 00000000..e0cd54e0 --- /dev/null +++ b/src/Handler/Tags/DoBatchAssignment/DoBatchAssignmentPayload.php @@ -0,0 +1,41 @@ +request->get('elementType', '')), + childrenIds: json_decode($request->request->get('childrenIds'), true) ?? [], + assignedTags: json_decode($request->request->get('assignedTags'), true) ?? [], + doCleanupTags: $request->request->get('removeAndApply') === 'true', + ); + } +} diff --git a/src/Handler/Tags/GetBatchAssignmentJobsHandler.php b/src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsHandler.php similarity index 95% rename from src/Handler/Tags/GetBatchAssignmentJobsHandler.php rename to src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsHandler.php index dcc59361..228143d1 100644 --- a/src/Handler/Tags/GetBatchAssignmentJobsHandler.php +++ b/src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\GetBatchAssignmentJobs; use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -32,10 +32,10 @@ public function __construct( private readonly EventDispatcherInterface $eventDispatcher, ) {} - public function __invoke( - string $elementType, - int $elementId, - ): GetBatchAssignmentJobsResult { + public function __invoke(GetBatchAssignmentJobsPayload $payload): GetBatchAssignmentJobsResult + { + $elementType = $payload->elementType; + $elementId = $payload->elementId; $adminUser = $this->userContext->getAdminUser(); $userIds = null; if (!$adminUser?->isAdmin()) { diff --git a/src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsPayload.php b/src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsPayload.php new file mode 100644 index 00000000..0ff602cf --- /dev/null +++ b/src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsPayload.php @@ -0,0 +1,37 @@ +query->get('elementType', ''), + elementId: $request->query->has('elementId') ? (int) $request->query->get('elementId') : 0, + ); + } +} diff --git a/src/Handler/Tags/GetBatchAssignmentJobsResult.php b/src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsResult.php similarity index 90% rename from src/Handler/Tags/GetBatchAssignmentJobsResult.php rename to src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsResult.php index 435cea78..76a61602 100644 --- a/src/Handler/Tags/GetBatchAssignmentJobsResult.php +++ b/src/Handler/Tags/GetBatchAssignmentJobs/GetBatchAssignmentJobsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\GetBatchAssignmentJobs; final readonly class GetBatchAssignmentJobsResult { diff --git a/src/Handler/Tags/GetTagTreeChildrenHandler.php b/src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenHandler.php similarity index 79% rename from src/Handler/Tags/GetTagTreeChildrenHandler.php rename to src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenHandler.php index 184ceb70..878c0532 100644 --- a/src/Handler/Tags/GetTagTreeChildrenHandler.php +++ b/src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenHandler.php @@ -15,47 +15,42 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\GetTagTreeChildren; use OpenDxp\Model\Element\Tag; final class GetTagTreeChildrenHandler { - public function __invoke( - bool $showSelection, - ?int $assignmentCId, - string $assignmentCType, - ?string $node, - ?string $filter, - ): GetTagTreeChildrenResult { + public function __invoke(GetTagTreeChildrenPayload $payload): GetTagTreeChildrenResult + { $assignedTagIds = []; - if ($assignmentCId && $assignmentCType) { - $assignedTags = Tag::getTagsForElement($assignmentCType, $assignmentCId); + if ($payload->assignmentCId && $payload->assignmentCType) { + $assignedTags = Tag::getTagsForElement($payload->assignmentCType, $payload->assignmentCId); foreach ($assignedTags as $assignedTag) { $assignedTagIds[$assignedTag->getId()] = $assignedTag; } } $tagList = new Tag\Listing(); - if ($node) { - $tagList->setCondition('parentId = ?', $node); + if ($payload->node) { + $tagList->setCondition('parentId = ?', $payload->node); } else { $tagList->setCondition('ISNULL(parentId) OR parentId = 0'); } $tagList->setOrderKey('name'); $recursiveChildren = false; - if (!empty($filter)) { + if (!empty($payload->filter)) { $filterIds = [0]; $filterTagList = new Tag\Listing(); - $filterTagList->setCondition('LOWER(`name`) LIKE ?', ['%' . $filterTagList->escapeLike(mb_strtolower($filter)) . '%']); + $filterTagList->setCondition('LOWER(`name`) LIKE ?', ['%' . $filterTagList->escapeLike(mb_strtolower($payload->filter)) . '%']); foreach ($filterTagList->load() as $filterTag) { if ($filterTag->getParentId() === 0) { $filterIds[] = $filterTag->getId(); } else { $ids = explode('/', $filterTag->getIdPath()); if (isset($ids[1])) { - $filterIds[] = (int)$ids[1]; + $filterIds[] = (int) $ids[1]; } } } @@ -67,7 +62,7 @@ public function __invoke( $tags = []; foreach ($tagList->load() as $tag) { - $tags[] = $this->convertTagToArray($tag, $showSelection, $assignedTagIds, true, $recursiveChildren); + $tags[] = $this->convertTagToArray($tag, $payload->showSelection, $assignedTagIds, true, $recursiveChildren); } return new GetTagTreeChildrenResult(tags: $tags); diff --git a/src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenPayload.php b/src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenPayload.php new file mode 100644 index 00000000..721d20cb --- /dev/null +++ b/src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenPayload.php @@ -0,0 +1,43 @@ +query->get('showSelection') === 'true', + assignmentCId: $request->query->has('assignmentCId') ? (int) $request->query->get('assignmentCId') : null, + assignmentCType: $request->query->get('assignmentCType', ''), + node: $request->query->get('node'), + filter: $request->query->get('filter'), + ); + } +} diff --git a/src/Handler/Tags/GetTagTreeChildrenResult.php b/src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenResult.php similarity index 89% rename from src/Handler/Tags/GetTagTreeChildrenResult.php rename to src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenResult.php index 59dfd5f9..6d5d232a 100644 --- a/src/Handler/Tags/GetTagTreeChildrenResult.php +++ b/src/Handler/Tags/GetTagTreeChildren/GetTagTreeChildrenResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\GetTagTreeChildren; final readonly class GetTagTreeChildrenResult { diff --git a/src/Handler/Tags/GetTagsForElementHandler.php b/src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementHandler.php similarity index 82% rename from src/Handler/Tags/GetTagsForElementHandler.php rename to src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementHandler.php index a7bc33fe..93facd46 100644 --- a/src/Handler/Tags/GetTagsForElementHandler.php +++ b/src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementHandler.php @@ -15,16 +15,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\LoadTagsForElement\GetTagsForElement; use OpenDxp\Model\Element\Tag; final class GetTagsForElementHandler { - public function __invoke(int $assignmentId, string $assignmentType): GetTagsForElementResult + public function __invoke(LoadTagsForElementPayload $payload): GetTagsForElementResult { $assignedTagArray = []; - $assignedTags = Tag::getTagsForElement($assignmentType, $assignmentId); + $assignedTags = Tag::getTagsForElement($payload->assignmentCType, $payload->assignmentCId); foreach ($assignedTags as $assignedTag) { $assignedTagArray[] = $this->convertTagToArray($assignedTag); diff --git a/src/Handler/Tags/GetTagsForElementResult.php b/src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementResult.php similarity index 87% rename from src/Handler/Tags/GetTagsForElementResult.php rename to src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementResult.php index 9fc75cf1..9d249129 100644 --- a/src/Handler/Tags/GetTagsForElementResult.php +++ b/src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\LoadTagsForElement\GetTagsForElement; final readonly class GetTagsForElementResult { diff --git a/src/Handler/Tags/LoadTagsForElement/LoadTagsForElementPayload.php b/src/Handler/Tags/LoadTagsForElement/LoadTagsForElementPayload.php new file mode 100644 index 00000000..b84b942b --- /dev/null +++ b/src/Handler/Tags/LoadTagsForElement/LoadTagsForElementPayload.php @@ -0,0 +1,37 @@ +query->has('assignmentCId') ? (int) $request->query->get('assignmentCId') : null, + assignmentCType: $request->query->get('assignmentCType', ''), + ); + } +} diff --git a/src/Handler/Tags/RemoveTagFromElementHandler.php b/src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementHandler.php similarity index 65% rename from src/Handler/Tags/RemoveTagFromElementHandler.php rename to src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementHandler.php index 978c44ac..f4742bf7 100644 --- a/src/Handler/Tags/RemoveTagFromElementHandler.php +++ b/src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementHandler.php @@ -15,21 +15,21 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\RemoveTagFromElement; use OpenDxp\Model\Element\Tag; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class RemoveTagFromElementHandler { - public function __invoke(int $tagId, string $elementType, int $elementId): RemoveTagFromElementResult + public function __invoke(RemoveTagFromElementPayload $payload): RemoveTagFromElementResult { - $tag = Tag::getById($tagId); + $tag = Tag::getById($payload->tagId); if (!$tag) { - throw new NotFoundHttpException('Tag with ID ' . $tagId . ' not found.'); + throw new NotFoundHttpException('Tag with ID ' . $payload->tagId . ' not found.'); } - Tag::removeTagFromElement($elementType, $elementId, $tag); + Tag::removeTagFromElement($payload->elementType, $payload->elementId, $tag); return new RemoveTagFromElementResult(id: $tag->getId()); } diff --git a/src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementPayload.php b/src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementPayload.php new file mode 100644 index 00000000..314549dc --- /dev/null +++ b/src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementPayload.php @@ -0,0 +1,39 @@ +request->get('tagId'), + elementType: strip_tags($request->request->get('assignmentElementType', '')), + elementId: (int) $request->request->get('assignmentElementId'), + ); + } +} diff --git a/src/Handler/Tags/RemoveTagFromElementResult.php b/src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementResult.php similarity index 89% rename from src/Handler/Tags/RemoveTagFromElementResult.php rename to src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementResult.php index 0f9ee064..6ebc4592 100644 --- a/src/Handler/Tags/RemoveTagFromElementResult.php +++ b/src/Handler/Tags/RemoveTagFromElement/RemoveTagFromElementResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\RemoveTagFromElement; final readonly class RemoveTagFromElementResult { diff --git a/src/Handler/Tags/UpdateTagHandler.php b/src/Handler/Tags/UpdateTag/UpdateTagHandler.php similarity index 62% rename from src/Handler/Tags/UpdateTagHandler.php rename to src/Handler/Tags/UpdateTag/UpdateTagHandler.php index 6e765187..8c3f8ff3 100644 --- a/src/Handler/Tags/UpdateTagHandler.php +++ b/src/Handler/Tags/UpdateTag/UpdateTagHandler.php @@ -15,26 +15,26 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Tags; +namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\UpdateTag; use OpenDxp\Model\Element\Tag; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class UpdateTagHandler { - public function __invoke(int $id, ?int $parentId, ?string $name): void + public function __invoke(UpdateTagPayload $payload): void { - $tag = Tag::getById($id); + $tag = Tag::getById($payload->id); if (!$tag) { - throw new NotFoundHttpException('Tag with ID ' . $id . ' not found.'); + throw new NotFoundHttpException('Tag with ID ' . $payload->id . ' not found.'); } - if ($parentId !== null) { - $tag->setParentId($parentId); + if ($payload->parentId !== null) { + $tag->setParentId($payload->parentId); } - if ($name !== null) { - $tag->setName($name); + if ($payload->name !== null) { + $tag->setName($payload->name); } $tag->save(); diff --git a/src/Handler/Tags/UpdateTag/UpdateTagPayload.php b/src/Handler/Tags/UpdateTag/UpdateTagPayload.php new file mode 100644 index 00000000..3e93a576 --- /dev/null +++ b/src/Handler/Tags/UpdateTag/UpdateTagPayload.php @@ -0,0 +1,41 @@ +request->get('parentId'); + + return new static( + id: (int) $request->request->get('id'), + parentId: ($parentId || $parentId === '0') ? (int) $parentId : null, + name: $request->request->has('text') ? strip_tags($request->request->get('text', '')) : null, + ); + } +} diff --git a/src/Handler/Translation/AddAdminTranslationKeysHandler.php b/src/Handler/Translation/AddAdminTranslationKeys/AddAdminTranslationKeysHandler.php similarity index 87% rename from src/Handler/Translation/AddAdminTranslationKeysHandler.php rename to src/Handler/Translation/AddAdminTranslationKeys/AddAdminTranslationKeysHandler.php index 6a04919b..998f575a 100644 --- a/src/Handler/Translation/AddAdminTranslationKeysHandler.php +++ b/src/Handler/Translation/AddAdminTranslationKeys/AddAdminTranslationKeysHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\AddAdminTranslationKeys; use Exception; use OpenDxp\Logger; @@ -24,11 +24,11 @@ final class AddAdminTranslationKeysHandler { - public function __invoke(array $keys): void + public function __invoke(AddAdminTranslationKeysPayload $payload): void { $availableLanguages = AdminTool::getLanguages(); - foreach ($keys as $translationData) { + foreach ($payload->keys as $translationData) { $t = null; try { diff --git a/src/Handler/Translation/AddAdminTranslationKeys/AddAdminTranslationKeysPayload.php b/src/Handler/Translation/AddAdminTranslationKeys/AddAdminTranslationKeysPayload.php new file mode 100644 index 00000000..79b320a9 --- /dev/null +++ b/src/Handler/Translation/AddAdminTranslationKeys/AddAdminTranslationKeysPayload.php @@ -0,0 +1,36 @@ +request->get('keys'); + + return new static( + keys: $keys ? json_decode($keys, true) : [], + ); + } +} diff --git a/src/Handler/Translation/BuildContentExportJobsHandler.php b/src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsHandler.php similarity index 88% rename from src/Handler/Translation/BuildContentExportJobsHandler.php rename to src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsHandler.php index 8459e627..5a13f90d 100644 --- a/src/Handler/Translation/BuildContentExportJobsHandler.php +++ b/src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\BuildContentExportJobs; use OpenDxp\Model\DataObject; use OpenDxp\Model\DataObject\AbstractObject; @@ -23,19 +23,14 @@ final class BuildContentExportJobsHandler { - public function __invoke( - array $data, - string $source, - string $target, - string $jobUrl, - int $elementsPerJob, - ): BuildContentExportJobsResult { + public function __invoke(BuildContentExportJobsPayload $payload): BuildContentExportJobsResult + { $elements = []; $jobs = []; $exportId = uniqid('', false); - if ($data !== []) { - foreach ($data as $element) { + if ($payload->data !== []) { + foreach ($payload->data as $element) { $elements[$element['type'] . '_' . $element['id']] = [ 'id' => $element['id'], 'type' => $element['type'], @@ -97,15 +92,15 @@ public function __invoke( $elements = array_values($elements); - $elements = array_chunk($elements, $elementsPerJob); + $elements = array_chunk($elements, $payload->elementsPerJob); foreach ($elements as $chunk) { $jobs[] = [[ - 'url' => $jobUrl, + 'url' => $payload->jobUrl, 'method' => 'POST', 'params' => [ 'id' => $exportId, - 'source' => $source, - 'target' => $target, + 'source' => $payload->source, + 'target' => $payload->target, 'data' => json_encode($chunk, JSON_THROW_ON_ERROR), ], ]]; diff --git a/src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsPayload.php b/src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsPayload.php new file mode 100644 index 00000000..170681d4 --- /dev/null +++ b/src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsPayload.php @@ -0,0 +1,46 @@ +request->get('type'); + $jobUrl = $request->request->get('job_url', $request->getBaseUrl() . '/admin/translation/' . $type . '-export'); + $data = json_decode($request->request->get('data'), true); + + return new static( + data: $data && is_array($data) ? $data : [], + source: str_replace('_', '-', $request->request->get('source', '')), + target: str_replace('_', '-', $request->request->get('target', '')), + jobUrl: $jobUrl, + elementsPerJob: max(1, (int) $request->request->get('elements_per_job', 10)), + ); + } +} diff --git a/src/Handler/Translation/BuildContentExportJobsResult.php b/src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsResult.php similarity index 89% rename from src/Handler/Translation/BuildContentExportJobsResult.php rename to src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsResult.php index f5d125a8..5ab20af1 100644 --- a/src/Handler/Translation/BuildContentExportJobsResult.php +++ b/src/Handler/Translation/BuildContentExportJobs/BuildContentExportJobsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\BuildContentExportJobs; final readonly class BuildContentExportJobsResult { diff --git a/src/Handler/Translation/CleanupTranslationsHandler.php b/src/Handler/Translation/CleanupTranslations/CleanupTranslationsHandler.php similarity index 79% rename from src/Handler/Translation/CleanupTranslationsHandler.php rename to src/Handler/Translation/CleanupTranslations/CleanupTranslationsHandler.php index c3b1a6eb..5a711b03 100644 --- a/src/Handler/Translation/CleanupTranslationsHandler.php +++ b/src/Handler/Translation/CleanupTranslations/CleanupTranslationsHandler.php @@ -15,17 +15,17 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\CleanupTranslations; use OpenDxp\Cache; use OpenDxp\Model\Translation; final class CleanupTranslationsHandler { - public function __invoke(string $domain): void + public function __invoke(CleanupTranslationsPayload $payload): void { $list = new Translation\Listing(); - $list->setDomain($domain); + $list->setDomain($payload->domain); $list->cleanup(); Cache::clearTags(['translator', 'translate']); diff --git a/src/Handler/Translation/CleanupTranslations/CleanupTranslationsPayload.php b/src/Handler/Translation/CleanupTranslations/CleanupTranslationsPayload.php new file mode 100644 index 00000000..35bd4f5b --- /dev/null +++ b/src/Handler/Translation/CleanupTranslations/CleanupTranslationsPayload.php @@ -0,0 +1,35 @@ +request->get('domain', Translation::DOMAIN_DEFAULT), + ); + } +} diff --git a/src/Handler/Translation/CreateTranslationHandler.php b/src/Handler/Translation/CreateTranslation/CreateTranslationHandler.php similarity index 96% rename from src/Handler/Translation/CreateTranslationHandler.php rename to src/Handler/Translation/CreateTranslation/CreateTranslationHandler.php index 1c8c6014..8c1f866b 100644 --- a/src/Handler/Translation/CreateTranslationHandler.php +++ b/src/Handler/Translation/CreateTranslation/CreateTranslationHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\CreateTranslation; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Translation; diff --git a/src/Handler/Translation/CreateTranslationResult.php b/src/Handler/Translation/CreateTranslation/CreateTranslationResult.php similarity index 90% rename from src/Handler/Translation/CreateTranslationResult.php rename to src/Handler/Translation/CreateTranslation/CreateTranslationResult.php index c6e93fd3..8300d479 100644 --- a/src/Handler/Translation/CreateTranslationResult.php +++ b/src/Handler/Translation/CreateTranslation/CreateTranslationResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\CreateTranslation; final readonly class CreateTranslationResult { diff --git a/src/Handler/Translation/DeleteTranslationHandler.php b/src/Handler/Translation/DeleteTranslation/DeleteTranslationHandler.php similarity index 91% rename from src/Handler/Translation/DeleteTranslationHandler.php rename to src/Handler/Translation/DeleteTranslation/DeleteTranslationHandler.php index f01969ff..73b63247 100644 --- a/src/Handler/Translation/DeleteTranslationHandler.php +++ b/src/Handler/Translation/DeleteTranslation/DeleteTranslationHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\DeleteTranslation; use OpenDxp\Model\Translation; diff --git a/src/Handler/Translation/ExportTranslationsHandler.php b/src/Handler/Translation/ExportTranslations/ExportTranslationsHandler.php similarity index 89% rename from src/Handler/Translation/ExportTranslationsHandler.php rename to src/Handler/Translation/ExportTranslations/ExportTranslationsHandler.php index cd6218e8..27221417 100644 --- a/src/Handler/Translation/ExportTranslationsHandler.php +++ b/src/Handler/Translation/ExportTranslations/ExportTranslationsHandler.php @@ -15,10 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\ExportTranslations; use Doctrine\DBAL\Exception\SyntaxErrorException; use InvalidArgumentException; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationQueryTrait; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Element; use OpenDxp\Model\Translation; @@ -31,21 +32,18 @@ final class ExportTranslationsHandler public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke( - ?string $domain, - ?string $filter, - ?string $searchString, - bool $admin, - ): ExportTranslationsResult { + public function __invoke(ExportTranslationsPayload $payload): ExportTranslationsResult + { + $admin = $payload->domain === Translation::DOMAIN_ADMIN; $allowedLanguages = $admin ? Tool\Admin::getLanguages() : $this->userContext->getAdminUser()->getAllowedLanguagesForViewingWebsiteTranslations(); $translation = new Translation(); - $translation->setDomain($domain); + $translation->setDomain($payload->domain); $tableName = $translation->getDao()->getDatabaseTableName(); $list = new Translation\Listing(); - $list->setDomain($domain); + $list->setDomain($payload->domain); $joins = []; @@ -53,8 +51,8 @@ public function __invoke( $list->setOrderKey($tableName . '.key', false); $filterParameters = [ - 'filter' => $filter, - 'searchString' => $searchString, + 'filter' => $payload->filter, + 'searchString' => $payload->searchString, ]; $conditions = $this->getGridFilterCondition($filterParameters, $tableName, false, $allowedLanguages); @@ -144,6 +142,6 @@ public function __invoke( $csv .= implode(';', $tempRow) . "\r\n"; } - return new ExportTranslationsResult(csv: $csv, domain: $domain ?? ''); + return new ExportTranslationsResult(csv: $csv, domain: $payload->domain ?? ''); } } diff --git a/src/Handler/Translation/ExportTranslations/ExportTranslationsPayload.php b/src/Handler/Translation/ExportTranslations/ExportTranslationsPayload.php new file mode 100644 index 00000000..979badcd --- /dev/null +++ b/src/Handler/Translation/ExportTranslations/ExportTranslationsPayload.php @@ -0,0 +1,38 @@ +query->get('domain'), + filter: $request->query->get('filter'), + searchString: $request->query->get('searchString'), + ); + } +} diff --git a/src/Handler/Translation/ExportTranslationsResult.php b/src/Handler/Translation/ExportTranslations/ExportTranslationsResult.php similarity index 89% rename from src/Handler/Translation/ExportTranslationsResult.php rename to src/Handler/Translation/ExportTranslations/ExportTranslationsResult.php index 0a2a0a16..0fcfbd2d 100644 --- a/src/Handler/Translation/ExportTranslationsResult.php +++ b/src/Handler/Translation/ExportTranslations/ExportTranslationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\ExportTranslations; final readonly class ExportTranslationsResult { diff --git a/src/Handler/Translation/GetTranslationDomainsHandler.php b/src/Handler/Translation/GetTranslationDomains/GetTranslationDomainsHandler.php similarity index 79% rename from src/Handler/Translation/GetTranslationDomainsHandler.php rename to src/Handler/Translation/GetTranslationDomains/GetTranslationDomainsHandler.php index 57817777..0171b7aa 100644 --- a/src/Handler/Translation/GetTranslationDomainsHandler.php +++ b/src/Handler/Translation/GetTranslationDomains/GetTranslationDomainsHandler.php @@ -15,13 +15,14 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslationDomains; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Model\Translation; final class GetTranslationDomainsHandler { - public function __invoke(): GetTranslationDomainsResult + public function __invoke(EmptyPayload $payload): GetTranslationDomainsResult { $translation = new Translation(); diff --git a/src/Handler/Translation/GetTranslationDomainsResult.php b/src/Handler/Translation/GetTranslationDomains/GetTranslationDomainsResult.php similarity index 88% rename from src/Handler/Translation/GetTranslationDomainsResult.php rename to src/Handler/Translation/GetTranslationDomains/GetTranslationDomainsResult.php index 3786ab94..641d6605 100644 --- a/src/Handler/Translation/GetTranslationDomainsResult.php +++ b/src/Handler/Translation/GetTranslationDomains/GetTranslationDomainsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslationDomains; final readonly class GetTranslationDomainsResult { diff --git a/src/Handler/Translation/GetTranslationsHandler.php b/src/Handler/Translation/GetTranslations/GetTranslationsHandler.php similarity index 96% rename from src/Handler/Translation/GetTranslationsHandler.php rename to src/Handler/Translation/GetTranslations/GetTranslationsHandler.php index 516589fa..9aed091e 100644 --- a/src/Handler/Translation/GetTranslationsHandler.php +++ b/src/Handler/Translation/GetTranslations/GetTranslationsHandler.php @@ -15,8 +15,9 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslations; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationQueryTrait; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Translation; diff --git a/src/Handler/Translation/GetTranslationsResult.php b/src/Handler/Translation/GetTranslations/GetTranslationsResult.php similarity index 89% rename from src/Handler/Translation/GetTranslationsResult.php rename to src/Handler/Translation/GetTranslations/GetTranslationsResult.php index 0527943c..194657e4 100644 --- a/src/Handler/Translation/GetTranslationsResult.php +++ b/src/Handler/Translation/GetTranslations/GetTranslationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslations; final readonly class GetTranslationsResult { diff --git a/src/Handler/Translation/GetWebsiteTranslationLanguagesHandler.php b/src/Handler/Translation/GetWebsiteTranslationLanguages/GetWebsiteTranslationLanguagesHandler.php similarity index 79% rename from src/Handler/Translation/GetWebsiteTranslationLanguagesHandler.php rename to src/Handler/Translation/GetWebsiteTranslationLanguages/GetWebsiteTranslationLanguagesHandler.php index 9edac445..e6c1af13 100644 --- a/src/Handler/Translation/GetWebsiteTranslationLanguagesHandler.php +++ b/src/Handler/Translation/GetWebsiteTranslationLanguages/GetWebsiteTranslationLanguagesHandler.php @@ -14,15 +14,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\GetWebsiteTranslationLanguages; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class GetWebsiteTranslationLanguagesHandler { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke(): GetWebsiteTranslationLanguagesResult + public function __invoke(EmptyPayload $payload): GetWebsiteTranslationLanguagesResult { $user = $this->userContext->getAdminUser(); diff --git a/src/Handler/Translation/GetWebsiteTranslationLanguagesResult.php b/src/Handler/Translation/GetWebsiteTranslationLanguages/GetWebsiteTranslationLanguagesResult.php similarity index 86% rename from src/Handler/Translation/GetWebsiteTranslationLanguagesResult.php rename to src/Handler/Translation/GetWebsiteTranslationLanguages/GetWebsiteTranslationLanguagesResult.php index 4c6ef3dc..cfc868bd 100644 --- a/src/Handler/Translation/GetWebsiteTranslationLanguagesResult.php +++ b/src/Handler/Translation/GetWebsiteTranslationLanguages/GetWebsiteTranslationLanguagesResult.php @@ -14,7 +14,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\GetWebsiteTranslationLanguages; final readonly class GetWebsiteTranslationLanguagesResult { diff --git a/src/Handler/Translation/ImportTranslationsHandler.php b/src/Handler/Translation/ImportTranslations/ImportTranslationsHandler.php similarity index 69% rename from src/Handler/Translation/ImportTranslationsHandler.php rename to src/Handler/Translation/ImportTranslations/ImportTranslationsHandler.php index 6d1a0f06..4a765be5 100644 --- a/src/Handler/Translation/ImportTranslationsHandler.php +++ b/src/Handler/Translation/ImportTranslations/ImportTranslationsHandler.php @@ -15,46 +15,44 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\ImportTranslations; use Locale; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Localization\LocaleServiceInterface; use OpenDxp\Model\Translation; use OpenDxp\Tool; +use Symfony\Component\Routing\RouterInterface; final class ImportTranslationsHandler { public function __construct( private readonly LocaleServiceInterface $localeService, private readonly AdminUserContextInterface $userContext, + private readonly RouterInterface $router, ) {} - public function __invoke( - string $tmpFile, - string $domain, - bool $overwrite, - ?object $dialect, - bool $enrichDelta, - string $flagUrlTemplate, - ): ImportTranslationsResult { - $admin = $domain === Translation::DOMAIN_ADMIN; + public function __invoke(ImportTranslationsPayload $payload): ImportTranslationsResult + { + $admin = $payload->domain === Translation::DOMAIN_ADMIN; $allowedLanguages = $admin ? Tool\Admin::getLanguages() : $this->userContext->getAdminUser()->getAllowedLanguagesForEditingWebsiteTranslations(); $delta = Translation::importTranslationsFromFile( - $tmpFile, - $domain, - $overwrite, + $payload->tmpFile, + $payload->domain, + $payload->overwrite, $allowedLanguages, - $dialect + $payload->dialect ); - if (is_file($tmpFile)) { - @unlink($tmpFile); + if (is_file($payload->tmpFile)) { + @unlink($payload->tmpFile); } - if ($enrichDelta) { + if ($payload->enrichDelta) { + $flagUrlTemplate = $this->router->generate('opendxp_admin_misc_getlanguageflag', ['language' => '{language}']); + $flagUrlTemplate = str_replace('%7Blanguage%7D', '{language}', $flagUrlTemplate); $enrichedDelta = []; foreach ($delta as $item) { $lg = $item['lg']; diff --git a/src/Handler/Translation/ImportTranslations/ImportTranslationsPayload.php b/src/Handler/Translation/ImportTranslations/ImportTranslationsPayload.php new file mode 100644 index 00000000..09f150a9 --- /dev/null +++ b/src/Handler/Translation/ImportTranslations/ImportTranslationsPayload.php @@ -0,0 +1,53 @@ +request->get('domain', Translation::DOMAIN_DEFAULT); + $merge = $request->query->get('merge'); + $dialect = $request->request->get('csvSettings'); + if ($dialect) { + $dialect = json_decode($dialect); + } + $session = Session::getSessionBag($request->getSession(), 'opendxp_importconfig'); + $tmpFile = $session->get('translation_import_file'); + + return new static( + domain: $domain, + tmpFile: $tmpFile, + overwrite: !$merge, + dialect: $dialect, + enrichDelta: (bool) $merge, + ); + } +} diff --git a/src/Handler/Translation/ImportTranslationsResult.php b/src/Handler/Translation/ImportTranslations/ImportTranslationsResult.php similarity index 89% rename from src/Handler/Translation/ImportTranslationsResult.php rename to src/Handler/Translation/ImportTranslations/ImportTranslationsResult.php index 1a363d60..18ae4be7 100644 --- a/src/Handler/Translation/ImportTranslationsResult.php +++ b/src/Handler/Translation/ImportTranslations/ImportTranslationsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\ImportTranslations; final readonly class ImportTranslationsResult { diff --git a/src/Handler/Translation/MergeTranslationItemsHandler.php b/src/Handler/Translation/MergeTranslationItems/MergeTranslationItemsHandler.php similarity index 74% rename from src/Handler/Translation/MergeTranslationItemsHandler.php rename to src/Handler/Translation/MergeTranslationItems/MergeTranslationItemsHandler.php index df967cf3..9c45375c 100644 --- a/src/Handler/Translation/MergeTranslationItemsHandler.php +++ b/src/Handler/Translation/MergeTranslationItems/MergeTranslationItemsHandler.php @@ -15,16 +15,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\MergeTranslationItems; use OpenDxp\Model\Translation; final class MergeTranslationItemsHandler { - public function __invoke(array $dataList, string $domain): void + public function __invoke(MergeTranslationItemsPayload $payload): void { - foreach ($dataList as $data) { - $t = Translation::getByKey($data['key'], $domain, true); + foreach ($payload->dataList as $data) { + $t = Translation::getByKey($data['key'], $payload->domain, true); $newValue = htmlspecialchars_decode($data['current']); $t->addTranslation($data['lg'], $newValue); $t->setModificationDate(time()); diff --git a/src/Handler/Translation/MergeTranslationItems/MergeTranslationItemsPayload.php b/src/Handler/Translation/MergeTranslationItems/MergeTranslationItemsPayload.php new file mode 100644 index 00000000..7f876ba4 --- /dev/null +++ b/src/Handler/Translation/MergeTranslationItems/MergeTranslationItemsPayload.php @@ -0,0 +1,37 @@ +request->get('data'), true), + domain: $request->request->get('domain', Translation::DOMAIN_DEFAULT), + ); + } +} diff --git a/src/Handler/Translation/UpdateTranslationHandler.php b/src/Handler/Translation/UpdateTranslation/UpdateTranslationHandler.php similarity index 95% rename from src/Handler/Translation/UpdateTranslationHandler.php rename to src/Handler/Translation/UpdateTranslation/UpdateTranslationHandler.php index a98e9568..cc962202 100644 --- a/src/Handler/Translation/UpdateTranslationHandler.php +++ b/src/Handler/Translation/UpdateTranslation/UpdateTranslationHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\UpdateTranslation; use OpenDxp\Model\Translation; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/src/Handler/Translation/UpdateTranslationResult.php b/src/Handler/Translation/UpdateTranslation/UpdateTranslationResult.php similarity index 90% rename from src/Handler/Translation/UpdateTranslationResult.php rename to src/Handler/Translation/UpdateTranslation/UpdateTranslationResult.php index 83deac47..45133aa8 100644 --- a/src/Handler/Translation/UpdateTranslationResult.php +++ b/src/Handler/Translation/UpdateTranslation/UpdateTranslationResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\UpdateTranslation; final readonly class UpdateTranslationResult { diff --git a/src/Handler/Translation/UploadTranslationImportFileHandler.php b/src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFileHandler.php similarity index 83% rename from src/Handler/Translation/UploadTranslationImportFileHandler.php rename to src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFileHandler.php index 7f2cd57f..4b67c622 100644 --- a/src/Handler/Translation/UploadTranslationImportFileHandler.php +++ b/src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFileHandler.php @@ -15,11 +15,10 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\UploadTranslationImportFile; use OpenDxp\Tool\Admin as AdminTool; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\HttpFoundation\File\UploadedFile; final class UploadTranslationImportFileHandler { @@ -27,9 +26,9 @@ public function __construct( private readonly Filesystem $filesystem, ) {} - public function __invoke(UploadedFile $file): UploadTranslationImportFileResult + public function __invoke(UploadTranslationImportFilePayload $payload): UploadTranslationImportFileResult { - $tmpData = file_get_contents($file->getPathname()); + $tmpData = file_get_contents($payload->file->getPathname()); $filename = uniqid('import_translations-', false); $importFile = OPENDXP_SYSTEM_TEMP_DIRECTORY . '/' . $filename; diff --git a/src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFilePayload.php b/src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFilePayload.php new file mode 100644 index 00000000..b57fd86e --- /dev/null +++ b/src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFilePayload.php @@ -0,0 +1,35 @@ +files->get('Filedata'), + ); + } +} diff --git a/src/Handler/Translation/UploadTranslationImportFileResult.php b/src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFileResult.php similarity index 88% rename from src/Handler/Translation/UploadTranslationImportFileResult.php rename to src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFileResult.php index 7fa3b4ed..63b3715f 100644 --- a/src/Handler/Translation/UploadTranslationImportFileResult.php +++ b/src/Handler/Translation/UploadTranslationImportFile/UploadTranslationImportFileResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Translation; +namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\UploadTranslationImportFile; final readonly class UploadTranslationImportFileResult { diff --git a/src/Handler/User/AddUserHandler.php b/src/Handler/User/AddUser/AddUserHandler.php similarity index 86% rename from src/Handler/User/AddUserHandler.php rename to src/Handler/User/AddUser/AddUserHandler.php index 0b0feb2a..9803a6eb 100644 --- a/src/Handler/User/AddUserHandler.php +++ b/src/Handler/User/AddUser/AddUserHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\AddUser; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\User; @@ -24,25 +24,20 @@ final class AddUserHandler { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke( - string $type, - int $parentId, - string $name, - bool $active, - ?int $referenceId, - ): AddUserResult { + public function __invoke(AddUserPayload $payload): AddUserResult + { $currentUserIsAdmin = $this->userContext->getAdminUser()?->isAdmin() ?? false; - $className = User\Service::getClassNameForType($type); + $className = User\Service::getClassNameForType($payload->type); $user = $className::create([ - 'parentId' => $parentId, - 'name' => $name, + 'parentId' => $payload->parentId, + 'name' => $payload->name, 'password' => '', - 'active' => $active, + 'active' => $payload->active, ]); - if ($referenceId !== null) { - $rObject = $className::getById($referenceId); - if ($rObject && ($type === 'user' || $type === 'role')) { + if ($payload->referenceId !== null) { + $rObject = $className::getById($payload->referenceId); + if ($rObject && ($payload->type === 'user' || $payload->type === 'role')) { $user->setParentId($rObject->getParentId()); if ($rObject->getClasses()) { $user->setClasses(implode(',', $rObject->getClasses())); @@ -78,7 +73,7 @@ public function __invoke( } $user->setPerspectives($rObject->getPerspectives()); $user->setPermissions($rObject->getPermissions()); - if ($type === 'user') { + if ($payload->type === 'user') { $user->setAdmin(false); if ($currentUserIsAdmin) { $user->setAdmin($rObject->getAdmin()); diff --git a/src/Handler/User/AddUser/AddUserPayload.php b/src/Handler/User/AddUser/AddUserPayload.php new file mode 100644 index 00000000..eee78a86 --- /dev/null +++ b/src/Handler/User/AddUser/AddUserPayload.php @@ -0,0 +1,46 @@ +request->get('name', ''); + $referenceId = $request->request->has('rid') ? (int) $request->request->get('rid') : null; + + return new static( + type: $request->request->get('type'), + parentId: $request->request->getInt('parentId'), + name: trim((string) $name), + active: $request->request->getBoolean('active'), + referenceId: $referenceId, + ); + } +} diff --git a/src/Handler/User/AddUserResult.php b/src/Handler/User/AddUser/AddUserResult.php similarity index 91% rename from src/Handler/User/AddUserResult.php rename to src/Handler/User/AddUser/AddUserResult.php index c677329d..f0f4b5cf 100644 --- a/src/Handler/User/AddUserResult.php +++ b/src/Handler/User/AddUser/AddUserResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\AddUser; final readonly class AddUserResult { diff --git a/src/Handler/User/DeleteUserHandler.php b/src/Handler/User/DeleteUser/DeleteUserHandler.php similarity index 93% rename from src/Handler/User/DeleteUserHandler.php rename to src/Handler/User/DeleteUser/DeleteUserHandler.php index cf2a870e..8233418f 100644 --- a/src/Handler/User/DeleteUserHandler.php +++ b/src/Handler/User/DeleteUser/DeleteUserHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUser; use Exception; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -30,12 +30,12 @@ public function __construct(private readonly AdminUserContextInterface $userCont /** * @throws Exception */ - public function __invoke(int $id): void + public function __invoke(DeleteUserPayload $payload): void { $adminUser = $this->userContext->getAdminUser(); $currentUserId = (int) $adminUser?->getId(); - $user = User\AbstractUser::getById($id); + $user = User\AbstractUser::getById($payload->id); if (!$user) { throw new NotFoundHttpException('User not found'); } diff --git a/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigPayload.php b/src/Handler/User/DeleteUser/DeleteUserPayload.php similarity index 74% rename from src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigPayload.php rename to src/Handler/User/DeleteUser/DeleteUserPayload.php index 1970bf14..88fb6e64 100644 --- a/src/Handler/DataObject/Helper/DeleteDataObjectGridColumnConfigPayload.php +++ b/src/Handler/User/DeleteUser/DeleteUserPayload.php @@ -15,21 +15,21 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Helper; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUser; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -final readonly class DeleteDataObjectGridColumnConfigPayload implements ExtJsPayloadInterface +final readonly class DeleteUserPayload implements ExtJsPayloadInterface { public function __construct( - public int $gridConfigId = 0, + public readonly int $id, ) {} public static function fromRequest(Request $request): static { return new static( - gridConfigId: $request->request->getInt('gridConfigId'), + id: $request->request->getInt('id'), ); } } diff --git a/src/Handler/User/DeleteUserImageHandler.php b/src/Handler/User/DeleteUserImage/DeleteUserImageHandler.php similarity index 87% rename from src/Handler/User/DeleteUserImageHandler.php rename to src/Handler/User/DeleteUserImage/DeleteUserImageHandler.php index 55fa1f94..4402c177 100644 --- a/src/Handler/User/DeleteUserImageHandler.php +++ b/src/Handler/User/DeleteUserImage/DeleteUserImageHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\DeleteUserImage; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\User; @@ -26,10 +26,10 @@ final class DeleteUserImageHandler { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke(?int $targetUserId = null): void + public function __invoke(DeleteUserImagePayload $payload): void { $adminUser = $this->userContext->getAdminUser(); - $targetUserId ??= (int) $adminUser?->getId(); + $targetUserId = $payload->targetUserId ?? (int) $adminUser?->getId(); $userObj = User::getById($targetUserId); if (!$userObj) { diff --git a/src/Handler/User/DeleteUserImage/DeleteUserImagePayload.php b/src/Handler/User/DeleteUserImage/DeleteUserImagePayload.php new file mode 100644 index 00000000..c17c1206 --- /dev/null +++ b/src/Handler/User/DeleteUserImage/DeleteUserImagePayload.php @@ -0,0 +1,35 @@ +query->has('id') ? $request->query->getInt('id') : null, + ); + } +} diff --git a/src/Handler/User/Disable2FaHandler.php b/src/Handler/User/Disable2Fa/Disable2FaHandler.php similarity index 90% rename from src/Handler/User/Disable2FaHandler.php rename to src/Handler/User/Disable2Fa/Disable2FaHandler.php index a4507533..d4eb72aa 100644 --- a/src/Handler/User/Disable2FaHandler.php +++ b/src/Handler/User/Disable2Fa/Disable2FaHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\Disable2Fa; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\User; @@ -25,7 +25,7 @@ final class Disable2FaHandler { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke(): void + public function __invoke(Disable2FaPayload $payload): void { $user = $this->userContext->getAdminUser(); if (!$user instanceof User) { diff --git a/src/Handler/User/Disable2Fa/Disable2FaPayload.php b/src/Handler/User/Disable2Fa/Disable2FaPayload.php new file mode 100644 index 00000000..3512c66e --- /dev/null +++ b/src/Handler/User/Disable2Fa/Disable2FaPayload.php @@ -0,0 +1,31 @@ +userContext->getAdminUser(); $list = new User\Permission\Definition\Listing(); @@ -46,7 +46,7 @@ public function __invoke(bool $isPasswordReset): GetCurrentUserResult unset($userData['twoFactorAuthentication']['secret']); $userData['twoFactorAuthentication']['isActive'] = $user->getTwoFactorAuthentication('enabled') && $user->getTwoFactorAuthentication('secret'); $userData['hasImage'] = $user->hasImage(); - $userData['isPasswordReset'] = $isPasswordReset; + $userData['isPasswordReset'] = $payload->isPasswordReset; $userData['validLocales'] = Tool::getSupportedJSLocales(); return new GetCurrentUserResult(userData: $userData); diff --git a/src/Handler/User/GetCurrentUser/GetCurrentUserPayload.php b/src/Handler/User/GetCurrentUser/GetCurrentUserPayload.php new file mode 100644 index 00000000..e9f7aa83 --- /dev/null +++ b/src/Handler/User/GetCurrentUser/GetCurrentUserPayload.php @@ -0,0 +1,33 @@ +getSession(), fn (AttributeBagInterface $adminSession) => $adminSession->get('password_reset')), + ); + } +} diff --git a/src/Handler/User/GetCurrentUserResult.php b/src/Handler/User/GetCurrentUser/GetCurrentUserResult.php similarity index 90% rename from src/Handler/User/GetCurrentUserResult.php rename to src/Handler/User/GetCurrentUser/GetCurrentUserResult.php index b8350e1c..655f22aa 100644 --- a/src/Handler/User/GetCurrentUserResult.php +++ b/src/Handler/User/GetCurrentUser/GetCurrentUserResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetCurrentUser; final readonly class GetCurrentUserResult { diff --git a/src/Handler/User/GetMinimalUserHandler.php b/src/Handler/User/GetMinimalUser/GetMinimalUserHandler.php similarity index 85% rename from src/Handler/User/GetMinimalUserHandler.php rename to src/Handler/User/GetMinimalUser/GetMinimalUserHandler.php index ecce6c32..f210b514 100644 --- a/src/Handler/User/GetMinimalUserHandler.php +++ b/src/Handler/User/GetMinimalUser/GetMinimalUserHandler.php @@ -15,16 +15,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetMinimalUser; use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class GetMinimalUserHandler { - public function __invoke(int $id): GetMinimalUserResult + public function __invoke(GetMinimalUserPayload $payload): GetMinimalUserResult { - $user = User::getById($id); + $user = User::getById($payload->id); if (!$user) { throw new NotFoundHttpException('User not found'); } diff --git a/src/Handler/User/GetMinimalUser/GetMinimalUserPayload.php b/src/Handler/User/GetMinimalUser/GetMinimalUserPayload.php new file mode 100644 index 00000000..6d059025 --- /dev/null +++ b/src/Handler/User/GetMinimalUser/GetMinimalUserPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/User/GetMinimalUserResult.php b/src/Handler/User/GetMinimalUser/GetMinimalUserResult.php similarity index 91% rename from src/Handler/User/GetMinimalUserResult.php rename to src/Handler/User/GetMinimalUser/GetMinimalUserResult.php index 503795c9..8ca6cef9 100644 --- a/src/Handler/User/GetMinimalUserResult.php +++ b/src/Handler/User/GetMinimalUser/GetMinimalUserResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetMinimalUser; final readonly class GetMinimalUserResult { diff --git a/src/Handler/User/GetRoleHandler.php b/src/Handler/User/GetRole/GetRoleHandler.php similarity index 93% rename from src/Handler/User/GetRoleHandler.php rename to src/Handler/User/GetRole/GetRoleHandler.php index 0d7ddc3d..e5844198 100644 --- a/src/Handler/User/GetRoleHandler.php +++ b/src/Handler/User/GetRole/GetRoleHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetRole; use OpenDxp\Bundle\AdminBundle\Perspective\Config; use OpenDxp\Model\Element; @@ -26,9 +26,9 @@ final class GetRoleHandler { - public function __invoke(int $id): GetRoleResult + public function __invoke(GetRolePayload $payload): GetRoleResult { - $role = User\Role::getById($id); + $role = User\Role::getById($payload->id); if (!$role) { throw new NotFoundHttpException('Role not found'); } diff --git a/src/Handler/User/GetRole/GetRolePayload.php b/src/Handler/User/GetRole/GetRolePayload.php new file mode 100644 index 00000000..93fcc124 --- /dev/null +++ b/src/Handler/User/GetRole/GetRolePayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/User/GetRoleResult.php b/src/Handler/User/GetRole/GetRoleResult.php similarity index 93% rename from src/Handler/User/GetRoleResult.php rename to src/Handler/User/GetRole/GetRoleResult.php index 2659143b..54c94589 100644 --- a/src/Handler/User/GetRoleResult.php +++ b/src/Handler/User/GetRole/GetRoleResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetRole; final readonly class GetRoleResult { diff --git a/src/Handler/User/GetRoleTreeChildrenHandler.php b/src/Handler/User/GetRoleTreeChildren/GetRoleTreeChildrenHandler.php similarity index 89% rename from src/Handler/User/GetRoleTreeChildrenHandler.php rename to src/Handler/User/GetRoleTreeChildren/GetRoleTreeChildrenHandler.php index fd136819..b39bc7a6 100644 --- a/src/Handler/User/GetRoleTreeChildrenHandler.php +++ b/src/Handler/User/GetRoleTreeChildren/GetRoleTreeChildrenHandler.php @@ -15,16 +15,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetRoleTreeChildren; use OpenDxp\Model\User; final class GetRoleTreeChildrenHandler { - public function __invoke(int $parentId): array + public function __invoke(GetRoleTreeChildrenPayload $payload): array { $list = new User\Role\Listing(); - $list->setCondition('parentId = ?', $parentId); + $list->setCondition('parentId = ?', $payload->node); $list->load(); $roles = []; diff --git a/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsPayload.php b/src/Handler/User/GetRoleTreeChildren/GetRoleTreeChildrenPayload.php similarity index 68% rename from src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsPayload.php rename to src/Handler/User/GetRoleTreeChildren/GetRoleTreeChildrenPayload.php index 8440e165..3127b7e3 100644 --- a/src/Handler/Document/Copy/GetDocumentChildIds/GetDocumentChildIdsPayload.php +++ b/src/Handler/User/GetRoleTreeChildren/GetRoleTreeChildrenPayload.php @@ -14,17 +14,17 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Document\Copy\GetDocumentChildIds; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetRoleTreeChildren; use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; -final readonly class GetDocumentChildIdsPayload implements ExtJsPayloadInterface +final readonly class GetRoleTreeChildrenPayload implements ExtJsPayloadInterface { - public function __construct(public readonly int $sourceId = 0) {} + public function __construct(public readonly int $node = 0) {} public static function fromRequest(Request $request): static { - return new static(sourceId: $request->query->getInt('sourceId')); + return new static(node: $request->query->getInt('node')); } } diff --git a/src/Handler/User/GetRolesHandler.php b/src/Handler/User/GetRoles/GetRolesHandler.php similarity index 80% rename from src/Handler/User/GetRolesHandler.php rename to src/Handler/User/GetRoles/GetRolesHandler.php index b4aa87a9..fa42595b 100644 --- a/src/Handler/User/GetRolesHandler.php +++ b/src/Handler/User/GetRoles/GetRolesHandler.php @@ -15,13 +15,13 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetRoles; use OpenDxp\Model\User; final class GetRolesHandler { - public function __invoke(?string $permission): array + public function __invoke(GetRolesPayload $payload): array { $list = new User\Role\Listing(); $list->setCondition('`type` = "role"'); @@ -29,7 +29,7 @@ public function __invoke(?string $permission): array $roles = []; foreach ($list->getRoles() as $role) { - if ($permission === null || in_array($permission, $role->getPermissions())) { + if ($payload->permission === null || in_array($payload->permission, $role->getPermissions())) { $roles[] = [ 'id' => $role->getId(), 'label' => $role->getName(), diff --git a/src/Handler/User/GetRoles/GetRolesPayload.php b/src/Handler/User/GetRoles/GetRolesPayload.php new file mode 100644 index 00000000..7120087e --- /dev/null +++ b/src/Handler/User/GetRoles/GetRolesPayload.php @@ -0,0 +1,30 @@ +query->get('permission')); + } +} diff --git a/src/Handler/User/GetTokenLoginLinkHandler.php b/src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkHandler.php similarity index 90% rename from src/Handler/User/GetTokenLoginLinkHandler.php rename to src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkHandler.php index 836931c9..63226fb4 100644 --- a/src/Handler/User/GetTokenLoginLinkHandler.php +++ b/src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetTokenLoginLink; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\CustomLoginUrlGenerator; @@ -33,9 +33,9 @@ public function __construct( private readonly TranslatorInterface $translator, ) {} - public function __invoke(int $id): GetTokenLoginLinkResult + public function __invoke(GetTokenLoginLinkPayload $payload): GetTokenLoginLinkResult { - $user = User::getById($id); + $user = User::getById($payload->id); if (!$user) { throw new NotFoundHttpException($this->translator->trans('login_token_invalid_user_error', [], 'admin')); } diff --git a/src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkPayload.php b/src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkPayload.php new file mode 100644 index 00000000..f13fe924 --- /dev/null +++ b/src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/User/GetTokenLoginLinkResult.php b/src/Handler/User/GetTokenLoginLinkResult.php deleted file mode 100644 index 7b2cfdf7..00000000 --- a/src/Handler/User/GetTokenLoginLinkResult.php +++ /dev/null @@ -1,25 +0,0 @@ -id < 1) { throw new NotFoundHttpException('User not found'); } - $user = User::getById($id); + $user = User::getById($payload->id); if (!$user) { throw new NotFoundHttpException('User not found'); } diff --git a/src/Handler/User/GetUser/GetUserPayload.php b/src/Handler/User/GetUser/GetUserPayload.php new file mode 100644 index 00000000..1cc8e0cc --- /dev/null +++ b/src/Handler/User/GetUser/GetUserPayload.php @@ -0,0 +1,35 @@ +query->getInt('id'), + ); + } +} diff --git a/src/Handler/User/GetUserResult.php b/src/Handler/User/GetUser/GetUserResult.php similarity index 93% rename from src/Handler/User/GetUserResult.php rename to src/Handler/User/GetUser/GetUserResult.php index 099db2dd..4903aa06 100644 --- a/src/Handler/User/GetUserResult.php +++ b/src/Handler/User/GetUser/GetUserResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetUser; final readonly class GetUserResult { diff --git a/src/Handler/User/GetUserImageHandler.php b/src/Handler/User/GetUserImage/GetUserImageHandler.php similarity index 69% rename from src/Handler/User/GetUserImageHandler.php rename to src/Handler/User/GetUserImage/GetUserImageHandler.php index a9e89d54..695ce140 100644 --- a/src/Handler/User/GetUserImageHandler.php +++ b/src/Handler/User/GetUserImage/GetUserImageHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetUserImage; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\User; @@ -25,15 +25,16 @@ final class GetUserImageHandler { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke(?int $targetUserId = null): mixed + public function __invoke(GetUserImagePayload $payload): GetUserImageResult { - $targetUserId ??= (int) $this->userContext->getAdminUser()?->getId(); + $targetUserId = $payload->targetUserId ?? (int) $this->userContext->getAdminUser()?->getId(); - $user = User::getById($targetUserId); - if (!$user) { + $userObj = User::getById($targetUserId); + + if (!$userObj) { throw new NotFoundHttpException('User not found'); } - return $user->getImage(); + return new GetUserImageResult(image: $userObj->getImage()); } } diff --git a/src/Handler/User/GetUserImage/GetUserImagePayload.php b/src/Handler/User/GetUserImage/GetUserImagePayload.php new file mode 100644 index 00000000..1a482473 --- /dev/null +++ b/src/Handler/User/GetUserImage/GetUserImagePayload.php @@ -0,0 +1,35 @@ +query->has('id') ? $request->query->getInt('id') : null, + ); + } +} diff --git a/src/Handler/User/GetUserImage/GetUserImageResult.php b/src/Handler/User/GetUserImage/GetUserImageResult.php new file mode 100644 index 00000000..d40ccf25 --- /dev/null +++ b/src/Handler/User/GetUserImage/GetUserImageResult.php @@ -0,0 +1,27 @@ +setCondition('parentId = ?', $parentId); + $list->setCondition('parentId = ?', $payload->node); $list->setOrder('ASC'); $list->setOrderKey('name'); $list->load(); diff --git a/src/Handler/User/GetUserTreeChildren/GetUserTreeChildrenPayload.php b/src/Handler/User/GetUserTreeChildren/GetUserTreeChildrenPayload.php new file mode 100644 index 00000000..202a7dc4 --- /dev/null +++ b/src/Handler/User/GetUserTreeChildren/GetUserTreeChildrenPayload.php @@ -0,0 +1,35 @@ +query->getInt('node'), + ); + } +} diff --git a/src/Handler/User/GetUsersHandler.php b/src/Handler/User/GetUsers/GetUsersHandler.php similarity index 83% rename from src/Handler/User/GetUsersHandler.php rename to src/Handler/User/GetUsers/GetUsersHandler.php index d60d304a..fbdd4c63 100644 --- a/src/Handler/User/GetUsersHandler.php +++ b/src/Handler/User/GetUsers/GetUsersHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\GetUsers; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\User; @@ -24,14 +24,14 @@ final class GetUsersHandler { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke(bool $includeCurrentUser, ?string $permission): array + public function __invoke(GetUsersPayload $payload): array { $currentUserId = (int) $this->userContext->getAdminUser()?->getId(); $list = new User\Listing(); $conditions = ['type = "user"']; - if (!$includeCurrentUser) { + if (!$payload->includeCurrentUser) { $conditions[] = 'id != ' . $currentUserId; } @@ -40,7 +40,7 @@ public function __invoke(bool $includeCurrentUser, ?string $permission): array $users = []; foreach ($list->getUsers() as $user) { - if (!$permission || $user->isAllowed($permission)) { + if (!$payload->permission || $user->isAllowed($payload->permission)) { $users[] = [ 'id' => $user->getId(), 'label' => $user->getUsername(), diff --git a/src/Handler/User/GetUsers/GetUsersPayload.php b/src/Handler/User/GetUsers/GetUsersPayload.php new file mode 100644 index 00000000..24b3d558 --- /dev/null +++ b/src/Handler/User/GetUsers/GetUsersPayload.php @@ -0,0 +1,39 @@ +query->has('include_current_user') + ? $request->query->get('include_current_user') === '1' + : false, + permission: $request->query->get('permission'), + ); + } +} diff --git a/src/Handler/User/Reset2FaSecretHandler.php b/src/Handler/User/Reset2FaSecret/Reset2FaSecretHandler.php similarity index 83% rename from src/Handler/User/Reset2FaSecretHandler.php rename to src/Handler/User/Reset2FaSecret/Reset2FaSecretHandler.php index b767e6b7..af03b01d 100644 --- a/src/Handler/User/Reset2FaSecretHandler.php +++ b/src/Handler/User/Reset2FaSecret/Reset2FaSecretHandler.php @@ -15,16 +15,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\Reset2FaSecret; use OpenDxp\Model\User; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class Reset2FaSecretHandler { - public function __invoke(int $id): void + public function __invoke(Reset2FaSecretPayload $payload): void { - $user = User::getById($id); + $user = User::getById($payload->id); if (!$user) { throw new NotFoundHttpException('User not found'); } diff --git a/src/Handler/User/Reset2FaSecret/Reset2FaSecretPayload.php b/src/Handler/User/Reset2FaSecret/Reset2FaSecretPayload.php new file mode 100644 index 00000000..7f047f9d --- /dev/null +++ b/src/Handler/User/Reset2FaSecret/Reset2FaSecretPayload.php @@ -0,0 +1,35 @@ +request->getInt('id'), + ); + } +} diff --git a/src/Handler/User/ResetMy2FaSecretHandler.php b/src/Handler/User/ResetMy2FaSecret/ResetMy2FaSecretHandler.php similarity index 82% rename from src/Handler/User/ResetMy2FaSecretHandler.php rename to src/Handler/User/ResetMy2FaSecret/ResetMy2FaSecretHandler.php index 50f6df92..5d4b26d9 100644 --- a/src/Handler/User/ResetMy2FaSecretHandler.php +++ b/src/Handler/User/ResetMy2FaSecret/ResetMy2FaSecretHandler.php @@ -14,15 +14,16 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\ResetMy2FaSecret; +use OpenDxp\Bundle\AdminBundle\Payload\Common\EmptyPayload; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; final class ResetMy2FaSecretHandler { public function __construct(private readonly AdminUserContextInterface $userContext) {} - public function __invoke(): void + public function __invoke(EmptyPayload $payload): void { $user = $this->userContext->getAdminUser(); $user->setTwoFactorAuthentication('required', true); diff --git a/src/Handler/User/SearchUsersHandler.php b/src/Handler/User/SearchUsers/SearchUsersHandler.php similarity index 78% rename from src/Handler/User/SearchUsersHandler.php rename to src/Handler/User/SearchUsers/SearchUsersHandler.php index 23be879b..ad740e6b 100644 --- a/src/Handler/User/SearchUsersHandler.php +++ b/src/Handler/User/SearchUsers/SearchUsersHandler.php @@ -15,24 +15,24 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\SearchUsers; use OpenDxp\Model\User; final class SearchUsersHandler { - public function __invoke(?string $query): array + public function __invoke(SearchUsersPayload $payload): array { - $q = '%' . $query . '%'; + $q = '%' . $payload->query . '%'; $list = new User\Listing(); - $list->setCondition('name LIKE ? OR firstname LIKE ? OR lastname LIKE ? OR email LIKE ? OR id = ?', [$q, $q, $q, $q, $query]); + $list->setCondition('name LIKE ? OR firstname LIKE ? OR lastname LIKE ? OR email LIKE ? OR id = ?', [$q, $q, $q, $q, $payload->query]); $list->setOrder('ASC'); $list->setOrderKey('name'); $users = []; foreach ($list->getUsers() as $user) { - if ($user->getId() && $user->getName() !== 'system') { + if ($user instanceof User && $user->getName() !== 'system') { $users[] = [ 'id' => $user->getId(), 'name' => $user->getName(), diff --git a/src/Handler/User/SearchUsers/SearchUsersPayload.php b/src/Handler/User/SearchUsers/SearchUsersPayload.php new file mode 100644 index 00000000..9e13e361 --- /dev/null +++ b/src/Handler/User/SearchUsers/SearchUsersPayload.php @@ -0,0 +1,35 @@ +query->get('query'), + ); + } +} diff --git a/src/Handler/User/SendInvitationLinkHandler.php b/src/Handler/User/SendInvitationLink/SendInvitationLinkHandler.php similarity index 83% rename from src/Handler/User/SendInvitationLinkHandler.php rename to src/Handler/User/SendInvitationLink/SendInvitationLinkHandler.php index 8ee110bc..9a4e79b5 100644 --- a/src/Handler/User/SendInvitationLinkHandler.php +++ b/src/Handler/User/SendInvitationLink/SendInvitationLinkHandler.php @@ -15,12 +15,14 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\SendInvitationLink; use Exception; use OpenDxp\Bundle\AdminBundle\Service\CustomLoginUrlGenerator; +use OpenDxp\Http\Request\Host\GeneralHostResolver; use OpenDxp\Model\User; use OpenDxp\Tool; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\RouterInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -30,18 +32,20 @@ public function __construct( private readonly TranslatorInterface $translator, private readonly CustomLoginUrlGenerator $loginUrlGenerator, private readonly RouterInterface $router, + private readonly GeneralHostResolver $generalHostResolver, + private readonly RequestStack $requestStack, ) {} - public function __invoke(string $username, string $domain): SendInvitationLinkResult + public function __invoke(SendInvitationLinkPayload $payload): SendInvitationLinkResult { $success = false; $message = ''; - if (!$username) { + if (!$payload->username) { return new SendInvitationLinkResult(success: false, message: $message); } - $user = User::getByName($username); + $user = User::getByName($payload->username); if (!$user instanceof User) { return new SendInvitationLinkResult(success: false, message: 'User unknown
'); } @@ -55,6 +59,8 @@ public function __invoke(string $username, string $domain): SendInvitationLinkRe } if (empty($message)) { + $domain = $this->generalHostResolver->resolve(['source' => $this->requestStack->getCurrentRequest()]) ?? ''; + if (!$domain) { return new SendInvitationLinkResult(success: false, message: 'No main domain set in system settings, unable to generate login invitation link'); } diff --git a/src/Handler/User/SendInvitationLink/SendInvitationLinkPayload.php b/src/Handler/User/SendInvitationLink/SendInvitationLinkPayload.php new file mode 100644 index 00000000..86fea4e2 --- /dev/null +++ b/src/Handler/User/SendInvitationLink/SendInvitationLinkPayload.php @@ -0,0 +1,35 @@ +request->get('username', ''), + ); + } +} diff --git a/src/Handler/User/SendInvitationLinkResult.php b/src/Handler/User/SendInvitationLink/SendInvitationLinkResult.php similarity index 86% rename from src/Handler/User/SendInvitationLinkResult.php rename to src/Handler/User/SendInvitationLink/SendInvitationLinkResult.php index 4e53d56b..17c2c7fa 100644 --- a/src/Handler/User/SendInvitationLinkResult.php +++ b/src/Handler/User/SendInvitationLink/SendInvitationLinkResult.php @@ -15,12 +15,12 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\SendInvitationLink; final readonly class SendInvitationLinkResult { public function __construct( - public bool $success, + public bool $success, public string $message, ) {} } diff --git a/src/Handler/User/UpdateCurrentUserHandler.php b/src/Handler/User/UpdateCurrentUser/UpdateCurrentUserHandler.php similarity index 87% rename from src/Handler/User/UpdateCurrentUserHandler.php rename to src/Handler/User/UpdateCurrentUser/UpdateCurrentUserHandler.php index 0006a08b..ba360391 100644 --- a/src/Handler/User/UpdateCurrentUserHandler.php +++ b/src/Handler/User/UpdateCurrentUser/UpdateCurrentUserHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\UpdateCurrentUser; use Exception; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -36,22 +36,19 @@ public function __construct( * @throws Exception * @throws BadRequestHttpException */ - public function __invoke( - int $requestedUserId, - array $values, - bool $isPasswordReset, - ?string $keyBindingsJson, - ): void { + public function __invoke(UpdateCurrentUserPayload $payload): void + { $user = $this->userContext->getAdminUser(); - if ($user === null || $user->getId() !== $requestedUserId) { + if ($user === null || $user->getId() !== $payload->requestedUserId) { throw new BadRequestHttpException('User ID mismatch'); } + $values = $payload->values; unset($values['name'], $values['id'], $values['admin'], $values['permissions'], $values['roles'], $values['active']); if (!empty($values['new_password'])) { $oldPasswordCheck = false; - if ($isPasswordReset) { + if ($payload->isPasswordReset) { $oldPasswordCheck = true; } elseif (!empty($values['old_password'])) { $errors = $this->validator->validate($values['old_password'], [new UserPassword()]); @@ -82,8 +79,8 @@ public function __invoke( $user->setValues($values); - if ($keyBindingsJson !== null) { - $keyBindings = json_decode($keyBindingsJson, true); + if ($payload->keyBindingsJson !== null) { + $keyBindings = json_decode($payload->keyBindingsJson, true); $tmpArray = []; foreach ($keyBindings as $item) { $tmpArray[] = json_decode($item, true); diff --git a/src/Handler/User/UpdateCurrentUser/UpdateCurrentUserPayload.php b/src/Handler/User/UpdateCurrentUser/UpdateCurrentUserPayload.php new file mode 100644 index 00000000..87b29821 --- /dev/null +++ b/src/Handler/User/UpdateCurrentUser/UpdateCurrentUserPayload.php @@ -0,0 +1,41 @@ +request->get('id'), + values: json_decode($request->request->get('data'), true), + isPasswordReset: \OpenDxp\Tool\Session::useBag($request->getSession(), static fn (AttributeBagInterface $adminSession) => (bool) $adminSession->get('password_reset')), + keyBindingsJson: $request->request->has('keyBindings') ? $request->request->get('keyBindings') : null, + ); + } +} diff --git a/src/Handler/User/UpdateUserHandler.php b/src/Handler/User/UpdateUser/UpdateUserHandler.php similarity index 90% rename from src/Handler/User/UpdateUserHandler.php rename to src/Handler/User/UpdateUser/UpdateUserHandler.php index b92c49f1..6fc409d1 100644 --- a/src/Handler/User/UpdateUserHandler.php +++ b/src/Handler/User/UpdateUser/UpdateUserHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\UpdateUser; use Exception; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; @@ -36,17 +36,13 @@ public function __construct( /** * @throws Exception */ - public function __invoke( - int $id, - ?array $values, - ?array $workspaces, - ?string $keyBindingsJson, - ): void { + public function __invoke(UpdateUserPayload $payload): void + { $adminUser = $this->userContext->getAdminUser(); $currentUserIsAdmin = $adminUser?->isAdmin() ?? false; /** @var User\UserRole|null $user */ - $user = User\UserRole::getById($id); + $user = User\UserRole::getById($payload->id); if (!$user) { throw new NotFoundHttpException('User not found'); } @@ -54,7 +50,8 @@ public function __invoke( if ($user instanceof User && $user->isAdmin() && !$currentUserIsAdmin) { throw new AccessDeniedHttpException('Only admin users are allowed to modify admin users'); } - if ($values !== null) { + if ($payload->values !== null) { + $values = $payload->values; if (!empty($values['password'])) { if (strlen($values['password']) < 10) { throw new Exception('Passwords have to be at least 10 characters long'); @@ -93,9 +90,9 @@ public function __invoke( } // check for workspaces - if ($workspaces !== null) { + if ($payload->workspaces !== null) { $processedPaths = ['object' => [], 'asset' => [], 'document' => []]; - foreach ($workspaces as $type => $spaces) { + foreach ($payload->workspaces as $type => $spaces) { $newWorkspaces = []; foreach ($spaces as $space) { if (in_array($space['path'], $processedPaths[$type])) { @@ -121,8 +118,8 @@ public function __invoke( } } - if ($user instanceof User && $keyBindingsJson !== null) { - $keyBindings = json_decode($keyBindingsJson, true); + if ($user instanceof User && $payload->keyBindingsJson !== null) { + $keyBindings = json_decode($payload->keyBindingsJson, true); $tmpArray = []; foreach ($keyBindings as $item) { $tmpArray[] = json_decode($item, true); diff --git a/src/Handler/User/UpdateUser/UpdateUserPayload.php b/src/Handler/User/UpdateUser/UpdateUserPayload.php new file mode 100644 index 00000000..382bccd5 --- /dev/null +++ b/src/Handler/User/UpdateUser/UpdateUserPayload.php @@ -0,0 +1,47 @@ +request->getInt('id'), + values: $request->request->has('data') + ? (json_decode($request->request->get('data'), true) ?? null) + : null, + workspaces: $request->request->has('workspaces') + ? (json_decode($request->request->get('workspaces'), true) ?? null) + : null, + keyBindingsJson: $request->request->has('keyBindings') + ? $request->request->get('keyBindings') + : null, + ); + } +} diff --git a/src/Handler/User/UploadUserImageHandler.php b/src/Handler/User/UploadUserImage/UploadUserImageHandler.php similarity index 81% rename from src/Handler/User/UploadUserImageHandler.php rename to src/Handler/User/UploadUserImage/UploadUserImageHandler.php index 4e28a1f1..091cee27 100644 --- a/src/Handler/User/UploadUserImageHandler.php +++ b/src/Handler/User/UploadUserImage/UploadUserImageHandler.php @@ -15,12 +15,11 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\User; +namespace OpenDxp\Bundle\AdminBundle\Handler\User\UploadUserImage; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Asset; use OpenDxp\Model\User; -use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -32,10 +31,10 @@ public function __construct(private readonly AdminUserContextInterface $userCont /** * @throws BadRequestHttpException */ - public function __invoke(?int $targetUserId, UploadedFile $avatarFile): void + public function __invoke(UploadUserImagePayload $payload): void { $adminUser = $this->userContext->getAdminUser(); - $targetUserId ??= (int) $adminUser?->getId(); + $targetUserId = $payload->targetUserId ?? (int) $adminUser?->getId(); $userObj = User::getById($targetUserId); if (!$userObj) { @@ -51,11 +50,11 @@ public function __invoke(?int $targetUserId, UploadedFile $avatarFile): void } } - $assetType = Asset::getTypeFromMimeMapping($avatarFile->getMimeType(), $avatarFile->getFileName()); + $assetType = Asset::getTypeFromMimeMapping($payload->avatarFile->getMimeType(), $payload->avatarFile->getFileName()); if ($assetType !== 'image') { throw new BadRequestHttpException('Unsupported file format.'); } - $userObj->setImage($avatarFile->getPathname()); + $userObj->setImage($payload->avatarFile->getPathname()); } } diff --git a/src/Handler/User/UploadUserImage/UploadUserImagePayload.php b/src/Handler/User/UploadUserImage/UploadUserImagePayload.php new file mode 100644 index 00000000..f973b68f --- /dev/null +++ b/src/Handler/User/UploadUserImage/UploadUserImagePayload.php @@ -0,0 +1,37 @@ +query->has('id') ? $request->query->getInt('id') : null, + avatarFile: $request->files->get('Filedata'), + ); + } +} diff --git a/src/Handler/Workflow/GetModalCustomHtmlHandler.php b/src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlHandler.php similarity index 81% rename from src/Handler/Workflow/GetModalCustomHtmlHandler.php rename to src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlHandler.php index 603d602e..6318a7ea 100644 --- a/src/Handler/Workflow/GetModalCustomHtmlHandler.php +++ b/src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetModalCustomHtml; use OpenDxp\Bundle\AdminBundle\Service\Workflow\WorkflowElementResolver; use OpenDxp\Model\Asset; @@ -35,29 +35,24 @@ public function __construct( private readonly WorkflowElementResolver $elementResolver, ) {} - public function __invoke( - string $ctype, - int $cid, - string $workflowName, - string $transition, - bool $isGlobalAction, - ): GetModalCustomHtmlResult { - $element = $this->elementResolver->resolve($ctype, $cid); + public function __invoke(GetModalCustomHtmlPayload $payload): GetModalCustomHtmlResult + { + $element = $this->elementResolver->resolve($payload->ctype, $payload->cid); - $workflow = $this->workflowRegistry->get($element, $workflowName); + $workflow = $this->workflowRegistry->get($element, $payload->workflowName); - if ($isGlobalAction) { - $globalAction = $this->workflowManager->getGlobalAction($workflow->getName(), $transition); + if ($payload->isGlobalAction) { + $globalAction = $this->workflowManager->getGlobalAction($workflow->getName(), $payload->transition); if ($globalAction) { return new GetModalCustomHtmlResult( customHtml: $this->buildCustomHtml($globalAction->getCustomHtmlService(), $element), ); } - } elseif ($workflow->can($element, $transition)) { + } elseif ($workflow->can($element, $payload->transition)) { $enabledTransitions = $workflow->getEnabledTransitions($element); $matchedTransition = null; foreach ($enabledTransitions as $_transition) { - if ($_transition->getName() === $transition) { + if ($_transition->getName() === $payload->transition) { $matchedTransition = $_transition; } } diff --git a/src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlPayload.php b/src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlPayload.php new file mode 100644 index 00000000..619256b9 --- /dev/null +++ b/src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlPayload.php @@ -0,0 +1,42 @@ +request->has('ctype') ? $request->request->getString('ctype') : $request->query->getString('ctype'), + cid: (int) ($request->request->has('cid') ? $request->request->getString('cid') : $request->query->getString('cid')), + workflowName: $request->request->getString('workflowName'), + transition: $request->request->getString('transition'), + isGlobalAction: $request->request->getString('isGlobalAction') === 'true', + ); + } +} diff --git a/src/Handler/Workflow/GetModalCustomHtmlResult.php b/src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlResult.php similarity index 89% rename from src/Handler/Workflow/GetModalCustomHtmlResult.php rename to src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlResult.php index 92886821..55a3458a 100644 --- a/src/Handler/Workflow/GetModalCustomHtmlResult.php +++ b/src/Handler/Workflow/GetModalCustomHtml/GetModalCustomHtmlResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetModalCustomHtml; final readonly class GetModalCustomHtmlResult { diff --git a/src/Handler/Workflow/GetWorkflowDetailsHandler.php b/src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsHandler.php similarity index 92% rename from src/Handler/Workflow/GetWorkflowDetailsHandler.php rename to src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsHandler.php index 6aeee224..dd0fd98c 100644 --- a/src/Handler/Workflow/GetWorkflowDetailsHandler.php +++ b/src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowDetails; use InvalidArgumentException; use OpenDxp\Bundle\AdminBundle\Service\Workflow\ActionsButtonService; @@ -42,9 +42,9 @@ public function __construct( private readonly WorkflowElementResolver $elementResolver, ) {} - public function __invoke(string $ctype, int $cid): GetWorkflowDetailsResult + public function __invoke(GetWorkflowDetailsPayload $payload): GetWorkflowDetailsResult { - $element = $this->elementResolver->resolve($ctype, $cid); + $element = $this->elementResolver->resolve($payload->ctype, $payload->cid); $data = []; @@ -63,8 +63,8 @@ public function __invoke(string $ctype, int $cid): GetWorkflowDetailsResult $url = $this->router->generate( 'opendxp_admin_workflow_show_graph', [ - 'cid' => $cid, - 'ctype' => $ctype, + 'cid' => $payload->cid, + 'ctype' => $payload->ctype, 'workflow' => $workflow->getName(), ] ); diff --git a/src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsPayload.php b/src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsPayload.php new file mode 100644 index 00000000..5a8a9509 --- /dev/null +++ b/src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsPayload.php @@ -0,0 +1,36 @@ +query->getString('ctype'), + cid: $request->query->getInt('cid'), + ); + } +} diff --git a/src/Handler/Workflow/GetWorkflowDetailsResult.php b/src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsResult.php similarity index 89% rename from src/Handler/Workflow/GetWorkflowDetailsResult.php rename to src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsResult.php index 87de7055..20bbbdd9 100644 --- a/src/Handler/Workflow/GetWorkflowDetailsResult.php +++ b/src/Handler/Workflow/GetWorkflowDetails/GetWorkflowDetailsResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowDetails; final readonly class GetWorkflowDetailsResult { diff --git a/src/Handler/Workflow/GetWorkflowFormHandler.php b/src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormHandler.php similarity index 82% rename from src/Handler/Workflow/GetWorkflowFormHandler.php rename to src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormHandler.php index 55cde8be..872473d1 100644 --- a/src/Handler/Workflow/GetWorkflowFormHandler.php +++ b/src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowForm; use OpenDxp\Bundle\AdminBundle\Service\Workflow\WorkflowElementResolver; use OpenDxp\Workflow\Manager; @@ -28,15 +28,11 @@ public function __construct( private readonly WorkflowElementResolver $elementResolver, ) {} - public function __invoke( - string $ctype, - int $cid, - string $workflowName, - string $transitionName, - ): GetWorkflowFormResult { - $element = $this->elementResolver->resolve($ctype, $cid); + public function __invoke(GetWorkflowFormPayload $payload): GetWorkflowFormResult + { + $element = $this->elementResolver->resolve($payload->ctype, $payload->cid); - $workflow = $this->workflowManager->getWorkflowIfExists($element, $workflowName); + $workflow = $this->workflowManager->getWorkflowIfExists($element, $payload->workflowName); if (empty($workflow)) { return new GetWorkflowFormResult( @@ -50,14 +46,14 @@ public function __invoke( $enabledTransitions = $workflow->getEnabledTransitions($element); $transition = null; foreach ($enabledTransitions as $_transition) { - if ($_transition->getName() === $transitionName) { + if ($_transition->getName() === $payload->transitionName) { $transition = $_transition; } } if (!$transition instanceof Transition) { return new GetWorkflowFormResult( - message: sprintf('transition %s currently not allowed', $transitionName), + message: sprintf('transition %s currently not allowed', $payload->transitionName), notesEnabled: false, notesRequired: false, additionalFields: [], diff --git a/src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormPayload.php b/src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormPayload.php new file mode 100644 index 00000000..c4992e57 --- /dev/null +++ b/src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormPayload.php @@ -0,0 +1,40 @@ +request->has('ctype') ? $request->request->getString('ctype') : $request->query->getString('ctype'), + cid: (int) ($request->request->has('cid') ? $request->request->getString('cid') : $request->query->getString('cid')), + workflowName: $request->request->getString('workflowName'), + transitionName: $request->request->getString('transitionName'), + ); + } +} diff --git a/src/Handler/Workflow/GetWorkflowFormResult.php b/src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormResult.php similarity index 91% rename from src/Handler/Workflow/GetWorkflowFormResult.php rename to src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormResult.php index 12cb4666..7dc7e369 100644 --- a/src/Handler/Workflow/GetWorkflowFormResult.php +++ b/src/Handler/Workflow/GetWorkflowForm/GetWorkflowFormResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\GetWorkflowForm; final readonly class GetWorkflowFormResult { diff --git a/src/Handler/Workflow/GetWorkflowSvgHandler.php b/src/Handler/Workflow/ShowGraph/GetWorkflowSvg/GetWorkflowSvgHandler.php similarity index 86% rename from src/Handler/Workflow/GetWorkflowSvgHandler.php rename to src/Handler/Workflow/ShowGraph/GetWorkflowSvg/GetWorkflowSvgHandler.php index 09e71ba5..47f8440c 100644 --- a/src/Handler/Workflow/GetWorkflowSvgHandler.php +++ b/src/Handler/Workflow/ShowGraph/GetWorkflowSvg/GetWorkflowSvgHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\ShowGraph\GetWorkflowSvg; use InvalidArgumentException; use OpenDxp\Bundle\AdminBundle\Service\Workflow\WorkflowElementResolver; @@ -35,11 +35,11 @@ public function __construct( /** * @throws InvalidArgumentException */ - public function __invoke(string $ctype, int $cid, ?string $workflowName): string + public function __invoke(ShowGraphPayload $payload): string { - $element = $this->elementResolver->resolve($ctype, $cid); + $element = $this->elementResolver->resolve($payload->ctype, $payload->cid); - $workflow = $this->workflowManager->getWorkflowByName($workflowName); + $workflow = $this->workflowManager->getWorkflowByName($payload->workflowName); $marking = $workflow->getMarking($element); $php = Console::getExecutable('php'); diff --git a/src/Handler/Workflow/ShowGraph/ShowGraphPayload.php b/src/Handler/Workflow/ShowGraph/ShowGraphPayload.php new file mode 100644 index 00000000..21abcf48 --- /dev/null +++ b/src/Handler/Workflow/ShowGraph/ShowGraphPayload.php @@ -0,0 +1,38 @@ +query->getString('ctype'), + cid: $request->query->getInt('cid'), + workflowName: $request->query->has('workflowName') ? $request->query->getString('workflowName') : null, + ); + } +} diff --git a/src/Handler/Workflow/SubmitGlobalActionHandler.php b/src/Handler/Workflow/SubmitGlobalAction/SubmitGlobalActionHandler.php similarity index 73% rename from src/Handler/Workflow/SubmitGlobalActionHandler.php rename to src/Handler/Workflow/SubmitGlobalAction/SubmitGlobalActionHandler.php index 6a167e79..b61d602d 100644 --- a/src/Handler/Workflow/SubmitGlobalActionHandler.php +++ b/src/Handler/Workflow/SubmitGlobalAction/SubmitGlobalActionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitGlobalAction; use OpenDxp\Bundle\AdminBundle\Service\Workflow\WorkflowElementResolver; use OpenDxp\Model\Element\ValidationException; @@ -34,20 +34,15 @@ public function __construct( * @throws ValidationException * @throws \RuntimeException */ - public function __invoke( - string $ctype, - int $cid, - string $workflowName, - string $transition, - array $workflowOptions, - ): void { - $element = $this->elementResolver->resolve($ctype, $cid); - - $workflow = $this->workflowRegistry->get($element, $workflowName); - - $globalAction = $this->workflowManager->getGlobalAction($workflowName, $transition); + public function __invoke(SubmitGlobalActionPayload $payload): void + { + $element = $this->elementResolver->resolve($payload->ctype, $payload->cid); + + $workflow = $this->workflowRegistry->get($element, $payload->workflowName); + + $globalAction = $this->workflowManager->getGlobalAction($payload->workflowName, $payload->transition); $saveSubject = !$globalAction || $globalAction->getSaveSubject(); - $this->workflowManager->applyGlobalAction($workflow, $element, $transition, $workflowOptions, $saveSubject); + $this->workflowManager->applyGlobalAction($workflow, $element, $payload->transition, $payload->workflowOptions, $saveSubject); } } diff --git a/src/Handler/Workflow/SubmitGlobalAction/SubmitGlobalActionPayload.php b/src/Handler/Workflow/SubmitGlobalAction/SubmitGlobalActionPayload.php new file mode 100644 index 00000000..b72a1b59 --- /dev/null +++ b/src/Handler/Workflow/SubmitGlobalAction/SubmitGlobalActionPayload.php @@ -0,0 +1,42 @@ +request->has('ctype') ? $request->request->getString('ctype') : $request->query->getString('ctype'), + cid: (int) ($request->request->has('cid') ? $request->request->getString('cid') : $request->query->getString('cid')), + workflowName: $request->request->getString('workflowName'), + transition: $request->request->getString('transition'), + workflowOptions: $request->request->all('workflow'), + ); + } +} diff --git a/src/Handler/Workflow/SubmitWorkflowTransitionHandler.php b/src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionHandler.php similarity index 74% rename from src/Handler/Workflow/SubmitWorkflowTransitionHandler.php rename to src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionHandler.php index 46d6a8e3..0328aca0 100644 --- a/src/Handler/Workflow/SubmitWorkflowTransitionHandler.php +++ b/src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionHandler.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitWorkflowTransition; use OpenDxp\Bundle\AdminBundle\Service\Workflow\WorkflowElementResolver; use OpenDxp\Model\Element\ValidationException; @@ -34,19 +34,14 @@ public function __construct( * @throws ValidationException * @throws \RuntimeException */ - public function __invoke( - string $ctype, - int $cid, - string $workflowName, - string $transition, - array $workflowOptions, - ): SubmitWorkflowTransitionResult { - $element = $this->elementResolver->resolve($ctype, $cid); - - $workflow = $this->workflowRegistry->get($element, $workflowName); - - if (!$workflow->can($element, $transition)) { - $blockTransitionList = $workflow->buildTransitionBlockerList($element, $transition); + public function __invoke(SubmitWorkflowTransitionPayload $payload): SubmitWorkflowTransitionResult + { + $element = $this->elementResolver->resolve($payload->ctype, $payload->cid); + + $workflow = $this->workflowRegistry->get($element, $payload->workflowName); + + if (!$workflow->can($element, $payload->transition)) { + $blockTransitionList = $workflow->buildTransitionBlockerList($element, $payload->transition); $reasons = array_map( static fn ($item) => $item->getMessage(), iterator_to_array($blockTransitionList->getIterator(), true) @@ -55,7 +50,7 @@ public function __invoke( return new SubmitWorkflowTransitionResult(blocked: true, blockerReasons: $reasons); } - $this->workflowManager->applyWithAdditionalData($workflow, $element, $transition, $workflowOptions, true); + $this->workflowManager->applyWithAdditionalData($workflow, $element, $payload->transition, $payload->workflowOptions, true); return new SubmitWorkflowTransitionResult(blocked: false, blockerReasons: []); } diff --git a/src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionPayload.php b/src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionPayload.php new file mode 100644 index 00000000..08c79b3e --- /dev/null +++ b/src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionPayload.php @@ -0,0 +1,42 @@ +request->has('ctype') ? $request->request->getString('ctype') : $request->query->getString('ctype'), + cid: (int) ($request->request->has('cid') ? $request->request->getString('cid') : $request->query->getString('cid')), + workflowName: $request->request->getString('workflowName'), + transition: $request->request->getString('transition'), + workflowOptions: $request->request->all('workflow'), + ); + } +} diff --git a/src/Handler/Workflow/SubmitWorkflowTransitionResult.php b/src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionResult.php similarity index 89% rename from src/Handler/Workflow/SubmitWorkflowTransitionResult.php rename to src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionResult.php index d0c3fe6e..d792e285 100644 --- a/src/Handler/Workflow/SubmitWorkflowTransitionResult.php +++ b/src/Handler/Workflow/SubmitWorkflowTransition/SubmitWorkflowTransitionResult.php @@ -15,7 +15,7 @@ declare(strict_types=1); -namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow; +namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\SubmitWorkflowTransition; final readonly class SubmitWorkflowTransitionResult { diff --git a/src/Normalizer/DataObject/AdminStyleNormalizer.php b/src/Normalizer/DataObject/AdminStyleNormalizer.php deleted file mode 100644 index 401a3c1e..00000000 --- a/src/Normalizer/DataObject/AdminStyleNormalizer.php +++ /dev/null @@ -1,61 +0,0 @@ -dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); - $adminStyle = $event->getAdminStyle(); - - $data['general']['iconCls'] = $adminStyle->getElementIconClass() !== false ? $adminStyle->getElementIconClass() : null; - if (!$data['general']['iconCls']) { - $data['general']['icon'] = $adminStyle->getElementIcon() !== false ? $adminStyle->getElementIcon() : null; - } else { - $data['general']['icon'] = null; - } - if ($adminStyle->getElementCssClass() !== false) { - if (!isset($data['general']['cls'])) { - $data['general']['cls'] = ''; - } - $data['general']['cls'] .= $adminStyle->getElementCssClass() . ' '; - } - $data['general']['qtipCfg'] = $adminStyle->getElementQtipConfig(); - $elementText = $adminStyle->getElementText(); - if ($elementText !== null) { - $data['general']['text'] = $elementText; - } - } -} diff --git a/src/Normalizer/DataObject/TreeStyleNormalizer.php b/src/Normalizer/DataObject/TreeStyleNormalizer.php deleted file mode 100644 index 9c68e1ba..00000000 --- a/src/Normalizer/DataObject/TreeStyleNormalizer.php +++ /dev/null @@ -1,58 +0,0 @@ -dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); - $adminStyle = $event->getAdminStyle(); - - $data['iconCls'] = $adminStyle->getElementIconClass() !== false ? $adminStyle->getElementIconClass() : null; - if (!$data['iconCls']) { - $data['icon'] = $adminStyle->getElementIcon() !== false ? $adminStyle->getElementIcon() : null; - } else { - $data['icon'] = null; - } - if ($adminStyle->getElementCssClass() !== false) { - $data['cls'] = ($data['cls'] ?? '') . $adminStyle->getElementCssClass() . ' '; - } - $data['qtipCfg'] = $adminStyle->getElementQtipConfig(); - $elementText = $adminStyle->getElementText(); - if ($elementText !== null) { - $data['text'] = $elementText; - } - } -} diff --git a/src/Normalizer/DataObject/UserNamesNormalizer.php b/src/Normalizer/DataObject/UserNamesNormalizer.php deleted file mode 100644 index 11528dad..00000000 --- a/src/Normalizer/DataObject/UserNamesNormalizer.php +++ /dev/null @@ -1,46 +0,0 @@ -resolveUserName($element->getUserOwner()); - $modificationName = $element->getUserOwner() === $element->getUserModification() - ? $ownerName - : $this->resolveUserName($element->getUserModification()); - - $data['general']['userOwnerUsername'] = $ownerName['userName']; - $data['general']['userOwnerFullname'] = $ownerName['fullName']; - $data['general']['userModificationUsername'] = $modificationName['userName']; - $data['general']['userModificationFullname'] = $modificationName['fullName']; - } -} diff --git a/src/Normalizer/Document/AdminStyleNormalizer.php b/src/Normalizer/Document/AdminStyleNormalizer.php deleted file mode 100644 index 7df6d8ae..00000000 --- a/src/Normalizer/Document/AdminStyleNormalizer.php +++ /dev/null @@ -1,55 +0,0 @@ -dispatch($event, AdminEvents::RESOLVE_ELEMENT_ADMIN_STYLE); - $adminStyle = $event->getAdminStyle(); - - $data['iconCls'] = $adminStyle->getElementIconClass() !== false ? $adminStyle->getElementIconClass() : null; - $data['icon'] = !$data['iconCls'] && $adminStyle->getElementIcon() !== false ? $adminStyle->getElementIcon() : null; - - if ($adminStyle->getElementCssClass() !== false) { - $data['cls'] = ($data['cls'] ?? '') . $adminStyle->getElementCssClass() . ' '; - } - - $data['qtipCfg'] = $adminStyle->getElementQtipConfig(); - - $elementText = $adminStyle->getElementText(); - if ($elementText !== null) { - $data['text'] = $elementText; - } - } -} diff --git a/src/Normalizer/Document/DocumentMetaNormalizer.php b/src/Normalizer/Document/DocumentMetaNormalizer.php deleted file mode 100644 index b1ed9814..00000000 --- a/src/Normalizer/Document/DocumentMetaNormalizer.php +++ /dev/null @@ -1,59 +0,0 @@ -getId(), ['force' => true]); - - $data['versionDate'] = $fresh->getModificationDate(); - $data['userPermissions'] = $element->getUserPermissions(); - $data['idPath'] = ElementService::getIdPath($element); - $data['php'] = [ - 'classes' => [$element::class, ...array_values(class_parents($element))], - 'interfaces' => array_values(class_implements($element)), - ]; - } -} diff --git a/src/Normalizer/Document/DraftNormalizer.php b/src/Normalizer/Document/DraftNormalizer.php deleted file mode 100644 index 7eb66345..00000000 --- a/src/Normalizer/Document/DraftNormalizer.php +++ /dev/null @@ -1,52 +0,0 @@ -getId(), ['force' => true]); - if ($fresh->getModificationDate() < $draftVersion->getDate()) { - $data['draft'] = [ - 'id' => $draftVersion->getId(), - 'modificationDate' => $draftVersion->getDate(), - 'isAutoSave' => $draftVersion->isAutoSave(), - ]; - } - } -} diff --git a/src/Normalizer/Document/PropertiesNormalizer.php b/src/Normalizer/Document/PropertiesNormalizer.php deleted file mode 100644 index eedcd816..00000000 --- a/src/Normalizer/Document/PropertiesNormalizer.php +++ /dev/null @@ -1,51 +0,0 @@ -getProperties()); - } -} diff --git a/src/Normalizer/Document/TranslationNormalizer.php b/src/Normalizer/Document/TranslationNormalizer.php deleted file mode 100644 index dfd149dc..00000000 --- a/src/Normalizer/Document/TranslationNormalizer.php +++ /dev/null @@ -1,56 +0,0 @@ -getTranslations($element); - $unlinkTranslations = $service->getTranslations($element, 'unlink'); - $language = $element->getProperty('language'); - unset($translations[$language], $unlinkTranslations[$language]); - $data['translations'] = $translations; - $data['unlinkTranslations'] = $unlinkTranslations; - } -} diff --git a/src/Normalizer/Element/UserNamesNormalizer.php b/src/Normalizer/Element/UserNamesNormalizer.php deleted file mode 100644 index 54c7121d..00000000 --- a/src/Normalizer/Element/UserNamesNormalizer.php +++ /dev/null @@ -1,60 +0,0 @@ -resolveUserName($element->getUserOwner()); - $modificationName = $element->getUserOwner() === $element->getUserModification() - ? $ownerName - : $this->resolveUserName($element->getUserModification()); - - $data['userOwnerUsername'] = $ownerName['userName']; - $data['userOwnerFullname'] = $ownerName['fullName']; - $data['userModificationUsername'] = $modificationName['userName']; - $data['userModificationFullname'] = $modificationName['fullName']; - } -} diff --git a/src/Normalizer/ElementResponseNormalizer.php b/src/Normalizer/ElementResponseNormalizer.php deleted file mode 100644 index 07f87fe1..00000000 --- a/src/Normalizer/ElementResponseNormalizer.php +++ /dev/null @@ -1,35 +0,0 @@ - $normalizers */ - public function __construct(private readonly iterable $normalizers) {} - - public function normalize(ElementInterface $element, array &$data, string $handlerClass, array $context = []): void - { - foreach ($this->normalizers as $normalizer) { - if ($normalizer->supports($element, $handlerClass)) { - $normalizer->normalize($element, $data, $context); - } - } - } -} diff --git a/src/Normalizer/ElementResponseNormalizerInterface.php b/src/Normalizer/ElementResponseNormalizerInterface.php deleted file mode 100644 index 9fe5df2d..00000000 --- a/src/Normalizer/ElementResponseNormalizerInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -cache->clearAll(); diff --git a/src/Handler/Settings/ClearSymfonyCacheHandler.php b/src/Service/Cache/SymfonyCacheClearingService.php similarity index 82% rename from src/Handler/Settings/ClearSymfonyCacheHandler.php rename to src/Service/Cache/SymfonyCacheClearingService.php index 75965eab..4f4d9841 100644 --- a/src/Handler/Settings/ClearSymfonyCacheHandler.php +++ b/src/Service/Cache/SymfonyCacheClearingService.php @@ -10,19 +10,18 @@ * Full copyright and license information is available in * LICENSE.md which is distributed with this source code. * - * @copyright Copyright (c) Pimcore GmbH (https://pimcore.com) - * @copyright Modification Copyright (c) OpenDXP (https://www.opendxp.io) + * @copyright Copyright (c) OpenDXP (https://www.opendxp.io) * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Bundle\AdminBundle\Handler\Settings; +namespace OpenDxp\Service\Cache; use OpenDxp\Cache\Symfony\CacheClearer; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\KernelInterface; -final class ClearSymfonyCacheHandler +final class SymfonyCacheClearingService { public function __construct( private readonly KernelInterface $kernel, @@ -30,7 +29,7 @@ public function __construct( private readonly CacheClearer $cacheClearer, ) {} - public function __invoke(string $environment): void + public function clear(string $environment): void { if ($this->kernel->getEnvironment() === $environment) { foreach ($this->eventDispatcher->getListeners(KernelEvents::TERMINATE) as $listener) { From f572e9cd77057f8e9f3c931e507182ea829bc2b2 Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Thu, 18 Jun 2026 14:42:01 +0200 Subject: [PATCH 04/10] replace inline logic with dedicated route actions for improved maintainability (CQRS compliance) --- .../DataObject/QuantityValueController.php | 41 ++++-- .../Admin/Document/DocumentController.php | 45 ++++++- src/Controller/Admin/ElementController.php | 19 ++- src/Controller/Admin/EmailController.php | 40 ++++-- src/Controller/Admin/RecyclebinController.php | 18 ++- src/Controller/Admin/SettingsController.php | 125 +++++++++++++----- .../Admin/TranslationController.php | 45 ++++--- .../CreateTranslationHandler.php | 1 + .../DeleteTranslationHandler.php | 1 + .../GetTranslationsHandler.php | 1 + .../UpdateTranslationHandler.php | 1 + 11 files changed, 249 insertions(+), 88 deletions(-) diff --git a/src/Controller/Admin/DataObject/QuantityValueController.php b/src/Controller/Admin/DataObject/QuantityValueController.php index 6183c7f8..fe790f33 100644 --- a/src/Controller/Admin/DataObject/QuantityValueController.php +++ b/src/Controller/Admin/DataObject/QuantityValueController.php @@ -35,6 +35,7 @@ use OpenDxp\Bundle\AdminBundle\Handler\DataObject\QuantityValue\UpdateQuantityValueUnit\UpdateQuantityValueUnitHandler; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -79,20 +80,44 @@ public function unitProxyGetAction(GetQuantityValueUnitsHandler $getUnits, GetQu #[Route('/unit-proxy', name: 'unitproxy', methods: ['POST', 'PUT'])] #[IsGranted(CorePermission::QuantityValueUnits->value)] public function unitProxyAction( - QuantityValueUnitPayload $payload, - CreateQuantityValueUnitHandler $createUnit, - UpdateQuantityValueUnitHandler $updateUnit, - DeleteQuantityValueUnitHandler $deleteUnit, + Request $request, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse { + ): Response { return match ($xaction) { - 'destroy' => $this->adminJson(ApiResponse::ok(['data' => $deleteUnit($payload)->data])), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $updateUnit($payload)->data])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $createUnit($payload)->data])), + 'destroy' => $this->forward(self::class . '::unitProxyDestroyAction', [], $request->query->all()), + 'update' => $this->forward(self::class . '::unitProxyUpdateAction', [], $request->query->all()), + 'create' => $this->forward(self::class . '::unitProxyCreateAction', [], $request->query->all()), default => throw new BadRequestHttpException(), }; } + #[Route('/unit-proxy-destroy', name: 'unitproxy_destroy', methods: ['POST', 'PUT'])] + #[IsGranted(CorePermission::QuantityValueUnits->value)] + public function unitProxyDestroyAction( + QuantityValueUnitPayload $payload, + DeleteQuantityValueUnitHandler $deleteUnit, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $deleteUnit($payload)->data])); + } + + #[Route('/unit-proxy-update', name: 'unitproxy_update', methods: ['POST', 'PUT'])] + #[IsGranted(CorePermission::QuantityValueUnits->value)] + public function unitProxyUpdateAction( + QuantityValueUnitPayload $payload, + UpdateQuantityValueUnitHandler $updateUnit, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $updateUnit($payload)->data])); + } + + #[Route('/unit-proxy-create', name: 'unitproxy_create', methods: ['POST', 'PUT'])] + #[IsGranted(CorePermission::QuantityValueUnits->value)] + public function unitProxyCreateAction( + QuantityValueUnitPayload $payload, + CreateQuantityValueUnitHandler $createUnit, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $createUnit($payload)->data])); + } + #[Route('/unit-list', name: 'unitlist', methods: ['GET'])] public function unitListAction(GetQuantityValueUnitListHandler $getUnitList, GetQuantityValueUnitListPayload $payload): JsonResponse { diff --git a/src/Controller/Admin/Document/DocumentController.php b/src/Controller/Admin/Document/DocumentController.php index 5dc75892..7c684cf4 100644 --- a/src/Controller/Admin/Document/DocumentController.php +++ b/src/Controller/Admin/Document/DocumentController.php @@ -67,6 +67,7 @@ use Override; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; @@ -201,19 +202,49 @@ public function docTypesGetAction( #[IsGranted(CorePermission::DocumentTypes->value)] #[Route('/doc-types', name: 'opendxp_admin_document_document_doctypes', methods: ['PUT', 'POST', 'DELETE'])] public function docTypesAction( + Request $request, + #[MapQueryParameter] ?string $xaction = null, + ): Response + { + return match ($xaction) { + 'destroy' => $this->forward(self::class . '::docTypesDestroyAction', [], $request->query->all()), + 'update' => $this->forward(self::class . '::docTypesUpdateAction', [], $request->query->all()), + 'create' => $this->forward(self::class . '::docTypesCreateAction', [], $request->query->all()), + default => $this->adminJson(false), + }; + } + + #[IsGranted(CorePermission::Documents->value)] + #[IsGranted(CorePermission::DocumentTypes->value)] + #[Route('/doc-types-destroy', name: 'opendxp_admin_document_document_doctypes_destroy', methods: ['PUT', 'POST', 'DELETE'])] + public function docTypesDestroyAction( DocTypePayload $payload, DeleteDocTypeHandler $delete, + ): JsonResponse + { + return $this->adminJson(ApiResponse::ok(['data' => $delete($payload)->data])); + } + + #[IsGranted(CorePermission::Documents->value)] + #[IsGranted(CorePermission::DocumentTypes->value)] + #[Route('/doc-types-update', name: 'opendxp_admin_document_document_doctypes_update', methods: ['PUT', 'POST', 'DELETE'])] + public function docTypesUpdateAction( + DocTypePayload $payload, UpdateDocTypeHandler $update, + ): JsonResponse + { + return $this->adminJson(ApiResponse::ok(['data' => $update($payload)->data])); + } + + #[IsGranted(CorePermission::Documents->value)] + #[IsGranted(CorePermission::DocumentTypes->value)] + #[Route('/doc-types-create', name: 'opendxp_admin_document_document_doctypes_create', methods: ['PUT', 'POST', 'DELETE'])] + public function docTypesCreateAction( + DocTypePayload $payload, CreateDocTypeHandler $create, - #[MapQueryParameter] ?string $xaction = null, ): JsonResponse { - return match ($xaction) { - 'destroy' => $this->adminJson(ApiResponse::ok(['data' => $delete($payload)->data])), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $update($payload)->data])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $create($payload)->data])), - default => $this->adminJson(false), - }; + return $this->adminJson(ApiResponse::ok(['data' => $create($payload)->data])); } #[IsGranted(CorePermission::Documents->value)] diff --git a/src/Controller/Admin/ElementController.php b/src/Controller/Admin/ElementController.php index fa7ad2e1..d517ca8d 100644 --- a/src/Controller/Admin/ElementController.php +++ b/src/Controller/Admin/ElementController.php @@ -63,6 +63,7 @@ use OpenDxp\Bundle\AdminBundle\Handler\Element\NoteListPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -131,15 +132,15 @@ public function noteTypesAction( #[Route('/note-list', name: 'opendxp_admin_element_notelist', methods: ['POST'])] #[IsGranted(CorePermission::NotesEvents->value)] public function noteListAction( + Request $request, NoteListPayload $payload, GetNoteListHandler $getNoteList, - DeleteNoteHandler $deleteNote, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse { + ): Response { if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->handleDeleteNote($deleteNote, $payload), - default => throw new BadRequestHttpException(), + 'destroy' => $this->forward(self::class . '::noteListDestroyAction', [], $request->query->all()), + default => throw new BadRequestHttpException(), }; } @@ -151,9 +152,13 @@ public function noteListAction( ])); } - private function handleDeleteNote(DeleteNoteHandler $handler, NoteListPayload $payload): JsonResponse - { - $handler($payload); + #[Route('/note-list-destroy', name: 'opendxp_admin_element_notelist_destroy', methods: ['POST'])] + #[IsGranted(CorePermission::NotesEvents->value)] + public function noteListDestroyAction( + NoteListPayload $payload, + DeleteNoteHandler $deleteNote, + ): JsonResponse { + $deleteNote($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); } diff --git a/src/Controller/Admin/EmailController.php b/src/Controller/Admin/EmailController.php index 98f11c83..29e79dff 100644 --- a/src/Controller/Admin/EmailController.php +++ b/src/Controller/Admin/EmailController.php @@ -129,18 +129,16 @@ public function sendTestEmailAction( #[IsGranted(CorePermission::Emails->value)] #[Route('/blocklist', name: 'opendxp_admin_email_blocklist', methods: ['POST'])] public function blocklistAction( + Request $request, BlocklistPayload $payload, GetBlocklistHandler $getBlocklist, - CreateBlocklistEntryHandler $createBlocklistEntry, - UpdateBlocklistEntryHandler $updateBlocklistEntry, - DeleteBlocklistEntryHandler $deleteBlocklistEntry, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse { + ): Response { if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->destroyBlocklistEntry($deleteBlocklistEntry, $payload), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $updateBlocklistEntry($payload)])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $createBlocklistEntry($payload)])), + 'destroy' => $this->forward(self::class . '::blocklistDestroyAction', [], $request->query->all()), + 'update' => $this->forward(self::class . '::blocklistUpdateAction', [], $request->query->all()), + 'create' => $this->forward(self::class . '::blocklistCreateAction', [], $request->query->all()), default => throw new BadRequestHttpException(), }; } @@ -153,10 +151,32 @@ public function blocklistAction( ])); } - private function destroyBlocklistEntry(DeleteBlocklistEntryHandler $handler, BlocklistPayload $payload): JsonResponse - { - $handler($payload); + #[IsGranted(CorePermission::Emails->value)] + #[Route('/blocklist-destroy', name: 'opendxp_admin_email_blocklist_destroy', methods: ['POST'])] + public function blocklistDestroyAction( + BlocklistPayload $payload, + DeleteBlocklistEntryHandler $deleteBlocklistEntry, + ): JsonResponse { + $deleteBlocklistEntry($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); } + + #[IsGranted(CorePermission::Emails->value)] + #[Route('/blocklist-update', name: 'opendxp_admin_email_blocklist_update', methods: ['POST'])] + public function blocklistUpdateAction( + BlocklistPayload $payload, + UpdateBlocklistEntryHandler $updateBlocklistEntry, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $updateBlocklistEntry($payload)])); + } + + #[IsGranted(CorePermission::Emails->value)] + #[Route('/blocklist-create', name: 'opendxp_admin_email_blocklist_create', methods: ['POST'])] + public function blocklistCreateAction( + BlocklistPayload $payload, + CreateBlocklistEntryHandler $createBlocklistEntry, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $createBlocklistEntry($payload)])); + } } diff --git a/src/Controller/Admin/RecyclebinController.php b/src/Controller/Admin/RecyclebinController.php index 2101f1db..b5a77c13 100644 --- a/src/Controller/Admin/RecyclebinController.php +++ b/src/Controller/Admin/RecyclebinController.php @@ -31,6 +31,8 @@ use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use OpenDxp\Controller\KernelControllerEventInterface; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -45,14 +47,14 @@ class RecyclebinController extends AdminAbstractController implements KernelCont #[IsGranted(CorePermission::Recyclebin->value)] #[Route('/recyclebin/list', name: 'opendxp_admin_recyclebin_list', methods: ['POST'])] public function listAction( + Request $request, RecyclebinPayload $payload, ListRecyclebinHandler $listRecyclebin, - DeleteRecyclebinItemHandler $deleteRecyclebinItem, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse { + ): Response { if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->destroyRecyclebinItem($deleteRecyclebinItem, $payload), + 'destroy' => $this->forward(self::class . '::listDestroyAction', [], $request->query->all()), default => throw new BadRequestHttpException(), }; } @@ -62,9 +64,13 @@ public function listAction( return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - private function destroyRecyclebinItem(DeleteRecyclebinItemHandler $handler, RecyclebinPayload $payload): JsonResponse - { - $handler($payload); + #[IsGranted(CorePermission::Recyclebin->value)] + #[Route('/recyclebin/list-destroy', name: 'opendxp_admin_recyclebin_list_destroy', methods: ['POST'])] + public function listDestroyAction( + RecyclebinPayload $payload, + DeleteRecyclebinItemHandler $deleteRecyclebinItem, + ): JsonResponse { + $deleteRecyclebinItem($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); } diff --git a/src/Controller/Admin/SettingsController.php b/src/Controller/Admin/SettingsController.php index df03cce3..202e0882 100644 --- a/src/Controller/Admin/SettingsController.php +++ b/src/Controller/Admin/SettingsController.php @@ -115,19 +115,17 @@ public function deleteCustomLogoAction(DeleteCustomLogoHandler $handler): JsonRe #[IsGranted(CorePermission::AssetMetadata->value)] #[Route('/predefined-metadata', name: 'opendxp_admin_settings_metadata', methods: ['POST'])] public function metadataAction( + Request $request, PredefinedMetadataPayload $payload, GetPredefinedMetadataListHandler $getPredefinedMetadataList, - CreatePredefinedMetadataHandler $createPredefinedMetadata, - UpdatePredefinedMetadataHandler $updatePredefinedMetadata, - DeletePredefinedMetadataHandler $deletePredefinedMetadata, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse { + ): Response { if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->destroyPredefinedMetadata($deletePredefinedMetadata, $payload), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedMetadata($payload)->data])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $createPredefinedMetadata($payload)->data])), - default => throw new BadRequestHttpException(), + 'destroy' => $this->forward(self::class . '::metadataDestroyAction', [], $request->query->all()), + 'update' => $this->forward(self::class . '::metadataUpdateAction', [], $request->query->all()), + 'create' => $this->forward(self::class . '::metadataCreateAction', [], $request->query->all()), + default => throw new BadRequestHttpException(), }; } @@ -136,13 +134,35 @@ public function metadataAction( return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - private function destroyPredefinedMetadata(DeletePredefinedMetadataHandler $handler, PredefinedMetadataPayload $payload): JsonResponse - { - $handler($payload); + #[IsGranted(CorePermission::AssetMetadata->value)] + #[Route('/predefined-metadata-destroy', name: 'opendxp_admin_settings_metadata_destroy', methods: ['POST'])] + public function metadataDestroyAction( + PredefinedMetadataPayload $payload, + DeletePredefinedMetadataHandler $deletePredefinedMetadata, + ): JsonResponse { + $deletePredefinedMetadata($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); } + #[IsGranted(CorePermission::AssetMetadata->value)] + #[Route('/predefined-metadata-update', name: 'opendxp_admin_settings_metadata_update', methods: ['POST'])] + public function metadataUpdateAction( + PredefinedMetadataPayload $payload, + UpdatePredefinedMetadataHandler $updatePredefinedMetadata, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedMetadata($payload)->data])); + } + + #[IsGranted(CorePermission::AssetMetadata->value)] + #[Route('/predefined-metadata-create', name: 'opendxp_admin_settings_metadata_create', methods: ['POST'])] + public function metadataCreateAction( + PredefinedMetadataPayload $payload, + CreatePredefinedMetadataHandler $createPredefinedMetadata, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $createPredefinedMetadata($payload)->data])); + } + #[Route('/get-predefined-metadata', name: 'opendxp_admin_settings_getpredefinedmetadata', methods: ['GET'])] public function getPredefinedMetadataAction( GetFilteredPredefinedMetadataHandler $handler, @@ -156,19 +176,17 @@ public function getPredefinedMetadataAction( #[IsGranted(CorePermission::PredefinedProperties->value)] #[Route('/properties', name: 'opendxp_admin_settings_properties', methods: ['POST'])] public function propertiesAction( + Request $request, PredefinedPropertyPayload $payload, GetPredefinedPropertiesListHandler $getPredefinedPropertiesList, - CreatePredefinedPropertyHandler $createPredefinedProperty, - UpdatePredefinedPropertyHandler $updatePredefinedProperty, - DeletePredefinedPropertyHandler $deletePredefinedProperty, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse { + ): Response { if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->destroyPredefinedProperty($payload, $deletePredefinedProperty), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedProperty($payload)->data])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $createPredefinedProperty($payload)->data])), - default => throw new BadRequestHttpException(), + 'destroy' => $this->forward(self::class . '::propertiesDestroyAction', [], $request->query->all()), + 'update' => $this->forward(self::class . '::propertiesUpdateAction', [], $request->query->all()), + 'create' => $this->forward(self::class . '::propertiesCreateAction', [], $request->query->all()), + default => throw new BadRequestHttpException(), }; } @@ -177,16 +195,35 @@ public function propertiesAction( return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - private function destroyPredefinedProperty( + #[IsGranted(CorePermission::PredefinedProperties->value)] + #[Route('/properties-destroy', name: 'opendxp_admin_settings_properties_destroy', methods: ['POST'])] + public function propertiesDestroyAction( PredefinedPropertyPayload $payload, - DeletePredefinedPropertyHandler $handler, + DeletePredefinedPropertyHandler $deletePredefinedProperty, ): JsonResponse { - - $handler($payload); + $deletePredefinedProperty($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); } + #[IsGranted(CorePermission::PredefinedProperties->value)] + #[Route('/properties-update', name: 'opendxp_admin_settings_properties_update', methods: ['POST'])] + public function propertiesUpdateAction( + PredefinedPropertyPayload $payload, + UpdatePredefinedPropertyHandler $updatePredefinedProperty, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $updatePredefinedProperty($payload)->data])); + } + + #[IsGranted(CorePermission::PredefinedProperties->value)] + #[Route('/properties-create', name: 'opendxp_admin_settings_properties_create', methods: ['POST'])] + public function propertiesCreateAction( + PredefinedPropertyPayload $payload, + CreatePredefinedPropertyHandler $createPredefinedProperty, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $createPredefinedProperty($payload)->data])); + } + #[IsGranted(AdminPermission::SystemAppearance->value)] #[Route('/get-admin-system', name: 'opendxp_appearance_admin_settings_get', methods: ['GET'])] public function getAppearanceSystemAction(GetAppearanceSettingsHandler $handler): JsonResponse @@ -305,19 +342,17 @@ public function thumbnailAdapterCheckAction(ThumbnailAdapterCheckHandler $handle #[IsGranted(CorePermission::WebsiteSettings->value)] #[Route('/website-settings', name: 'opendxp_admin_settings_websitesettings', methods: ['POST'])] public function websiteSettingsAction( + Request $request, WebsiteSettingPayload $payload, GetWebsiteSettingsListHandler $getWebsiteSettingsList, - CreateWebsiteSettingHandler $createWebsiteSetting, - UpdateWebsiteSettingHandler $updateWebsiteSetting, - DeleteWebsiteSettingHandler $deleteWebsiteSetting, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse { + ): Response { if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->destroyWebsiteSetting($deleteWebsiteSetting, $payload), - 'update' => $this->adminJson(ApiResponse::ok(['data' => $updateWebsiteSetting($payload)->data])), - 'create' => $this->adminJson(ApiResponse::ok(['data' => $createWebsiteSetting($payload)->data])), - default => throw new BadRequestHttpException(), + 'destroy' => $this->forward(self::class . '::websiteSettingsDestroyAction', [], $request->query->all()), + 'update' => $this->forward(self::class . '::websiteSettingsUpdateAction', [], $request->query->all()), + 'create' => $this->forward(self::class . '::websiteSettingsCreateAction', [], $request->query->all()), + default => throw new BadRequestHttpException(), }; } @@ -326,13 +361,35 @@ public function websiteSettingsAction( return $this->adminJson(ApiResponse::ok(['data' => $result->data, 'total' => $result->total])); } - private function destroyWebsiteSetting(DeleteWebsiteSettingHandler $handler, WebsiteSettingPayload $payload): JsonResponse - { - $handler($payload); + #[IsGranted(CorePermission::WebsiteSettings->value)] + #[Route('/website-settings-destroy', name: 'opendxp_admin_settings_websitesettings_destroy', methods: ['POST'])] + public function websiteSettingsDestroyAction( + WebsiteSettingPayload $payload, + DeleteWebsiteSettingHandler $deleteWebsiteSetting, + ): JsonResponse { + $deleteWebsiteSetting($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); } + #[IsGranted(CorePermission::WebsiteSettings->value)] + #[Route('/website-settings-update', name: 'opendxp_admin_settings_websitesettings_update', methods: ['POST'])] + public function websiteSettingsUpdateAction( + WebsiteSettingPayload $payload, + UpdateWebsiteSettingHandler $updateWebsiteSetting, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $updateWebsiteSetting($payload)->data])); + } + + #[IsGranted(CorePermission::WebsiteSettings->value)] + #[Route('/website-settings-create', name: 'opendxp_admin_settings_websitesettings_create', methods: ['POST'])] + public function websiteSettingsCreateAction( + WebsiteSettingPayload $payload, + CreateWebsiteSettingHandler $createWebsiteSetting, + ): JsonResponse { + return $this->adminJson(ApiResponse::ok(['data' => $createWebsiteSetting($payload)->data])); + } + #[Route('/get-available-algorithms', name: 'opendxp_admin_settings_getavailablealgorithms', methods: ['GET'])] public function getAvailableAlgorithmsAction(GetAvailableAlgorithmsHandler $handler): JsonResponse { diff --git a/src/Controller/Admin/TranslationController.php b/src/Controller/Admin/TranslationController.php index 988565f3..886d2330 100644 --- a/src/Controller/Admin/TranslationController.php +++ b/src/Controller/Admin/TranslationController.php @@ -126,20 +126,18 @@ public function addAdminTranslationKeysAction( #[Route('/translations', name: 'opendxp_admin_translation_translations', methods: ['POST'])] public function translationsAction( + Request $request, TranslationPayload $payload, - DeleteTranslationHandler $deleteTranslation, - UpdateTranslationHandler $updateTranslation, - CreateTranslationHandler $createTranslation, GetTranslationsHandler $getTranslations, #[MapQueryParameter] ?string $xaction = null, - ): JsonResponse { + ): Response { $this->checkPermission(($payload->domain === Translation::DOMAIN_ADMIN ? 'admin_' : '') . 'translations'); if ($payload->hasData) { return match ($xaction) { - 'destroy' => $this->handleDestroyTranslation($deleteTranslation, $payload), - 'update' => $this->handleUpdateTranslation($updateTranslation, $payload), - 'create' => $this->handleCreateTranslation($createTranslation, $payload), + 'destroy' => $this->forward(self::class . '::translationsDestroyAction', [], $request->query->all()), + 'update' => $this->forward(self::class . '::translationsUpdateAction', [], $request->query->all()), + 'create' => $this->forward(self::class . '::translationsCreateAction', [], $request->query->all()), default => throw new BadRequestHttpException(), }; } @@ -149,16 +147,26 @@ public function translationsAction( return $this->adminJson(ApiResponse::ok(['data' => $result->translations, 'total' => $result->total])); } - private function handleDestroyTranslation(DeleteTranslationHandler $handler, TranslationPayload $payload): JsonResponse - { - $handler($payload); + #[Route('/translations-destroy', name: 'opendxp_admin_translation_translations_destroy', methods: ['POST'])] + public function translationsDestroyAction( + TranslationPayload $payload, + DeleteTranslationHandler $deleteTranslation, + ): JsonResponse { + $this->checkPermission(($payload->domain === Translation::DOMAIN_ADMIN ? 'admin_' : '') . 'translations'); + + $deleteTranslation($payload); return $this->adminJson(ApiResponse::ok(['data' => []])); } - private function handleUpdateTranslation(UpdateTranslationHandler $handler, TranslationPayload $payload): JsonResponse - { - $result = $handler($payload); + #[Route('/translations-update', name: 'opendxp_admin_translation_translations_update', methods: ['POST'])] + public function translationsUpdateAction( + TranslationPayload $payload, + UpdateTranslationHandler $updateTranslation, + ): JsonResponse { + $this->checkPermission(($payload->domain === Translation::DOMAIN_ADMIN ? 'admin_' : '') . 'translations'); + + $result = $updateTranslation($payload); return $this->adminJson(ApiResponse::ok(['data' => [ 'key' => $result->key, @@ -169,9 +177,14 @@ private function handleUpdateTranslation(UpdateTranslationHandler $handler, Tran ]])); } - private function handleCreateTranslation(CreateTranslationHandler $handler, TranslationPayload $payload): JsonResponse - { - $result = $handler($payload); + #[Route('/translations-create', name: 'opendxp_admin_translation_translations_create', methods: ['POST'])] + public function translationsCreateAction( + TranslationPayload $payload, + CreateTranslationHandler $createTranslation, + ): JsonResponse { + $this->checkPermission(($payload->domain === Translation::DOMAIN_ADMIN ? 'admin_' : '') . 'translations'); + + $result = $createTranslation($payload); return $this->adminJson(ApiResponse::ok(['data' => [ 'key' => $result->key, diff --git a/src/Handler/Translation/CreateTranslation/CreateTranslationHandler.php b/src/Handler/Translation/CreateTranslation/CreateTranslationHandler.php index 8c1f866b..849e514d 100644 --- a/src/Handler/Translation/CreateTranslation/CreateTranslationHandler.php +++ b/src/Handler/Translation/CreateTranslation/CreateTranslationHandler.php @@ -20,6 +20,7 @@ use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Translation; use OpenDxp\Tool; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationPayload; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; final class CreateTranslationHandler diff --git a/src/Handler/Translation/DeleteTranslation/DeleteTranslationHandler.php b/src/Handler/Translation/DeleteTranslation/DeleteTranslationHandler.php index 73b63247..9317c06e 100644 --- a/src/Handler/Translation/DeleteTranslation/DeleteTranslationHandler.php +++ b/src/Handler/Translation/DeleteTranslation/DeleteTranslationHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\DeleteTranslation; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationPayload; use OpenDxp\Model\Translation; final class DeleteTranslationHandler diff --git a/src/Handler/Translation/GetTranslations/GetTranslationsHandler.php b/src/Handler/Translation/GetTranslations/GetTranslationsHandler.php index 9aed091e..b3393a96 100644 --- a/src/Handler/Translation/GetTranslations/GetTranslationsHandler.php +++ b/src/Handler/Translation/GetTranslations/GetTranslationsHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\GetTranslations; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationPayload; use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationQueryTrait; use OpenDxp\Bundle\AdminBundle\Helper\QueryParams; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; diff --git a/src/Handler/Translation/UpdateTranslation/UpdateTranslationHandler.php b/src/Handler/Translation/UpdateTranslation/UpdateTranslationHandler.php index cc962202..e1195aa6 100644 --- a/src/Handler/Translation/UpdateTranslation/UpdateTranslationHandler.php +++ b/src/Handler/Translation/UpdateTranslation/UpdateTranslationHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Translation\UpdateTranslation; +use OpenDxp\Bundle\AdminBundle\Handler\Translation\TranslationPayload; use OpenDxp\Model\Translation; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; From 876067f116ba25d71b68efb2defa7dd033cbc34c Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Thu, 18 Jun 2026 16:20:53 +0200 Subject: [PATCH 05/10] align admin bundle with CQRS principles by introducing payload classes, updating type strictness, and cleaning up handlers --- .../Admin/Asset/AssetController.php | 2 +- .../Admin/Asset/AssetThumbnailController.php | 2 +- src/Controller/Admin/SettingsController.php | 6 ++-- src/Controller/Admin/User/RoleController.php | 4 +-- .../DownloadImageThumbnailResult.php | 2 +- .../UpdateProperty/UpdatePropertyHandler.php | 1 + .../CopyDataObject/CopyDataObjectResult.php | 2 +- .../SuggestCustomLayoutIdentifierHandler.php | 3 +- .../GetExportJobs/GetExportJobsHandler.php | 4 ++- .../PublishVersion/PublishVersionHandler.php | 2 +- .../Document/DocTypes/DocTypePayload.php | 4 +-- .../GetDocTypesByTypeHandler.php | 6 ++-- src/Handler/Document/Page/PagePayload.php | 1 + .../UpdateDocument/UpdateDocumentHandler.php | 20 +++++------ .../DiffVersions/DiffVersionsHandler.php | 4 +-- .../AnalyzePermissionsPayload.php | 2 +- .../DeleteDraft/DeleteDraftHandler.php | 1 + .../DeleteVersion/DeleteVersionHandler.php | 1 + .../UnlockElements/UnlockElementsHandler.php | 1 - .../Email/ResendEmail/ResendEmailHandler.php | 1 - .../RestoreRecyclebinItemHandler.php | 2 +- .../ClearOpenDxpCacheHandler.php | 2 ++ .../ClearSymfonyCacheHandler.php | 2 ++ .../DisplayCustomLogoHandler.php | 4 +-- .../DisplayCustomLogoPayload.php | 35 +++++++++++++++++++ .../GetTagsForElementHandler.php | 1 + .../GetTokenLoginLinkResult.php | 25 +++++++++++++ .../User/SearchUsers/SearchUsersHandler.php | 1 + .../GetWorkflowSvg/GetWorkflowSvgHandler.php | 1 + src/Service/Asset/AssetPayloadMapper.php | 2 +- src/Service/Element/SessionService.php | 4 +-- .../Workflow/WorkflowElementResolver.php | 2 -- .../Permissions/ModelAssetPermissionsTest.php | 5 +-- .../ModelDataObjectPermissionsTest.php | 8 ++--- .../ModelDocumentPermissionsTest.php | 7 ++-- 35 files changed, 120 insertions(+), 50 deletions(-) create mode 100644 src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoPayload.php create mode 100644 src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkResult.php diff --git a/src/Controller/Admin/Asset/AssetController.php b/src/Controller/Admin/Asset/AssetController.php index 7289441c..e0a1d7d2 100644 --- a/src/Controller/Admin/Asset/AssetController.php +++ b/src/Controller/Admin/Asset/AssetController.php @@ -21,7 +21,7 @@ use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Exception\ElementLockedException; use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnail\ClearAssetThumbnailPayload; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnailHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\ClearAssetThumbnail\ClearAssetThumbnailHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfo\GetDeleteInfoHandler; use OpenDxp\Bundle\AdminBundle\Handler\Element\GetDeleteInfo\GetDeleteInfoPayload; use OpenDxp\Bundle\AdminBundle\Handler\Asset\CreateAssetFolder\CreateAssetFolderPayload; diff --git a/src/Controller/Admin/Asset/AssetThumbnailController.php b/src/Controller/Admin/Asset/AssetThumbnailController.php index e4a0d9c6..a979324f 100644 --- a/src/Controller/Admin/Asset/AssetThumbnailController.php +++ b/src/Controller/Admin/Asset/AssetThumbnailController.php @@ -60,7 +60,7 @@ public function getImageThumbnailAction( } if ($result->thumbnailResult === null) { - throw $this->createNotFoundException(sprintf('Tree preview thumbnail not available for asset %s', $id)); + throw $this->createNotFoundException(sprintf('Tree preview thumbnail not available for asset %s', $payload->id)); } if ($result->returnFileinfo) { diff --git a/src/Controller/Admin/SettingsController.php b/src/Controller/Admin/SettingsController.php index 202e0882..429b9958 100644 --- a/src/Controller/Admin/SettingsController.php +++ b/src/Controller/Admin/SettingsController.php @@ -34,6 +34,7 @@ use OpenDxp\Bundle\AdminBundle\Handler\Settings\PredefinedPropertyPayload; use OpenDxp\Bundle\AdminBundle\Handler\Settings\WebsiteSettingPayload; use OpenDxp\Bundle\AdminBundle\Handler\Settings\DisplayCustomLogo\DisplayCustomLogoHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Settings\DisplayCustomLogo\DisplayCustomLogoPayload; use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAppearanceSettings\GetAppearanceSettingsHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAdminLanguages\GetAvailableAdminLanguagesHandler; use OpenDxp\Bundle\AdminBundle\Handler\Settings\GetAvailableAlgorithms\GetAvailableAlgorithmsHandler; @@ -74,9 +75,9 @@ class SettingsController extends AdminAbstractController { #[Route('/display-custom-logo', name: 'opendxp_settings_display_custom_logo', methods: ['GET'])] - public function displayCustomLogoAction(Request $request, DisplayCustomLogoHandler $handler): StreamedResponse + public function displayCustomLogoAction(DisplayCustomLogoPayload $payload, DisplayCustomLogoHandler $handler): StreamedResponse { - $result = $handler(white: $request->query->has('white')); + $result = $handler($payload); return new StreamedResponse(static function () use ($result): void { fpassthru($result->stream); @@ -89,7 +90,6 @@ public function displayCustomLogoAction(Request $request, DisplayCustomLogoHandl #[Route('/upload-custom-logo', name: 'opendxp_admin_settings_uploadcustomlogo', methods: ['POST'])] public function uploadCustomLogoAction(Request $request, UploadCustomLogoHandler $handler): JsonResponse { - /** @var UploadedFile $logoFile */ $logoFile = $request->files->get('Filedata'); if (!$logoFile instanceof UploadedFile) { diff --git a/src/Controller/Admin/User/RoleController.php b/src/Controller/Admin/User/RoleController.php index 9c7f3276..2426c962 100644 --- a/src/Controller/Admin/User/RoleController.php +++ b/src/Controller/Admin/User/RoleController.php @@ -20,11 +20,11 @@ use OpenDxp\Bundle\AdminBundle\Controller\AdminAbstractController; use OpenDxp\Bundle\AdminBundle\Dto\Response\ApiResponse; use OpenDxp\Bundle\AdminBundle\Handler\User\GetRole\GetRoleHandler; +use OpenDxp\Bundle\AdminBundle\Handler\User\GetRole\GetRolePayload; use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoles\GetRolesHandler; use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoles\GetRolesPayload; use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoleTreeChildren\GetRoleTreeChildrenHandler; use OpenDxp\Bundle\AdminBundle\Handler\User\GetRoleTreeChildren\GetRoleTreeChildrenPayload; -use OpenDxp\Bundle\AdminBundle\Payload\Common\IdQueryPayload; use OpenDxp\Bundle\AdminBundle\Security\Permission\CorePermission; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; @@ -47,7 +47,7 @@ public function roleTreeGetChildrenByIdAction( #[IsGranted(CorePermission::Users->value)] #[Route('/user/role-get', name: 'opendxp_admin_user_roleget', methods: ['GET'])] public function roleGetAction( - IdQueryPayload $payload, + GetRolePayload $payload, GetRoleHandler $getRole, ): JsonResponse { $result = $getRole($payload); diff --git a/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailResult.php b/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailResult.php index 21712489..5ff6213f 100644 --- a/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailResult.php +++ b/src/Handler/Asset/Download/DownloadImageThumbnail/DownloadImageThumbnailResult.php @@ -23,7 +23,7 @@ { public function __construct( public Image $image, - public Image\Thumbnail $thumbnail, + public Image\ThumbnailInterface $thumbnail, public string $localFile, public bool $deleteThumbnail, ) {} diff --git a/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyHandler.php b/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyHandler.php index 97dd5e87..d47a12d1 100644 --- a/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyHandler.php +++ b/src/Handler/DataObject/Classificationstore/UpdateProperty/UpdatePropertyHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\UpdateProperty; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\Classificationstore\GetProperties\GetPropertiesHandler; use OpenDxp\Model\DataObject\Classificationstore; final class UpdatePropertyHandler diff --git a/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectResult.php b/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectResult.php index 56d912a2..98d77270 100644 --- a/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectResult.php +++ b/src/Handler/DataObject/Copy/CopyDataObject/CopyDataObjectResult.php @@ -23,6 +23,6 @@ { public function __construct( public int $sourceId, - public ?DataObject $newObject = null, + public ?DataObject\AbstractObject $newObject = null, ) {} } diff --git a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierHandler.php b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierHandler.php index 1faade98..a70f6454 100644 --- a/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierHandler.php +++ b/src/Handler/DataObject/CustomLayout/SuggestCustomLayoutIdentifier/SuggestCustomLayoutIdentifierHandler.php @@ -17,7 +17,6 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\CustomLayout\SuggestCustomLayoutIdentifier\SuggestCustomLayoutIdentifierPayload; use OpenDxp\Model\DataObject; final class SuggestCustomLayoutIdentifierHandler @@ -36,6 +35,6 @@ public function __invoke(SuggestCustomLayoutIdentifierPayload $payload): Suggest } } - return new SuggestCustomLayoutIdentifierResult($identifier, $existingIds, $existingNames); + return new SuggestCustomLayoutIdentifierResult($identifier !== null ? (string) $identifier : '', $existingIds, $existingNames); } } diff --git a/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsHandler.php b/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsHandler.php index a7258585..5fb3a235 100644 --- a/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsHandler.php +++ b/src/Handler/DataObject/Helper/GetExportJobs/GetExportJobsHandler.php @@ -19,6 +19,7 @@ use OpenDxp\Bundle\AdminBundle\Event\AdminEvents; use OpenDxp\Bundle\AdminBundle\Helper\GridHelperService; +use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService; use OpenDxp\Tool\Storage; use Symfony\Component\EventDispatcher\GenericEvent; @@ -30,6 +31,7 @@ public function __construct( private readonly GridHelperService $gridHelperService, private readonly GridExportService $gridExportService, private readonly EventDispatcherInterface $eventDispatcher, + private readonly AdminUserContextInterface $userContext, ) {} public function __invoke(GetExportJobsPayload $payload): GetExportJobsResult @@ -42,7 +44,7 @@ public function __invoke(GetExportJobsPayload $payload): GetExportJobsResult } $allParams['fields'] = $fieldnames; - $list = $this->gridHelperService->prepareListingForGrid($allParams, $payload->requestedLanguage); + $list = $this->gridHelperService->prepareListingForGrid($allParams, $payload->requestedLanguage, $this->userContext->getAdminUser()); $beforeListPrepareEvent = new GenericEvent($this, [ 'list' => $list, diff --git a/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php b/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php index 3140c072..83b84eff 100644 --- a/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php +++ b/src/Handler/DataObject/Version/PublishVersion/PublishVersionHandler.php @@ -38,7 +38,7 @@ public function __invoke(IdBodyPayload $payload): PublishVersionResult $version = Version::getById($payload->id); $object = $version?->loadData(); - if (!$object instanceof DataObject\AbstractObject) { + if (!$object instanceof DataObject\Concrete) { throw new DataObjectNotFoundException($payload->id); } diff --git a/src/Handler/Document/DocTypes/DocTypePayload.php b/src/Handler/Document/DocTypes/DocTypePayload.php index 1f4e562b..904f6894 100644 --- a/src/Handler/Document/DocTypes/DocTypePayload.php +++ b/src/Handler/Document/DocTypes/DocTypePayload.php @@ -10,14 +10,14 @@ final readonly class DocTypePayload implements ExtJsPayloadInterface { public function __construct( - public int $id, + public string $id, public array $data, ) {} public static function fromRequest(Request $request): static { $data = json_decode($request->request->get('data', ''), true) ?? []; - $id = (int) ($data['id'] ?? 0); + $id = (string) ($data['id'] ?? ''); unset($data['id']); return new static(id: $id, data: $data); diff --git a/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeHandler.php b/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeHandler.php index 40f5012d..339c23a7 100644 --- a/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeHandler.php +++ b/src/Handler/Document/GetDocTypesByType/GetDocTypesByTypeHandler.php @@ -27,10 +27,10 @@ public function __invoke(GetDocTypesByTypePayload $payload): GetDocTypesByTypeRe { $list = new DocType\Listing(); if ($payload->type) { - if (!Document\Service::isValidType($type)) { - throw new BadRequestHttpException('Invalid type: ' . $type); + if (!Document\Service::isValidType($payload->type)) { + throw new BadRequestHttpException('Invalid type: ' . $payload->type); } - $list->setFilter(static fn (DocType $docType) => $docType->getType() === $type); + $list->setFilter(static fn (DocType $docType) => $docType->getType() === $payload->type); } $docTypes = []; diff --git a/src/Handler/Document/Page/PagePayload.php b/src/Handler/Document/Page/PagePayload.php index ee975c7c..7c394705 100644 --- a/src/Handler/Document/Page/PagePayload.php +++ b/src/Handler/Document/Page/PagePayload.php @@ -20,6 +20,7 @@ use OpenDxp\Bundle\AdminBundle\Payload\ExtJsPayloadInterface; use Symfony\Component\HttpFoundation\Request; +/** @phpstan-consistent-constructor */ readonly class PagePayload implements ExtJsPayloadInterface { public function __construct( diff --git a/src/Handler/Document/UpdateDocument/UpdateDocumentHandler.php b/src/Handler/Document/UpdateDocument/UpdateDocumentHandler.php index f0a582b2..577fc19f 100644 --- a/src/Handler/Document/UpdateDocument/UpdateDocumentHandler.php +++ b/src/Handler/Document/UpdateDocument/UpdateDocumentHandler.php @@ -39,11 +39,11 @@ public function __invoke(UpdateDocumentPayload $payload): UpdateDocumentResult { $document = Document::getById($payload->id); if (!$document instanceof Document) { - throw new DocumentNotFoundException($id); + throw new DocumentNotFoundException($payload->id); } $oldPath = (string) $document->getDao()->getCurrentFullPath(); - $oldDocument = Document::getById($id, ['force' => true]); + $oldDocument = Document::getById($payload->id, ['force' => true]); $adminUser = $this->userContext->getAdminUser(); $allowUpdate = true; @@ -59,8 +59,8 @@ public function __invoke(UpdateDocumentPayload $payload): UpdateDocumentResult } if ($document->isAllowed('settings')) { - if (isset($updateData['parentId'])) { - $parentDocument = Document::getById((int) $updateData['parentId']); + if (isset($payload->updateData['parentId'])) { + $parentDocument = Document::getById((int) $payload->updateData['parentId']); if ($document->getParentId() !== $parentDocument->getId()) { if (!$parentDocument->isAllowed('create')) { @@ -88,12 +88,12 @@ public function __invoke(UpdateDocumentPayload $payload): UpdateDocumentResult if ($allowUpdate) { $blockedVars = ['id', 'controller', 'action', 'module']; - if (!$document->isAllowed('rename') && isset($updateData['key'])) { + if (!$document->isAllowed('rename') && isset($payload->updateData['key'])) { $blockedVars[] = 'key'; Logger::debug('prevented renaming document because of missing permissions '); } - foreach ($updateData as $key => $value) { + foreach ($payload->updateData as $key => $value) { if (!in_array($key, $blockedVars)) { $document->setValue($key, $value); } @@ -102,8 +102,8 @@ public function __invoke(UpdateDocumentPayload $payload): UpdateDocumentResult $document->setUserModification($adminUser->getId()); $document->save(); - if (isset($updateData['index'])) { - $this->updateIndexesOfDocumentSiblings($document, (int) $updateData['index']); + if (isset($payload->updateData['index'])) { + $this->updateIndexesOfDocumentSiblings($document, (int) $payload->updateData['index']); } if ($oldPath && $oldPath != $document->getRealFullPath()) { @@ -122,8 +122,8 @@ public function __invoke(UpdateDocumentPayload $payload): UpdateDocumentResult throw new BadRequestHttpException($msg); } - if ($document->isAllowed('rename') && isset($updateData['key'])) { - $document->setKey($updateData['key']); + if ($document->isAllowed('rename') && isset($payload->updateData['key'])) { + $document->setKey($payload->updateData['key']); $document->setUserModification($adminUser->getId()); $document->save(); diff --git a/src/Handler/Document/Version/DiffVersions/DiffVersionsHandler.php b/src/Handler/Document/Version/DiffVersions/DiffVersionsHandler.php index d84831b8..4880e96a 100644 --- a/src/Handler/Document/Version/DiffVersions/DiffVersionsHandler.php +++ b/src/Handler/Document/Version/DiffVersions/DiffVersionsHandler.php @@ -42,14 +42,14 @@ public function __invoke(DiffVersionsPayload $payload): DiffVersionsResult $versionFrom = Version::getById($payload->from); $docFrom = $versionFrom?->loadData(); - if (!$docFrom instanceof Document) { + if (!$docFrom instanceof Document\PageSnippet) { throw new DocumentNotFoundException($payload->from); } $versionTo = Version::getById($payload->to); $docTo = $versionTo?->loadData(); - if (!$docTo instanceof Document) { + if (!$docTo instanceof Document\PageSnippet) { throw new DocumentNotFoundException($payload->to); } diff --git a/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsPayload.php b/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsPayload.php index 0da836fa..3b41038a 100644 --- a/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsPayload.php +++ b/src/Handler/Element/AnalyzePermissions/AnalyzePermissionsPayload.php @@ -10,9 +10,9 @@ final readonly class AnalyzePermissionsPayload implements ExtJsPayloadInterface { public function __construct( + public readonly int $elementId, public readonly ?int $userId = null, public readonly ?string $elementType = null, - public readonly int $elementId, ) {} public static function fromRequest(Request $request): static diff --git a/src/Handler/Element/DeleteDraft/DeleteDraftHandler.php b/src/Handler/Element/DeleteDraft/DeleteDraftHandler.php index 84f8213d..47a41b9d 100644 --- a/src/Handler/Element/DeleteDraft/DeleteDraftHandler.php +++ b/src/Handler/Element/DeleteDraft/DeleteDraftHandler.php @@ -4,6 +4,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteDraft; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Model\Version; final class DeleteDraftHandler diff --git a/src/Handler/Element/DeleteVersion/DeleteVersionHandler.php b/src/Handler/Element/DeleteVersion/DeleteVersionHandler.php index 0b44de0b..6c98e270 100644 --- a/src/Handler/Element/DeleteVersion/DeleteVersionHandler.php +++ b/src/Handler/Element/DeleteVersion/DeleteVersionHandler.php @@ -4,6 +4,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Element\DeleteVersion; +use OpenDxp\Bundle\AdminBundle\Payload\Common\IdBodyPayload; use OpenDxp\Model\Version; final class DeleteVersionHandler diff --git a/src/Handler/Element/UnlockElements/UnlockElementsHandler.php b/src/Handler/Element/UnlockElements/UnlockElementsHandler.php index 22b2d496..1ddd2d0c 100644 --- a/src/Handler/Element/UnlockElements/UnlockElementsHandler.php +++ b/src/Handler/Element/UnlockElements/UnlockElementsHandler.php @@ -8,7 +8,6 @@ final class UnlockElementsHandler { - /** @param array $elements */ public function __invoke(UnlockElementsPayload $payload): void { foreach ($payload->elements as $element) { diff --git a/src/Handler/Email/ResendEmail/ResendEmailHandler.php b/src/Handler/Email/ResendEmail/ResendEmailHandler.php index f0381936..51cfac16 100644 --- a/src/Handler/Email/ResendEmail/ResendEmailHandler.php +++ b/src/Handler/Email/ResendEmail/ResendEmailHandler.php @@ -14,7 +14,6 @@ final class ResendEmailHandler { - /** @param array $fieldOverrides Keys: 'from', 'to', 'cc', 'bcc', 'replyto' */ public function __invoke(ResendEmailPayload $payload): void { $emailLog = Tool\Email\Log::getById($payload->id); diff --git a/src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemHandler.php b/src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemHandler.php index e27b7ae7..09928779 100644 --- a/src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemHandler.php +++ b/src/Handler/Recyclebin/RestoreRecyclebinItem/RestoreRecyclebinItemHandler.php @@ -26,7 +26,7 @@ public function __invoke(RestoreRecyclebinItemPayload $payload): void { $item = Recyclebin\Item::getById($payload->id); if (!$item) { - throw new NotFoundHttpException(sprintf('Recyclebin item with id %d not found', $id)); + throw new NotFoundHttpException(sprintf('Recyclebin item with id %d not found', $payload->id)); } $item->restore(); diff --git a/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php b/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php index 97b6a876..820fee34 100644 --- a/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php +++ b/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php @@ -17,6 +17,8 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearOpenDxpCache; +use OpenDxp\Service\Cache\OpenDxpCacheClearingService; + final class ClearOpenDxpCacheHandler { public function __construct( diff --git a/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php b/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php index 3e6afbf9..0ae3edf1 100644 --- a/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php +++ b/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php @@ -17,6 +17,8 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearSymfonyCache; +use OpenDxp\Service\Cache\SymfonyCacheClearingService; + final class ClearSymfonyCacheHandler { public function __construct( diff --git a/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoHandler.php b/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoHandler.php index a2b62c9e..78dd3c77 100644 --- a/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoHandler.php +++ b/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoHandler.php @@ -24,10 +24,10 @@ final class DisplayCustomLogoHandler { private const string LOGO_PATH = 'custom-logo.image'; - public function __invoke(bool $white): DisplayCustomLogoResult + public function __invoke(DisplayCustomLogoPayload $payload): DisplayCustomLogoResult { $mime = 'image/svg+xml'; - $logoFile = OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/' . ($white ? 'logo-claim-white.svg' : 'logo-claim-gray.svg'); + $logoFile = OPENDXP_WEB_ROOT . '/bundles/opendxpadmin/img/' . ($payload->white ? 'logo-claim-white.svg' : 'logo-claim-gray.svg'); $stream = fopen($logoFile, 'rb'); $storage = Tool\Storage::get('admin'); diff --git a/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoPayload.php b/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoPayload.php new file mode 100644 index 00000000..0013da89 --- /dev/null +++ b/src/Handler/Settings/DisplayCustomLogo/DisplayCustomLogoPayload.php @@ -0,0 +1,35 @@ +query->has('white'), + ); + } +} diff --git a/src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementHandler.php b/src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementHandler.php index 93facd46..ad8b4e1d 100644 --- a/src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementHandler.php +++ b/src/Handler/Tags/LoadTagsForElement/GetTagsForElement/GetTagsForElementHandler.php @@ -17,6 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Tags\LoadTagsForElement\GetTagsForElement; +use OpenDxp\Bundle\AdminBundle\Handler\Tags\LoadTagsForElement\LoadTagsForElementPayload; use OpenDxp\Model\Element\Tag; final class GetTagsForElementHandler diff --git a/src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkResult.php b/src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkResult.php new file mode 100644 index 00000000..6f040b7d --- /dev/null +++ b/src/Handler/User/GetTokenLoginLink/GetTokenLoginLinkResult.php @@ -0,0 +1,25 @@ +getUsers() as $user) { + /** @phpstan-ignore instanceof.alwaysTrue */ if ($user instanceof User && $user->getName() !== 'system') { $users[] = [ 'id' => $user->getId(), diff --git a/src/Handler/Workflow/ShowGraph/GetWorkflowSvg/GetWorkflowSvgHandler.php b/src/Handler/Workflow/ShowGraph/GetWorkflowSvg/GetWorkflowSvgHandler.php index 47f8440c..6069aea3 100644 --- a/src/Handler/Workflow/ShowGraph/GetWorkflowSvg/GetWorkflowSvgHandler.php +++ b/src/Handler/Workflow/ShowGraph/GetWorkflowSvg/GetWorkflowSvgHandler.php @@ -18,6 +18,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Workflow\ShowGraph\GetWorkflowSvg; use InvalidArgumentException; +use OpenDxp\Bundle\AdminBundle\Handler\Workflow\ShowGraph\ShowGraphPayload; use OpenDxp\Bundle\AdminBundle\Service\Workflow\WorkflowElementResolver; use OpenDxp\Tool\Console; use OpenDxp\Workflow\Manager; diff --git a/src/Service/Asset/AssetPayloadMapper.php b/src/Service/Asset/AssetPayloadMapper.php index 63772335..58331ab2 100644 --- a/src/Service/Asset/AssetPayloadMapper.php +++ b/src/Service/Asset/AssetPayloadMapper.php @@ -84,7 +84,7 @@ private function applyProperties(?array $propertiesData, Asset $asset): void private function applyScheduler(?array $schedulerData, Asset $asset): void { - if ($schedulerData === null || !$asset->isAllowed('settings') || !method_exists($asset, 'setScheduledTasks')) { + if ($schedulerData === null || !$asset->isAllowed('settings')) { return; } diff --git a/src/Service/Element/SessionService.php b/src/Service/Element/SessionService.php index 90bbe394..a9e9463f 100644 --- a/src/Service/Element/SessionService.php +++ b/src/Service/Element/SessionService.php @@ -47,11 +47,11 @@ public function getDocument(Document $doc): ?Document return $sessionDoc ?: null; } - public function getOrLoadDocument(int $id): ?Document\PageSnippet + public function getOrLoadDocument(int $id): ?Document { $sessionId = $this->sessionId(); $doc = DocumentService::getElementFromSession('document', $id, $sessionId); - if ($doc) { + if ($doc instanceof Document) { return $doc; } diff --git a/src/Service/Workflow/WorkflowElementResolver.php b/src/Service/Workflow/WorkflowElementResolver.php index c7e57642..5967f2fe 100644 --- a/src/Service/Workflow/WorkflowElementResolver.php +++ b/src/Service/Workflow/WorkflowElementResolver.php @@ -20,7 +20,6 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Service\AdminUserContextInterface; use OpenDxp\Model\Asset; -use OpenDxp\Model\DataObject; use OpenDxp\Model\DataObject\Concrete as ConcreteObject; use OpenDxp\Model\Document; @@ -52,7 +51,6 @@ private function getLatestVersion(ConcreteObject|Document|Asset $element): Concr if ( $element instanceof Document\Folder || $element instanceof Asset\Folder - || $element instanceof DataObject\Folder || $element instanceof Document\Hardlink || $element instanceof Document\Link ) { diff --git a/tests/Model/Permissions/ModelAssetPermissionsTest.php b/tests/Model/Permissions/ModelAssetPermissionsTest.php index f6cac295..28faaf00 100644 --- a/tests/Model/Permissions/ModelAssetPermissionsTest.php +++ b/tests/Model/Permissions/ModelAssetPermissionsTest.php @@ -18,7 +18,8 @@ namespace OpenDxp\Bundle\AdminBundle\Tests\Model\Controller; use OpenDxp\Bundle\AdminBundle\Controller\Admin\Asset\AssetController; -use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildren\GetAssetChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Asset\GetAssetChildren\GetAssetChildrenPayload; use OpenDxp\Bundle\AdminBundle\Service\Asset\AssetGridService; use OpenDxp\Model\Asset; use OpenDxp\Model\Property; @@ -306,7 +307,7 @@ protected function doTestTreeGetChildrenById(Asset $element, User $user, array $ 'view' => 0, ]); - $responseData = $controller->treeGetChildrenByIdAction($handler, $request); + $responseData = $controller->treeGetChildrenByIdAction(GetAssetChildrenPayload::fromRequest($request), $handler); $responsePaths = []; $responseData = json_decode($responseData->getContent(), true); foreach ($responseData['nodes'] as $node) { diff --git a/tests/Model/Permissions/ModelDataObjectPermissionsTest.php b/tests/Model/Permissions/ModelDataObjectPermissionsTest.php index 4e82b4fb..a3c37adf 100644 --- a/tests/Model/Permissions/ModelDataObjectPermissionsTest.php +++ b/tests/Model/Permissions/ModelDataObjectPermissionsTest.php @@ -19,8 +19,9 @@ use Exception; use OpenDxp\Bundle\AdminBundle\Controller\Admin\DataObject\DataObjectController; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectChildrenHandler; -use OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenByIdHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\GetDataObjectChildren\GetDataObjectChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenById\TreeGetChildrenByIdHandler; +use OpenDxp\Bundle\AdminBundle\Handler\DataObject\TreeGetChildrenById\TreeGetChildrenByIdPayload; use OpenDxp\Bundle\AdminBundle\Service\Element\SessionService; use OpenDxp\Model\DataObject; use OpenDxp\Model\User; @@ -233,9 +234,8 @@ protected function doTestTreeGetChildrenById( try { TestHelper::callMethod($controller, 'checkPermission', ['objects']); $responseData = $controller->treeGetChildrenByIdAction( + TreeGetChildrenByIdPayload::fromRequest($request), $handler, - $request, - node: (int) $element->getId(), ); } catch (Exception $e) { if (is_null($expectedChildren)) { diff --git a/tests/Model/Permissions/ModelDocumentPermissionsTest.php b/tests/Model/Permissions/ModelDocumentPermissionsTest.php index bf79baef..80cfa40f 100644 --- a/tests/Model/Permissions/ModelDocumentPermissionsTest.php +++ b/tests/Model/Permissions/ModelDocumentPermissionsTest.php @@ -18,8 +18,9 @@ namespace OpenDxp\Bundle\AdminBundle\Tests\Model\Controller; use OpenDxp\Bundle\AdminBundle\Controller\Admin\Document\DocumentController; -use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentChildrenHandler; -use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\GetDocumentChildren\GetDocumentChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildren\TreeGetDocumentChildrenHandler; +use OpenDxp\Bundle\AdminBundle\Handler\Document\TreeGetDocumentChildren\TreeGetDocumentChildrenPayload; use OpenDxp\Model\Document; use OpenDxp\Model\Document\Page; use OpenDxp\Model\User; @@ -177,7 +178,7 @@ protected function doTestTreeGetChildrenById(Document $element, User $user, arra 'view' => 0, ]); - $responseData = $controller->treeGetChildrenByIdAction($handler, $request); + $responseData = $controller->treeGetChildrenByIdAction($handler, TreeGetDocumentChildrenPayload::fromRequest($request)); $responsePaths = []; $responseData = json_decode($responseData->getContent(), true); From eff42e24ea64a3ae302c27a6a7a203a2a3acd4d4 Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Thu, 18 Jun 2026 16:42:59 +0200 Subject: [PATCH 06/10] remove unused FileResult and StreamResult classes --- src/Http/Result/FileResult.php | 27 --------------------------- src/Http/Result/StreamResult.php | 26 -------------------------- 2 files changed, 53 deletions(-) delete mode 100644 src/Http/Result/FileResult.php delete mode 100644 src/Http/Result/StreamResult.php diff --git a/src/Http/Result/FileResult.php b/src/Http/Result/FileResult.php deleted file mode 100644 index 8473b01a..00000000 --- a/src/Http/Result/FileResult.php +++ /dev/null @@ -1,27 +0,0 @@ - Date: Thu, 18 Jun 2026 16:51:05 +0200 Subject: [PATCH 07/10] add cache clearing services, fix namespace alignment --- config/services.yaml | 16 ++++------------ .../Settings/ClearCache/ClearCacheHandler.php | 4 ++-- .../ClearOpenDxpCacheHandler.php | 2 +- .../SaveAppearanceSettingsHandler.php | 4 ++-- .../SaveSystemSettingsHandler.php | 4 ++-- .../Cache/OpenDxpCacheClearingService.php | 2 +- .../Cache/SymfonyCacheClearingService.php | 2 +- 7 files changed, 13 insertions(+), 21 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index 0da3c783..2c5b6f45 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -110,37 +110,29 @@ services: class: OpenDxp\Bundle\AdminBundle\Service\AdminUserContext OpenDxp\Bundle\AdminBundle\Service\Element\SessionService: ~ - OpenDxp\Bundle\AdminBundle\Service\Element\EditLockService: ~ OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPayloadMapper: ~ - OpenDxp\Bundle\AdminBundle\Service\Document\DocumentPersistenceCoordinator: ~ OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPayloadMapper: ~ - OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectPersistenceCoordinator: ~ + OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService: ~ OpenDxp\Bundle\AdminBundle\Service\Asset\AssetPayloadMapper: ~ - OpenDxp\Bundle\AdminBundle\Service\Asset\AssetPersistenceCoordinator: ~ - OpenDxp\Bundle\AdminBundle\Service\Asset\AssetUploadService: ~ - OpenDxp\Bundle\AdminBundle\Service\Asset\AssetGridService: ~ - OpenDxp\Bundle\AdminBundle\Service\DataObject\DataObjectGridService: ~ - OpenDxp\Bundle\AdminBundle\Service\Grid\GridColumnConfigService: ~ - OpenDxp\Bundle\AdminBundle\Service\Grid\AssetGridColumnConfigResolver: ~ - OpenDxp\Bundle\AdminBundle\Service\Grid\DataObjectGridColumnConfigResolver: ~ - OpenDxp\Bundle\AdminBundle\Service\Grid\GridExportService: ~ - OpenDxp\Bundle\AdminBundle\Service\Grid\GridBatchService: ~ + OpenDxp\Bundle\AdminBundle\Service\Cache\OpenDxpCacheClearingService: ~ + OpenDxp\Bundle\AdminBundle\Service\Cache\SymfonyCacheClearingService: ~ + # # Factories # diff --git a/src/Handler/Settings/ClearCache/ClearCacheHandler.php b/src/Handler/Settings/ClearCache/ClearCacheHandler.php index 4e51929d..75d37952 100644 --- a/src/Handler/Settings/ClearCache/ClearCacheHandler.php +++ b/src/Handler/Settings/ClearCache/ClearCacheHandler.php @@ -16,8 +16,8 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearCache; -use OpenDxp\Service\Cache\OpenDxpCacheClearingService; -use OpenDxp\Service\Cache\SymfonyCacheClearingService; +use OpenDxp\Bundle\AdminBundle\Service\Cache\OpenDxpCacheClearingService; +use OpenDxp\Bundle\AdminBundle\Service\Cache\SymfonyCacheClearingService; use Symfony\Component\HttpKernel\KernelInterface; final class ClearCacheHandler diff --git a/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php b/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php index 820fee34..5853d745 100644 --- a/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php +++ b/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php @@ -17,7 +17,7 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\ClearOpenDxpCache; -use OpenDxp\Service\Cache\OpenDxpCacheClearingService; +use OpenDxp\Bundle\AdminBundle\Service\Cache\OpenDxpCacheClearingService; final class ClearOpenDxpCacheHandler { diff --git a/src/Handler/Settings/SaveAppearanceSettings/SaveAppearanceSettingsHandler.php b/src/Handler/Settings/SaveAppearanceSettings/SaveAppearanceSettingsHandler.php index 4c41cb1a..db0ce3c2 100644 --- a/src/Handler/Settings/SaveAppearanceSettings/SaveAppearanceSettingsHandler.php +++ b/src/Handler/Settings/SaveAppearanceSettings/SaveAppearanceSettingsHandler.php @@ -18,10 +18,10 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveAppearanceSettings; use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSettingsPayload; +use OpenDxp\Bundle\AdminBundle\Service\Cache\OpenDxpCacheClearingService; +use OpenDxp\Bundle\AdminBundle\Service\Cache\SymfonyCacheClearingService; use OpenDxp\Bundle\AdminBundle\System\AdminConfig; use OpenDxp\Helper\StopMessengerWorkersTrait; -use OpenDxp\Service\Cache\OpenDxpCacheClearingService; -use OpenDxp\Service\Cache\SymfonyCacheClearingService; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\Event\TerminateEvent; use Symfony\Component\HttpKernel\KernelEvents; diff --git a/src/Handler/Settings/SaveSystemSettings/SaveSystemSettingsHandler.php b/src/Handler/Settings/SaveSystemSettings/SaveSystemSettingsHandler.php index 49f5bd64..6d650984 100644 --- a/src/Handler/Settings/SaveSystemSettings/SaveSystemSettingsHandler.php +++ b/src/Handler/Settings/SaveSystemSettings/SaveSystemSettingsHandler.php @@ -18,9 +18,9 @@ namespace OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSystemSettings; use OpenDxp\Bundle\AdminBundle\Handler\Settings\SaveSettingsPayload; +use OpenDxp\Bundle\AdminBundle\Service\Cache\OpenDxpCacheClearingService; +use OpenDxp\Bundle\AdminBundle\Service\Cache\SymfonyCacheClearingService; use OpenDxp\Helper\StopMessengerWorkersTrait; -use OpenDxp\Service\Cache\OpenDxpCacheClearingService; -use OpenDxp\Service\Cache\SymfonyCacheClearingService; use OpenDxp\SystemSettingsConfig; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\Event\TerminateEvent; diff --git a/src/Service/Cache/OpenDxpCacheClearingService.php b/src/Service/Cache/OpenDxpCacheClearingService.php index 80f89537..8fcd72e2 100644 --- a/src/Service/Cache/OpenDxpCacheClearingService.php +++ b/src/Service/Cache/OpenDxpCacheClearingService.php @@ -14,7 +14,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Service\Cache; +namespace OpenDxp\Bundle\AdminBundle\Service\Cache; use OpenDxp\Cache\Core\CoreCacheHandler; use OpenDxp\Event\SystemEvents; diff --git a/src/Service/Cache/SymfonyCacheClearingService.php b/src/Service/Cache/SymfonyCacheClearingService.php index 4f4d9841..a176e944 100644 --- a/src/Service/Cache/SymfonyCacheClearingService.php +++ b/src/Service/Cache/SymfonyCacheClearingService.php @@ -14,7 +14,7 @@ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 (GPLv3) */ -namespace OpenDxp\Service\Cache; +namespace OpenDxp\Bundle\AdminBundle\Service\Cache; use OpenDxp\Cache\Symfony\CacheClearer; use Symfony\Component\EventDispatcher\EventDispatcherInterface; From b6524c1950e660a9cff26e9b145670af21e0ad9f Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Thu, 18 Jun 2026 16:57:12 +0200 Subject: [PATCH 08/10] add workflow and translation resolver services --- config/services.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/services.yaml b/config/services.yaml index 2c5b6f45..88cc6be5 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -92,6 +92,7 @@ services: # OpenDxp\Bundle\AdminBundle\Service\Workflow\ActionsButtonService: ~ + OpenDxp\Bundle\AdminBundle\Service\Workflow\WorkflowElementResolver: ~ # # Admin Config Services @@ -133,6 +134,8 @@ services: OpenDxp\Bundle\AdminBundle\Service\Cache\OpenDxpCacheClearingService: ~ OpenDxp\Bundle\AdminBundle\Service\Cache\SymfonyCacheClearingService: ~ + OpenDxp\Bundle\AdminBundle\Service\Translation\AdminSearchTermResolver: ~ + # # Factories # From 6427294fb68717420daa09d76c4ea10dee66e0df Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Thu, 18 Jun 2026 17:08:48 +0200 Subject: [PATCH 09/10] remove unused ClearOpenDxpCacheHandler and ClearSymfonyCacheHandler classes (CQRS refactor cleanup) --- .../ClearOpenDxpCacheHandler.php | 32 ------------------- .../ClearSymfonyCacheHandler.php | 32 ------------------- 2 files changed, 64 deletions(-) delete mode 100644 src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php delete mode 100644 src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php diff --git a/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php b/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php deleted file mode 100644 index 5853d745..00000000 --- a/src/Handler/Settings/ClearOpenDxpCache/ClearOpenDxpCacheHandler.php +++ /dev/null @@ -1,32 +0,0 @@ -service->clear(); - } -} diff --git a/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php b/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php deleted file mode 100644 index 0ae3edf1..00000000 --- a/src/Handler/Settings/ClearSymfonyCache/ClearSymfonyCacheHandler.php +++ /dev/null @@ -1,32 +0,0 @@ -service->clear($environment); - } -} From b51ca03eae27851da088b4cf600c15ae85530730 Mon Sep 17 00:00:00 2001 From: Stefan Hagspiel Date: Thu, 18 Jun 2026 17:43:01 +0200 Subject: [PATCH 10/10] add LoginCheckPayload --- src/Controller/Admin/LoginController.php | 10 ++++---- .../Login/LoginCheck/LoginCheckPayload.php | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 src/Handler/Login/LoginCheck/LoginCheckPayload.php diff --git a/src/Controller/Admin/LoginController.php b/src/Controller/Admin/LoginController.php index 415a4dd7..79044f98 100644 --- a/src/Controller/Admin/LoginController.php +++ b/src/Controller/Admin/LoginController.php @@ -24,6 +24,7 @@ use OpenDxp\Bundle\AdminBundle\Handler\Login\Deeplink\DeeplinkPayload; use OpenDxp\Bundle\AdminBundle\Handler\Login\GenerateTwoFactorSetup\GenerateTwoFactorSetupHandler; use OpenDxp\Bundle\AdminBundle\Handler\Login\GenerateTwoFactorSetup\GenerateTwoFactorSetupPayload; +use OpenDxp\Bundle\AdminBundle\Handler\Login\LoginCheck\LoginCheckPayload; use OpenDxp\Bundle\AdminBundle\Handler\Login\LostPassword\LostPasswordHandler; use OpenDxp\Bundle\AdminBundle\Handler\Login\LostPassword\LostPasswordPayload; use OpenDxp\Bundle\AdminBundle\Handler\Login\SaveTwoFactorSetup\SaveTwoFactorSetupHandler; @@ -140,12 +141,11 @@ public function logoutAction(): void * Dummy route used to check authentication */ #[Route('/login/login', name: 'opendxp_admin_login_check')] - public function loginCheckAction( - #[MapQueryParameter] ?string $perspective = null, - ): RedirectResponse { + public function loginCheckAction(LoginCheckPayload $payload): RedirectResponse + { $params = []; - if ($perspective !== null) { - $params['perspective'] = strip_tags($perspective); + if ($payload->perspective !== null) { + $params['perspective'] = $payload->perspective; } return new RedirectResponse($this->generateUrl('opendxp_admin_login', $params)); diff --git a/src/Handler/Login/LoginCheck/LoginCheckPayload.php b/src/Handler/Login/LoginCheck/LoginCheckPayload.php new file mode 100644 index 00000000..48050882 --- /dev/null +++ b/src/Handler/Login/LoginCheck/LoginCheckPayload.php @@ -0,0 +1,24 @@ +query->getString('perspective') ?: null; + + return new static( + perspective: $perspective !== null ? strip_tags($perspective) : null, + ); + } +}