diff --git a/config/routes.php b/config/routes.php index 4b9401b1..5e4e6324 100644 --- a/config/routes.php +++ b/config/routes.php @@ -30,11 +30,11 @@ return $response; })->setName('test-post-request'); - $app->put('/dashboard-toggle-panel', \App\Application\Actions\Dashboard\DashboardTogglePanelAction::class) + $app->put('/dashboard-toggle-panel', \App\Application\Actions\Dashboard\DashboardTogglePanelProcessAction::class) ->setName('dashboard-toggle-panel'); $app->get('/login', \App\Application\Actions\Authentication\Page\LoginPageAction::class)->setName('login-page'); - $app->post('/login', \App\Application\Actions\Authentication\Submit\LoginSubmitAction::class)->setName( + $app->post('/login', \App\Application\Actions\Authentication\Ajax\LoginSubmitAction::class)->setName( 'login-submit' ); $app->get('/logout', \App\Application\Actions\Authentication\Page\LogoutPageAction::class)->setName('logout')->add( @@ -44,18 +44,18 @@ // Authentication - email verification - token $app->get( '/register-verification', - \App\Application\Actions\Authentication\Submit\RegisterVerifySubmitAction::class + \App\Application\Actions\Authentication\Ajax\RegisterVerifyProcessAction::class )->setName( 'register-verification' ); - $app->get('/unlock-account', \App\Application\Actions\Authentication\Submit\AccountUnlockAction::class)->setName( + $app->get('/unlock-account', \App\Application\Actions\Authentication\Ajax\AccountUnlockProcessAction::class)->setName( 'account-unlock-verification' ); $app->post(// Url password-forgotten hardcoded in login-main.js '/password-forgotten', - \App\Application\Actions\Authentication\Submit\PasswordForgottenEmailSubmitAction::class + \App\Application\Actions\Authentication\Ajax\PasswordForgottenEmailSubmitAction::class )->setName('password-forgotten-email-submit'); // Set new password page after clicking on email link with token $app->get('/reset-password', \App\Application\Actions\Authentication\Page\PasswordResetPageAction::class) @@ -63,13 +63,13 @@ // Submit new password (reset-password hardcoded in login-main.js) $app->post( '/reset-password', - \App\Application\Actions\Authentication\Submit\PasswordResetSubmitAction::class + \App\Application\Actions\Authentication\Ajax\NewPasswordResetSubmitAction::class )->setName('password-reset-submit'); // Submit new password when authenticated (post and not put as form submit) $app->put( '/change-password/{user_id:[0-9]+}', - \App\Application\Actions\User\Ajax\ChangePasswordSubmitAction::class + \App\Application\Actions\User\Ajax\PasswordChangeSubmitAction::class )->setName('change-password-submit')->add(UserAuthenticationMiddleware::class); // Without UserAuthenticationMiddleware as translations are also needed for non-protected pages such as password reset @@ -80,24 +80,24 @@ // $group->options('', PreflightAction::class); // Allow preflight requests $group->get('/list', \App\Application\Actions\User\Page\UserListPageAction::class) ->setName('user-list-page'); - $group->get('', \App\Application\Actions\User\Ajax\UserListAction::class) + $group->get('', \App\Application\Actions\User\Ajax\UserFetchListAction::class) ->setName('user-list'); $group // User dropdown options - ->get('/dropdown-options', \App\Application\Actions\User\Ajax\UserFetchDropdownOptionsAction::class) + ->get('/dropdown-options', \App\Application\Actions\User\Ajax\FetchDropdownOptionsForUserCreateAction::class) ->setName('user-dropdown-options'); - $group->get('/activity', \App\Application\Actions\User\Ajax\ListUserActivityAction::class) + $group->get('/activity', \App\Application\Actions\User\Ajax\UserActivityFetchListAction::class) ->setName('user-get-activity'); - $group->post('', \App\Application\Actions\User\Ajax\UserSubmitCreateAction::class) + $group->post('', \App\Application\Actions\User\Ajax\UserCreateAction::class) ->setName('user-create-submit'); // Route name has to be in the format: "[table_name]-read-page" and argument "[table-name]-id" to link from user activity $group->get('/{user_id:[0-9]+}', \App\Application\Actions\User\Page\UserReadPageAction::class) ->setName('user-read-page'); - $group->put('/{user_id:[0-9]+}', \App\Application\Actions\User\Ajax\UserSubmitUpdateAction::class) + $group->put('/{user_id:[0-9]+}', \App\Application\Actions\User\Ajax\UserUpdateAction::class) ->setName('user-update-submit'); - $group->delete('/{user_id:[0-9]+}', \App\Application\Actions\User\Ajax\UserSubmitDeleteAction::class) + $group->delete('/{user_id:[0-9]+}', \App\Application\Actions\User\Ajax\UserDeleteAction::class) ->setName('user-delete-submit'); })->add(UserAuthenticationMiddleware::class); @@ -114,7 +114,7 @@ // Client create form is rendered by the client and needs to have the available dropdown options $group->get( '/dropdown-options', - \App\Application\Actions\Client\Ajax\ClientCreateDropdownOptionsAction::class + \App\Application\Actions\Client\Ajax\FetchDropdownOptionsForClientCreateAction::class )->setName('client-create-dropdown'); /* For api response action: json_encode transforms object with public attributes to camelCase which matches Google recommendation @@ -135,16 +135,16 @@ // Note routes $app->group('/notes', function (RouteCollectorProxy $group) { - $group->get('', \App\Application\Actions\Note\Ajax\NoteListFetchAction::class)->setName('note-list'); + $group->get('', \App\Application\Actions\Note\Ajax\NoteFetchListAction::class)->setName('note-list'); $group->get('/{note_id:[0-9]+}', \App\Application\Actions\Note\Page\NoteReadPageAction::class)->setName( 'note-read-page' ); - $group->post('', \App\Application\Actions\Note\Ajax\NoteCreateSubmitAction::class)->setName( + $group->post('', \App\Application\Actions\Note\Ajax\NoteCreateAction::class)->setName( 'note-submit-creation' ); - $group->put('/{note_id:[0-9]+}', \App\Application\Actions\Note\Ajax\NoteUpdateSubmitAction::class) + $group->put('/{note_id:[0-9]+}', \App\Application\Actions\Note\Ajax\NoteUpdateAction::class) ->setName('note-submit-modification'); - $group->delete('/{note_id:[0-9]+}', \App\Application\Actions\Note\Ajax\NoteDeleteSubmitAction::class) + $group->delete('/{note_id:[0-9]+}', \App\Application\Actions\Note\Ajax\NoteDeleteAction::class) ->setName('note-submit-delete'); })->add(UserAuthenticationMiddleware::class); diff --git a/docs/naming-convention.md b/docs/naming-convention.md index 1b4af10f..da5d29c3 100644 --- a/docs/naming-convention.md +++ b/docs/naming-convention.md @@ -41,6 +41,34 @@ There are three kinds of DTOs: * Collection of Data objects with optional additional attributes. MUST end with ResultDataCollection: `ClientResultDataCollection`. +## Actions +1. **Use Case Specific**: The action name should clearly indicate the use case it handles. +The name of the resource should be the first word and in singular form (first word to be able to +find it better on a project wide search). +For example, if an action handles updating a client, it could be named `ClientUpdateAction.php`. +2. **Request Type Specific**: The action name should also indicate the type of request it handles. +For example, for fetch requests, `Fetch` could be used in the action name like `ClientFetchAction.php`. +For actions that display a page, the word `Page` should be in the action name like `LoginPageAction.php`. +Alternatively the word "Show" can also be used as it makes clear that a page is rendered `ShowUserProfileAction.php`. +4. **Suffix with "Action"**: `Action` at the end of the action names indicates that +the class is an action. +5. **Prefix with "Api"**: Only for Api requests add `Api` at the beginning of the action name +to indicate that the request is made from another application via said interface. +5. **Folder Structure**: Actions are organized into "Page" and "Ajax" folders based on whether they +handle page requests or Ajax requests. + +Based on these guidelines, here are some examples for different types of requests: + +- Show page actions: `LoginPageAction.php`, `UserProfilePageAction.php`, `ShowUserProfileAction.php` +- Fetch collection of data: `ClientFetchListAction.php`, `NoteFetchListAction.php` +- Read, get specific set of data: `ClientReadAction.php`, `UserReadAction.php` +- Submit/Process requests: `LoginSubmitAction.php`, `PasswordForgottenSubmitEmailAction.php`, +`NewPasswordResetSubmitAction.php`, `AccountUnlockProcessAction.php` +- Create requests: `ClientCreateAction.php`, `UserCreateAction.php` +- Update requests: `ClientUpdateAction.php`, `NoteUpdateAction.php` +- Delete requests: `ClientDeleteAction.php`, `NoteDeleteAction.php` +- Api requests: `ApiClientCreateAction.php`, + ## Templates * Follow [general files and folders](#General-files-and-folders) rules * View templates that are displayed to the user are in the module sub folder and must end diff --git a/src/Application/Actions/Authentication/Submit/AccountUnlockAction.php b/src/Application/Actions/Authentication/Ajax/AccountUnlockProcessAction.php similarity index 97% rename from src/Application/Actions/Authentication/Submit/AccountUnlockAction.php rename to src/Application/Actions/Authentication/Ajax/AccountUnlockProcessAction.php index 5a754a22..097493eb 100644 --- a/src/Application/Actions/Authentication/Submit/AccountUnlockAction.php +++ b/src/Application/Actions/Authentication/Ajax/AccountUnlockProcessAction.php @@ -1,6 +1,6 @@ queryFactory->insertQueryWithData($data)->into('user')->execute()->lastInsertId();. @@ -94,11 +94,10 @@ public function softDeleteQuery(string $fromTable): UpdateQuery return $this->connection->updateQuery()->update($fromTable)->set(['deleted_at' => date('Y-m-d H:i:s')]); } - /** * Returns a delete query instance for hard deletion. * - * @return Query\DeleteQuery The delete query object. + * @return Query\DeleteQuery the delete query object */ public function hardDeleteQuery(): Query\DeleteQuery { @@ -106,7 +105,6 @@ public function hardDeleteQuery(): Query\DeleteQuery return $this->connection->deleteQuery(); } - /** * Data is an assoc array of rows to insert where the key is the column name * Example: diff --git a/src/Infrastructure/SecurityLogging/EmailLogFinderRepository.php b/src/Infrastructure/SecurityLogging/EmailLogFinderRepository.php index 67117e94..fb26866f 100644 --- a/src/Infrastructure/SecurityLogging/EmailLogFinderRepository.php +++ b/src/Infrastructure/SecurityLogging/EmailLogFinderRepository.php @@ -79,6 +79,7 @@ public function getGlobalSentEmailAmount(int $days): int 'created_at >' => $query->newExpr('DATE_SUB(NOW(), INTERVAL :days DAY)'), ] )->bind(':days', $days, 'integer'); + return (int)($query->execute()->fetch('assoc')['sent_email_amount'] ?? 0); } } diff --git a/src/Infrastructure/SecurityLogging/EmailLoggerRepository.php b/src/Infrastructure/SecurityLogging/EmailLoggerRepository.php index 02343a42..9332bc73 100644 --- a/src/Infrastructure/SecurityLogging/EmailLoggerRepository.php +++ b/src/Infrastructure/SecurityLogging/EmailLoggerRepository.php @@ -17,6 +17,7 @@ public function __construct( * @param string $fromEmail * @param string $toEmail * @param string $subject + * @param int|string|null $userId * * @return string */ diff --git a/tests/Traits/AppTestTrait.php b/tests/Traits/AppTestTrait.php index 922eb1fb..206db2c7 100644 --- a/tests/Traits/AppTestTrait.php +++ b/tests/Traits/AppTestTrait.php @@ -81,10 +81,10 @@ protected function setUp(): void * - Cake\Database\Exception\MissingConnectionException: * Connection to Mysql could not be established: SQLSTATE[08004] [1040] Too many connections. * - * @return void * @throws \Psr\Container\NotFoundExceptionInterface - * * @throws \Psr\Container\ContainerExceptionInterface + * + * @return void */ protected function tearDown(): void {