From 1a7115efe361e22fd08f8fbc1e809d149b0d4a90 Mon Sep 17 00:00:00 2001 From: Tom Udding Date: Wed, 11 Sep 2024 14:51:49 +0200 Subject: [PATCH] feat: add member + user overview table Contains all information from members (and their associated users) for debugging purposes. --- module/Application/language/en.po | 27 +++++++- module/Application/language/gewisweb.pot | 25 ++++++- module/Application/language/nl.po | 27 +++++++- module/Application/view/partial/admin.phtml | 25 +++---- module/Decision/src/Mapper/Member.php | 27 ++++++++ module/User/config/module.config.php | 13 ++++ .../Factory/UserAdminControllerFactory.php | 28 ++++++++ .../src/Controller/UserAdminController.php | 35 ++++++++++ module/User/src/Model/User.php | 1 + module/User/src/Service/AclService.php | 3 + module/User/view/user/user-admin/index.phtml | 68 +++++++++++++++++++ phpcs.xml.dist | 1 + 12 files changed, 260 insertions(+), 20 deletions(-) create mode 100644 module/User/src/Controller/Factory/UserAdminControllerFactory.php create mode 100644 module/User/src/Controller/UserAdminController.php create mode 100644 module/User/view/user/user-admin/index.phtml diff --git a/module/Application/language/en.po b/module/Application/language/en.po index 786336aee3..23b0fb8418 100644 --- a/module/Application/language/en.po +++ b/module/Application/language/en.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: GEWISweb 0.1.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-24 21:14+0200\n" -"PO-Revision-Date: 2024-08-24 21:16+0200\n" +"POT-Creation-Date: 2024-09-11 14:50+0200\n" +"PO-Revision-Date: 2024-09-11 14:51+0200\n" "Last-Translator: Tom Udding \n" "Language-Team: English \n" "Language: en\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.4.4\n" +"X-Generator: Poedit 3.5\n" msgid " has a limited capacity and " msgstr " has a limited capacity and " @@ -118,6 +118,9 @@ msgstr "Actions" msgid "Activate" msgstr "Activate" +msgid "Activated" +msgstr "Activated" + msgid "Active" msgstr "Active" @@ -939,6 +942,9 @@ msgstr "Delete this news item" msgid "Delete this page" msgstr "Delete this page" +msgid "Deleted" +msgstr "Deleted" + msgid "Description" msgstr "Description" @@ -1219,6 +1225,9 @@ msgstr "Expiration date for the poll" msgid "Expired packages" msgstr "Expired packages" +msgid "Expires On" +msgstr "Expires On" + msgid "Exposure time" msgstr "Exposure time" @@ -1826,6 +1835,9 @@ msgstr "Member number" msgid "Members" msgstr "Members" +msgid "Membership Ends On" +msgstr "Membership Ends On" + msgid "Membership number" msgstr "Membership number" @@ -2203,6 +2215,9 @@ msgstr "Participants" msgid "Password" msgstr "Password" +msgid "Password Changed On" +msgstr "Password Changed On" + msgid "Password changed successfully" msgstr "Password changed successfully" @@ -2473,6 +2488,9 @@ msgstr "Revoked" msgid "Revoked authorizations for GMM %d" msgstr "Revoked authorizations for GMM %d" +msgid "Role(s)" +msgstr "Role(s)" + msgid "Root" msgstr "Root" @@ -3671,6 +3689,9 @@ msgstr "You are not allowed to administer option calendar periods" msgid "You are not allowed to administer polls" msgstr "You are not allowed to administer polls" +msgid "You are not allowed to administer users" +msgstr "You are not allowed to administer users" + msgid "You are not allowed to approve polls" msgstr "You are not allowed to approve polls" diff --git a/module/Application/language/gewisweb.pot b/module/Application/language/gewisweb.pot index 81bfc02e3d..cfb259224b 100644 --- a/module/Application/language/gewisweb.pot +++ b/module/Application/language/gewisweb.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: GEWISweb v2.8.6-747-ge3d122d45-dirty\n" +"Project-Id-Version: GEWISweb v2.8.6-764-gabeeb8c00-dirty\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-24 21:14+0200\n" +"POT-Creation-Date: 2024-09-11 14:50+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -117,6 +117,9 @@ msgstr "" msgid "Activate" msgstr "" +msgid "Activated" +msgstr "" + msgid "Active" msgstr "" @@ -893,6 +896,9 @@ msgstr "" msgid "Delete this page" msgstr "" +msgid "Deleted" +msgstr "" + msgid "Description" msgstr "" @@ -1163,6 +1169,9 @@ msgstr "" msgid "Expired packages" msgstr "" +msgid "Expires On" +msgstr "" + msgid "Exposure time" msgstr "" @@ -1719,6 +1728,9 @@ msgstr "" msgid "Members" msgstr "" +msgid "Membership Ends On" +msgstr "" + msgid "Membership number" msgstr "" @@ -2090,6 +2102,9 @@ msgstr "" msgid "Password" msgstr "" +msgid "Password Changed On" +msgstr "" + msgid "Password changed successfully" msgstr "" @@ -2343,6 +2358,9 @@ msgstr "" msgid "Revoked authorizations for GMM %d" msgstr "" +msgid "Role(s)" +msgstr "" + msgid "Root" msgstr "" @@ -3433,6 +3451,9 @@ msgstr "" msgid "You are not allowed to administer polls" msgstr "" +msgid "You are not allowed to administer users" +msgstr "" + msgid "You are not allowed to approve polls" msgstr "" diff --git a/module/Application/language/nl.po b/module/Application/language/nl.po index 1bcdb3205c..3cd3d9643e 100644 --- a/module/Application/language/nl.po +++ b/module/Application/language/nl.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: GEWISweb 0.1.0-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-24 21:14+0200\n" -"PO-Revision-Date: 2024-08-24 21:15+0200\n" +"POT-Creation-Date: 2024-09-11 14:50+0200\n" +"PO-Revision-Date: 2024-09-11 14:51+0200\n" "Last-Translator: Tom Udding \n" "Language-Team: English \n" "Language: nl\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.4.4\n" +"X-Generator: Poedit 3.5\n" msgid " has a limited capacity and " msgstr " heeft een beperkte capaciteit en " @@ -118,6 +118,9 @@ msgstr "Acties" msgid "Activate" msgstr "Activeer" +msgid "Activated" +msgstr "Geactiveerd" + msgid "Active" msgstr "Actief" @@ -944,6 +947,9 @@ msgstr "Verwijder dit nieuwsitem" msgid "Delete this page" msgstr "Verwijder deze pagina" +msgid "Deleted" +msgstr "Verwijderd" + msgid "Description" msgstr "Beschrijving" @@ -1230,6 +1236,9 @@ msgstr "Verloopdatum van de poll" msgid "Expired packages" msgstr "Verlopen pakketten" +msgid "Expires On" +msgstr "Verloopt op" + msgid "Exposure time" msgstr "Belichtingstijd" @@ -1850,6 +1859,9 @@ msgstr "Lidmaatschapsnummer" msgid "Members" msgstr "Leden" +msgid "Membership Ends On" +msgstr "Lidmaatschap eindigt op" + msgid "Membership number" msgstr "Lidmaatschapsnummer" @@ -2226,6 +2238,9 @@ msgstr "Deelnemers" msgid "Password" msgstr "Wachtwoord" +msgid "Password Changed On" +msgstr "Wachtwoord gewijzigd op" + msgid "Password changed successfully" msgstr "Wachtwoord succesvol gewijzigd" @@ -2498,6 +2513,9 @@ msgstr "Ingetrokken" msgid "Revoked authorizations for GMM %d" msgstr "Ingetrokken machtigingen voor ALV %d" +msgid "Role(s)" +msgstr "Rol(len)" + msgid "Root" msgstr "Root" @@ -3713,6 +3731,9 @@ msgstr "Je hebt niet de rechten om optieperiodes te beheren" msgid "You are not allowed to administer polls" msgstr "Je hebt niet de rechten om poll-instellingen te beheren" +msgid "You are not allowed to administer users" +msgstr "Je hebt niet de rechten om gebruikers te beheren" + msgid "You are not allowed to approve polls" msgstr "Je hebt niet de rechten om polls goed te keuren" diff --git a/module/Application/view/partial/admin.phtml b/module/Application/view/partial/admin.phtml index 18ca02115f..76f8dc62ab 100644 --- a/module/Application/view/partial/admin.phtml +++ b/module/Application/view/partial/admin.phtml @@ -95,26 +95,21 @@ use Laminas\View\Renderer\PhpRenderer; translate('Approvals') ?> - - acl('company_service_acl')->isAllowed('jobCategory', 'listAll')): ?> + + acl('company_service_acl')->isAllowed('jobCategory', 'listAll')): ?>
  • translate('Categories') ?>
  • - - acl('company_service_acl')->isAllowed('jobLabel', 'listAll')): ?> + + acl('company_service_acl')->isAllowed('jobLabel', 'listAll')): ?>
  • translate('Labels') ?>
  • - + @@ -179,8 +174,7 @@ use Laminas\View\Renderer\PhpRenderer;
  • translate('News') ?>
  • - + acl('frontpage_service_acl')->isAllowed('page', 'create')): ?>
  • translate('Pages') ?> @@ -210,6 +204,13 @@ use Laminas\View\Renderer\PhpRenderer;
  • + acl('user_service_acl')->isAllowed('user', 'view_status')): ?> +
  • + + translate('Members') ?> + +
  • + diff --git a/module/Decision/src/Mapper/Member.php b/module/Decision/src/Mapper/Member.php index 95f8ff94c5..3071ba2795 100644 --- a/module/Decision/src/Mapper/Member.php +++ b/module/Decision/src/Mapper/Member.php @@ -10,6 +10,8 @@ use Decision\Model\OrganMember as OrganMemberModel; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Query\ResultSetMappingBuilder; +use User\Model\User as UserModel; +use User\Model\UserRole as UserRoleModel; use function strtolower; @@ -173,6 +175,31 @@ public function findHistoricalInstallations(MemberModel $member): array return $qb->getQuery()->getResult(); } + /** + * Fetch all members including their associated user. + * + * NOTE: The ordering of the return array is not as you might expect. The actual result will be like: + * + * array{ + * 0: MemberModel, + * 1: ?UserModel, + * 2: MemberModel, + * 3: ... (repeat pattern) + * } + * + * In other words, every 2 rows represent a single `Member`. + * + * @return array + */ + public function findAllWithUserDetails(): array + { + $qb = $this->getRepository()->createQueryBuilder('m'); + $qb->leftJoin(UserModel::class, 'u', 'WITH', 'm.lidnr = u.lidnr') + ->addSelect('u'); + + return $qb->getQuery()->getResult(); + } + protected function getRepositoryName(): string { return MemberModel::class; diff --git a/module/User/config/module.config.php b/module/User/config/module.config.php index c79b931eb4..9f2124d64f 100644 --- a/module/User/config/module.config.php +++ b/module/User/config/module.config.php @@ -12,7 +12,9 @@ use User\Controller\ApiAuthenticationController; use User\Controller\Factory\ApiAdminControllerFactory; use User\Controller\Factory\ApiAuthenticationControllerFactory; +use User\Controller\Factory\UserAdminControllerFactory; use User\Controller\Factory\UserControllerFactory; +use User\Controller\UserAdminController; use User\Controller\UserController; return [ @@ -155,6 +157,16 @@ ], ], ], + 'members' => [ + 'type' => Literal::class, + 'options' => [ + 'route' => '/members', + 'defaults' => [ + 'controller' => UserAdminController::class, + 'action' => 'index', + ], + ], + ], ], 'priority' => 100, ], @@ -175,6 +187,7 @@ 'factories' => [ ApiAdminController::class => ApiAdminControllerFactory::class, ApiAuthenticationController::class => ApiAuthenticationControllerFactory::class, + UserAdminController::class => UserAdminControllerFactory::class, UserController::class => UserControllerFactory::class, ], ], diff --git a/module/User/src/Controller/Factory/UserAdminControllerFactory.php b/module/User/src/Controller/Factory/UserAdminControllerFactory.php new file mode 100644 index 0000000000..88471936dc --- /dev/null +++ b/module/User/src/Controller/Factory/UserAdminControllerFactory.php @@ -0,0 +1,28 @@ +get('user_service_acl'), + $container->get(MvcTranslator::class), + $container->get('decision_mapper_member'), + ); + } +} diff --git a/module/User/src/Controller/UserAdminController.php b/module/User/src/Controller/UserAdminController.php new file mode 100644 index 0000000000..696538bac1 --- /dev/null +++ b/module/User/src/Controller/UserAdminController.php @@ -0,0 +1,35 @@ +aclService->isAllowed('view_status', 'user')) { + throw new NotAllowedException( + $this->translator->translate('You are not allowed to administer users'), + ); + } + + return new ViewModel([ + 'members' => $this->memberMapper->findAllWithUserDetails(), + ]); + } +} diff --git a/module/User/src/Model/User.php b/module/User/src/Model/User.php index 96d0aaa315..9fe842f286 100644 --- a/module/User/src/Model/User.php +++ b/module/User/src/Model/User.php @@ -57,6 +57,7 @@ class User implements IdentityInterface #[OneToMany( targetEntity: UserRole::class, mappedBy: 'lidnr', + fetch: 'EAGER', )] protected Collection $roles; diff --git a/module/User/src/Service/AclService.php b/module/User/src/Service/AclService.php index 5f96784bf2..7b769d6e93 100644 --- a/module/User/src/Service/AclService.php +++ b/module/User/src/Service/AclService.php @@ -94,6 +94,9 @@ protected function createAcl(): void $this->acl->deny(UserRoles::Board->value, 'user_admin'); $this->acl->deny(UserRoles::Board->value, 'apiuser'); + // Do not allow the board to see activation status + $this->acl->deny(UserRoles::Board->value, 'user', ['view_status']); + $this->acl->allow(UserRoles::User->value, 'user', ['password_change']); $this->acl->allow(UserRoles::Company->value, 'user', ['password_change']); } diff --git a/module/User/view/user/user-admin/index.phtml b/module/User/view/user/user-admin/index.phtml new file mode 100644 index 0000000000..28c41295bb --- /dev/null +++ b/module/User/view/user/user-admin/index.phtml @@ -0,0 +1,68 @@ +breadcrumbs() + ->addBreadcrumb($this->translate('Members')); +?> +

    translate('Members') ?>

    +
    + + + + + + + + + + + + + + + + + + getDeleted(); + ?> + + + + + + + + + + + + + + +
    #translate('Name') ?>translate('Type') ?>translate('Activated') ?>translate('Hidden') ?>translate('Deleted') ?>translate('Password Changed On') ?>translate('Role(s)') ?>translate('Membership Ends On') ?>translate('Expires On') ?>
    getLidnr() ?>escapeHtml($member->getFullName()) ?>getType()->getName($this->plugin('translate')->getTranslator()) ?>getPasswordChangedOn()?->format(DateTimeInterface::ATOM) ?? '-') : $this->translate('N/A') ?> + getRoles()->count()): ?> + getRoles() as $role): ?> + getRole()->value . ' (' . ($role->getExpiration()?->format(DateTimeInterface::ATOM) ?? '-') . ')'?> + + + - + + getMembershipEndsOn()?->format(DateTimeInterface::ATOM) ?? $this->translate('N/A') ?>getExpiration()->format(DateTimeInterface::ATOM) ?>
    +
    diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 9c80888934..6ee29c0bf5 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -73,6 +73,7 @@ module/User/src/Command/Factory/DeleteOldLoginAttemptsFactory.php module/User/src/Controller/Factory/ApiAuthenticationControllerFactory.php module/User/src/Controller/Factory/UserControllerFactory.php + module/User/src/Controller/Factory/UserAdminControllerFactory.php module/User/src/Controller/Factory/ApiAdminControllerFactory.php module/User/src/Mapper/Factory/ApiAppFactory.php module/User/src/Service/Factory/PwnedPasswordsFactory.php