diff --git a/README.md b/README.md index 8e79fe4a..974adac5 100644 --- a/README.md +++ b/README.md @@ -579,7 +579,7 @@ If you change or add translations, you need to update the `messages` XLIFF file You can use: - ./bin/phpunit + ./vendor/bin/phpunit ## ✨ Code linting diff --git a/docs/api/README.md b/docs/api/README.md index 00b586a4..a1ef9ff0 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -19,17 +19,17 @@ When `API_KEY` is not set, the API endpoints are disabled and will return a 500 Each endpoint displays information related to the User: * [Get Users](v1/users/all.md) : `GET /api/v1/users` -* [Get User Details](v1/users/details.md) : `GET /api/v1/users/:username` +* [Get User Details](v1/users/details.md) : `GET /api/v1/users/:user_id` #### Calendars related Endpoints for viewing and modifying user calendars. -* [Show All User Calendars](v1/calendars/all.md) : `GET /api/v1/calendars/:username` -* [Show User Calendar Details](v1/calendars/details.md) : `GET /api/v1/calendars/:username/:calendar_id` -* [Create User Calendar](v1/calendars/create.md) : `POST /api/v1/calendars/:username/create` -* [Edit User Calendar](v1/calendars/edit.md) : `POST /api/v1/calendars/:username/:calendar_id/edit` -* [Delete User Calendar](v1/calendars/delete.md) : `POST /api/v1/calendars/:username/:calendar_id/delete` -* [Show User Calendar Shares](v1/calendars/shares.md) : `GET /api/v1/calendars/:username/shares/:calendar_id` -* [Share User Calendar](v1/calendars/share_add.md) : `POST /api/v1/calendars/:username/share/:calendar_id/add` -* [Remove Share User Calendar](v1/calendars/share_remove.md) : `POST /api/v1/calendars/:username/share/:calendar_id/remove` +* [Show All User Calendars](v1/calendars/all.md) : `GET /api/v1/calendars/:user_id` +* [Show User Calendar Details](v1/calendars/details.md) : `GET /api/v1/calendars/:user_id/:calendar_id` +* [Create User Calendar](v1/calendars/create.md) : `POST /api/v1/calendars/:user_id/create` +* [Edit User Calendar](v1/calendars/edit.md) : `PUT /api/v1/calendars/:user_id/:calendar_id` +* [Delete User Calendar](v1/calendars/delete.md) : `DELETE /api/v1/calendars/:user_id/:calendar_id` +* [Show User Calendar Shares](v1/calendars/shares.md) : `GET /api/v1/calendars/:user_id/shares/:calendar_id` +* [Share User Calendar](v1/calendars/share_add.md) : `POST /api/v1/calendars/:user_id/share/:calendar_id/add` +* [Remove Share User Calendar](v1/calendars/share_remove.md) : `POST /api/v1/calendars/:user_id/share/:calendar_id/remove` diff --git a/docs/api/v1/calendars/all.md b/docs/api/v1/calendars/all.md index 45e15fb2..df851582 100644 --- a/docs/api/v1/calendars/all.md +++ b/docs/api/v1/calendars/all.md @@ -2,7 +2,7 @@ Gets a list of all available calendars for a specific user. -**URL** : `/api/v1/calendars/:username` +**URL** : `/api/v1/calendars/:user_id` **Method** : `GET` @@ -11,7 +11,7 @@ Gets a list of all available calendars for a specific user. **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", ``` **URL example** diff --git a/docs/api/v1/calendars/create.md b/docs/api/v1/calendars/create.md index d11be111..7db27ed7 100644 --- a/docs/api/v1/calendars/create.md +++ b/docs/api/v1/calendars/create.md @@ -2,7 +2,7 @@ Creates a new calendar for a specific user. -**URL** : `/api/v1/calendars/:username/create` +**URL** : `/api/v1/calendars/:user_id/create` **Method** : `POST` @@ -11,7 +11,7 @@ Creates a new calendar for a specific user. **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", ``` **Request Body constraints** diff --git a/docs/api/v1/calendars/delete.md b/docs/api/v1/calendars/delete.md index 92e22655..cb120e1b 100644 --- a/docs/api/v1/calendars/delete.md +++ b/docs/api/v1/calendars/delete.md @@ -2,23 +2,23 @@ Deletes a specific calendar for a specific user. -**URL** : `/api/v1/calendars/:username/:calendar_id/delete` +**URL** : `/api/v1/calendars/:user_id/:calendar_id` -**Method** : `POST` +**Method** : `DELETE` **Auth required** : YES **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", :calendar_id -> "[numeric id of a calendar owned by the user]", ``` **URL example** ``` -/api/v1/calendars/jdoe/1/delete +/api/v1/calendars/jdoe/1 ``` ## Success Response diff --git a/docs/api/v1/calendars/details.md b/docs/api/v1/calendars/details.md index ec91acce..fe9f84a8 100644 --- a/docs/api/v1/calendars/details.md +++ b/docs/api/v1/calendars/details.md @@ -2,7 +2,7 @@ Gets a list of all available calendars for a specific user. -**URL** : `/api/v1/calendars/:username/:calendar_id` +**URL** : `/api/v1/calendars/:user_id/:calendar_id` **Method** : `GET` @@ -11,7 +11,7 @@ Gets a list of all available calendars for a specific user. **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", :calendar_id -> "[numeric id of a calendar owned by the user]", ``` diff --git a/docs/api/v1/calendars/edit.md b/docs/api/v1/calendars/edit.md index d5a5516f..ccb7a72b 100644 --- a/docs/api/v1/calendars/edit.md +++ b/docs/api/v1/calendars/edit.md @@ -2,16 +2,16 @@ Edits an existing calendar for a specific user. -**URL** : `/api/v1/calendars/:username/:calendar_id/edit` +**URL** : `/api/v1/calendars/:user_id/:calendar_id` -**Method** : `POST` +**Method** : `PUT` or `PATCH` **Auth required** : YES **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", :calendar_id -> "[numeric id of a calendar owned by the user]", ``` @@ -30,7 +30,7 @@ Edits an existing calendar for a specific user. **URL example** ``` -/api/v1/calendars/jdoe/1/edit +/api/v1/calendars/jdoe/1 ``` **Body example** diff --git a/docs/api/v1/calendars/share_add.md b/docs/api/v1/calendars/share_add.md index 73cbde0d..4dba5ec0 100644 --- a/docs/api/v1/calendars/share_add.md +++ b/docs/api/v1/calendars/share_add.md @@ -2,7 +2,7 @@ Shares (or updates write access) a calendar owned by the specified user to another user. -**URL** : `/api/v1/calendars/:username/share/:calendar_id/add` +**URL** : `/api/v1/calendars/:user_id/share/:calendar_id/add` **Method** : `POST` @@ -11,7 +11,7 @@ Shares (or updates write access) a calendar owned by the specified user to anoth **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", :calendar_id -> "[numeric id of a calendar owned by the user]", ``` diff --git a/docs/api/v1/calendars/share_remove.md b/docs/api/v1/calendars/share_remove.md index cadf3d72..3e23b5b9 100644 --- a/docs/api/v1/calendars/share_remove.md +++ b/docs/api/v1/calendars/share_remove.md @@ -2,7 +2,7 @@ Removes access to a specific shared calendar for a specific user. -**URL** : `/api/v1/calendars/:username/share/:calendar_id/remove` +**URL** : `/api/v1/calendars/:user_id/share/:calendar_id/remove` **Method** : `POST` @@ -11,7 +11,7 @@ Removes access to a specific shared calendar for a specific user. **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", :calendar_id -> "[numeric id of a calendar owned by the user]", ``` diff --git a/docs/api/v1/calendars/shares.md b/docs/api/v1/calendars/shares.md index 0776ddfd..f905fad9 100644 --- a/docs/api/v1/calendars/shares.md +++ b/docs/api/v1/calendars/shares.md @@ -2,7 +2,7 @@ Gets a list of all users with whom a specific user calendar is shared. -**URL** : `/api/v1/calendars/:username/shares/:calendar_id` +**URL** : `/api/v1/calendars/:user_id/shares/:calendar_id` **Method** : `GET` @@ -11,7 +11,7 @@ Gets a list of all users with whom a specific user calendar is shared. **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", :calendar_id -> "[numeric id of a calendar owned by the user]", ``` diff --git a/docs/api/v1/users/details.md b/docs/api/v1/users/details.md index c98e96ef..dff6587c 100644 --- a/docs/api/v1/users/details.md +++ b/docs/api/v1/users/details.md @@ -2,7 +2,7 @@ Gets details about a specific user account. -**URL** : `/api/v1/users/:username` +**URL** : `/api/v1/users/:user_id` **Method** : `GET` @@ -11,7 +11,7 @@ Gets details about a specific user account. **Params constraints** ``` -:username -> "[username in plain text]", +:user_id -> "[user id as an int]", ``` **URL example** diff --git a/src/Controller/Admin/AddressBookController.php b/src/Controller/Admin/AddressBookController.php index c66246aa..7711b983 100644 --- a/src/Controller/Admin/AddressBookController.php +++ b/src/Controller/Admin/AddressBookController.php @@ -4,6 +4,7 @@ use App\Entity\AddressBook; use App\Entity\Principal; +use App\Entity\User; use App\Form\AddressBookType; use App\Services\BirthdayService; use Doctrine\Persistence\ManagerRegistry; @@ -16,24 +17,40 @@ #[Route('/addressbooks', name: 'addressbook_')] class AddressBookController extends AbstractController { - #[Route('/{username}', name: 'index')] - public function addressBooks(ManagerRegistry $doctrine, string $username): Response + #[Route('/{userId}', name: 'index')] + public function addressBooks(ManagerRegistry $doctrine, int $userId): Response { - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); - $addressbooks = $doctrine->getRepository(AddressBook::class)->findByPrincipalUri(Principal::PREFIX.$username); + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + + $username = $user->getUsername(); + $principalUri = Principal::PREFIX.$username; + + $principal = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri); + $addressbooks = $doctrine->getRepository(AddressBook::class)->findByPrincipalUri($principalUri); return $this->render('addressbooks/index.html.twig', [ 'addressbooks' => $addressbooks, 'principal' => $principal, - 'username' => $username, + 'userId' => $userId, ]); } - #[Route('/{username}/new', name: 'create')] - #[Route('/{username}/edit/{id}', name: 'edit', requirements: ['id' => "\d+"])] - public function addressbookCreate(ManagerRegistry $doctrine, Request $request, string $username, ?int $id, TranslatorInterface $trans, BirthdayService $birthdayService): Response + #[Route('/{userId}/new', name: 'create')] + #[Route('/{userId}/edit/{id}', name: 'edit', requirements: ['id' => "\d+"])] + public function addressbookCreate(ManagerRegistry $doctrine, Request $request, int $userId, ?int $id, TranslatorInterface $trans, BirthdayService $birthdayService): Response { - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + + $username = $user->getUsername(); + $principalUri = Principal::PREFIX.$username; + + $principal = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri); if (!$principal) { throw $this->createNotFoundException('User not found'); @@ -55,7 +72,7 @@ public function addressbookCreate(ManagerRegistry $doctrine, Request $request, s if ($isBirthdayCalendarEnabled) { $form->get('includedInBirthdayCalendar')->setData($addressbook->isIncludedInBirthdayCalendar()); } - $form->get('principalUri')->setData(Principal::PREFIX.$username); + $form->get('principalUri')->setData($principalUri); $form->handleRequest($request); @@ -78,20 +95,25 @@ public function addressbookCreate(ManagerRegistry $doctrine, Request $request, s $birthdayService->syncUser($username); } - return $this->redirectToRoute('addressbook_index', ['username' => $username]); + return $this->redirectToRoute('addressbook_index', ['userId' => $userId]); } return $this->render('addressbooks/edit.html.twig', [ 'form' => $form->createView(), 'principal' => $principal, - 'username' => $username, + 'userId' => $userId, 'addressbook' => $addressbook, ]); } - #[Route('/{username}/delete/{id}', name: 'delete', requirements: ['id' => "\d+"])] - public function addressbookDelete(ManagerRegistry $doctrine, string $username, string $id, TranslatorInterface $trans, BirthdayService $birthdayService): Response + #[Route('/{userId}/delete/{id}', name: 'delete', requirements: ['id' => "\d+"])] + public function addressbookDelete(ManagerRegistry $doctrine, int $userId, string $id, TranslatorInterface $trans, BirthdayService $birthdayService): Response { + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + $addressbook = $doctrine->getRepository(AddressBook::class)->findOneById($id); if (!$addressbook) { throw $this->createNotFoundException('Address Book not found'); @@ -113,9 +135,9 @@ public function addressbookDelete(ManagerRegistry $doctrine, string $username, s $isBirthdayCalendarEnabled = $this->getParameter('caldav_enabled') && $this->getParameter('carddav_enabled'); if ($isBirthdayCalendarEnabled) { // Let's sync the user birthday calendar if needed - $birthdayService->syncUser($username); + $birthdayService->syncUser($user->getUsername()); } - return $this->redirectToRoute('addressbook_index', ['username' => $username]); + return $this->redirectToRoute('addressbook_index', ['userId' => $userId]); } } diff --git a/src/Controller/Admin/CalendarController.php b/src/Controller/Admin/CalendarController.php index d76cec8a..449e097c 100644 --- a/src/Controller/Admin/CalendarController.php +++ b/src/Controller/Admin/CalendarController.php @@ -7,6 +7,7 @@ use App\Entity\CalendarSubscription; use App\Entity\Principal; use App\Entity\SchedulingObject; +use App\Entity\User; use App\Form\CalendarInstanceType; use Doctrine\Persistence\ManagerRegistry; use Sabre\DAV\Sharing\Plugin as SharingPlugin; @@ -22,13 +23,21 @@ #[Route('/calendars', name: 'calendar_')] class CalendarController extends AbstractController { - #[Route('/{username}', name: 'index')] - public function calendars(ManagerRegistry $doctrine, UrlGeneratorInterface $router, string $username): Response + #[Route('/{userId}', name: 'index')] + public function calendars(ManagerRegistry $doctrine, UrlGeneratorInterface $router, int $userId): Response { - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); - $allCalendars = $doctrine->getRepository(CalendarInstance::class)->findByPrincipalUri(Principal::PREFIX.$username); + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + + $username = $user->getUsername(); + $principalUri = Principal::PREFIX.$username; + + $principal = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri); + $allCalendars = $doctrine->getRepository(CalendarInstance::class)->findByPrincipalUri($principalUri); - $subscriptions = $doctrine->getRepository(CalendarSubscription::class)->findByPrincipalUri(Principal::PREFIX.$username); + $subscriptions = $doctrine->getRepository(CalendarSubscription::class)->findByPrincipalUri($principalUri); // Separate shared calendars $calendars = []; @@ -54,7 +63,7 @@ public function calendars(ManagerRegistry $doctrine, UrlGeneratorInterface $rout } // We need all the other users so we can propose to share calendars with them - $allPrincipalsExcept = $doctrine->getRepository(Principal::class)->findAllExceptPrincipal(Principal::PREFIX.$username); + $allPrincipalsExcept = $doctrine->getRepository(Principal::class)->findAllExceptPrincipal($principalUri); return $this->render('calendars/index.html.twig', [ 'calendars' => $calendars, @@ -62,16 +71,24 @@ public function calendars(ManagerRegistry $doctrine, UrlGeneratorInterface $rout 'shared' => $shared, 'auto' => $auto, 'principal' => $principal, - 'username' => $username, + 'userId' => $userId, 'allPrincipals' => $allPrincipalsExcept, ]); } - #[Route('/{username}/new', name: 'create')] - #[Route('/{username}/edit/{id}', name: 'edit', requirements: ['id' => "\d+"])] - public function calendarEdit(ManagerRegistry $doctrine, Request $request, string $username, ?int $id, TranslatorInterface $trans): Response + #[Route('/{userId}/new', name: 'create')] + #[Route('/{userId}/edit/{id}', name: 'edit', requirements: ['id' => "\d+"])] + public function calendarEdit(ManagerRegistry $doctrine, Request $request, int $userId, ?int $id, TranslatorInterface $trans): Response { - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + + $username = $user->getUsername(); + $principalUri = Principal::PREFIX.$username; + + $principal = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri); if (!$principal) { throw $this->createNotFoundException('User not found'); @@ -101,7 +118,7 @@ public function calendarEdit(ManagerRegistry $doctrine, Request $request, string $form->get('events')->setData(in_array(Calendar::COMPONENT_EVENTS, $components)); $form->get('todos')->setData(in_array(Calendar::COMPONENT_TODOS, $components)); $form->get('notes')->setData(in_array(Calendar::COMPONENT_NOTES, $components)); - $form->get('principalUri')->setData(Principal::PREFIX.$username); + $form->get('principalUri')->setData($principalUri); $form->handleRequest($request); @@ -143,19 +160,19 @@ public function calendarEdit(ManagerRegistry $doctrine, Request $request, string $this->addFlash('success', $trans->trans('calendar.saved')); - return $this->redirectToRoute('calendar_index', ['username' => $username]); + return $this->redirectToRoute('calendar_index', ['userId' => $userId]); } return $this->render('calendars/edit.html.twig', [ 'form' => $form->createView(), 'principal' => $principal, - 'username' => $username, + 'userId' => $userId, 'calendar' => $calendarInstance, ]); } - #[Route('/{username}/shares/{calendarid}', name: 'shares', requirements: ['calendarid' => "\d+"])] - public function calendarShares(ManagerRegistry $doctrine, string $username, string $calendarid, TranslatorInterface $trans): Response + #[Route('/{userId}/shares/{calendarid}', name: 'shares', requirements: ['calendarid' => "\d+"])] + public function calendarShares(ManagerRegistry $doctrine, int $userId, string $calendarid, TranslatorInterface $trans): Response { $instances = $doctrine->getRepository(CalendarInstance::class)->findSharedInstancesOfInstance($calendarid, true); @@ -167,15 +184,15 @@ public function calendarShares(ManagerRegistry $doctrine, string $username, stri 'email' => $instance['email'], 'accessText' => $trans->trans('calendar.share_access.'.$instance[0]['access']), 'isWriteAccess' => SharingPlugin::ACCESS_READWRITE === $instance[0]['access'], - 'revokeUrl' => $this->generateUrl('calendar_revoke', ['username' => $username, 'id' => $instance[0]['id']]), + 'revokeUrl' => $this->generateUrl('calendar_revoke', ['userId' => $userId, 'id' => $instance[0]['id']]), ]; } return new JsonResponse($response); } - #[Route('/{username}/share/{instanceid}', name: 'share_add', requirements: ['instanceid' => "\d+"])] - public function calendarShareAdd(ManagerRegistry $doctrine, Request $request, string $username, string $instanceid, TranslatorInterface $trans): Response + #[Route('/{userId}/share/{instanceid}', name: 'share_add', requirements: ['instanceid' => "\d+"])] + public function calendarShareAdd(ManagerRegistry $doctrine, Request $request, int $userId, string $instanceid, TranslatorInterface $trans): Response { $instance = $doctrine->getRepository(CalendarInstance::class)->findOneById($instanceid); if (!$instance) { @@ -217,11 +234,11 @@ public function calendarShareAdd(ManagerRegistry $doctrine, Request $request, st $entityManager->flush(); $this->addFlash('success', $trans->trans('calendar.shared')); - return $this->redirectToRoute('calendar_index', ['username' => $username]); + return $this->redirectToRoute('calendar_index', ['userId' => $userId]); } - #[Route('/{username}/delete/{id}', name: 'delete', requirements: ['id' => "\d+"])] - public function calendarDelete(ManagerRegistry $doctrine, string $username, string $id, TranslatorInterface $trans): Response + #[Route('/{userId}/delete/{id}', name: 'delete', requirements: ['id' => "\d+"])] + public function calendarDelete(ManagerRegistry $doctrine, int $userId, string $id, TranslatorInterface $trans): Response { $instance = $doctrine->getRepository(CalendarInstance::class)->findOneById($id); if (!$instance) { @@ -262,11 +279,11 @@ public function calendarDelete(ManagerRegistry $doctrine, string $username, stri $entityManager->flush(); $this->addFlash('success', $trans->trans('calendar.deleted')); - return $this->redirectToRoute('calendar_index', ['username' => $username]); + return $this->redirectToRoute('calendar_index', ['userId' => $userId]); } - #[Route('/{username}/revoke/{id}', name: 'revoke', requirements: ['id' => "\d+"])] - public function calendarRevoke(ManagerRegistry $doctrine, string $username, string $id, TranslatorInterface $trans): Response + #[Route('/{userId}/revoke/{id}', name: 'revoke', requirements: ['id' => "\d+"])] + public function calendarRevoke(ManagerRegistry $doctrine, int $userId, string $id, TranslatorInterface $trans): Response { $instance = $doctrine->getRepository(CalendarInstance::class)->findOneById($id); if (!$instance) { @@ -279,6 +296,6 @@ public function calendarRevoke(ManagerRegistry $doctrine, string $username, stri $entityManager->flush(); $this->addFlash('success', $trans->trans('calendar.revoked')); - return $this->redirectToRoute('calendar_index', ['username' => $username]); + return $this->redirectToRoute('calendar_index', ['userId' => $userId]); } } diff --git a/src/Controller/Admin/UserController.php b/src/Controller/Admin/UserController.php index 76a5cfb7..0ab498d5 100644 --- a/src/Controller/Admin/UserController.php +++ b/src/Controller/Admin/UserController.php @@ -25,30 +25,30 @@ class UserController extends AbstractController #[Route('/', name: 'index')] public function users(ManagerRegistry $doctrine): Response { - $principals = $doctrine->getRepository(Principal::class)->findByIsMain(true); + $results = $doctrine->getRepository(Principal::class)->findAllMainPrincipalsWithUserIds(); return $this->render('users/index.html.twig', [ - 'principals' => $principals, + 'results' => $results, ]); } #[Route('/new', name: 'create')] - #[Route('/edit/{username}', name: 'edit')] - public function userCreate(ManagerRegistry $doctrine, Utils $utils, Request $request, ?string $username, TranslatorInterface $trans): Response + #[Route('/edit/{userId}', name: 'edit')] + public function userCreate(ManagerRegistry $doctrine, Utils $utils, Request $request, ?int $userId, TranslatorInterface $trans): Response { - if ($username) { - $user = $doctrine->getRepository(User::class)->findOneByUsername($username); + if ($userId) { + $user = $doctrine->getRepository(User::class)->findOneById($userId); if (!$user) { throw $this->createNotFoundException('User not found'); } $oldHash = $user->getPassword(); - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); + $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$user->getUsername()); } else { $user = new User(); $principal = new Principal(); } - $form = $this->createForm(UserType::class, $user, ['new' => !$username]); + $form = $this->createForm(UserType::class, $user, ['new' => !$userId]); $form->get('displayName')->setData($principal->getDisplayName()); $form->get('email')->setData($principal->getEmail()); @@ -62,7 +62,7 @@ public function userCreate(ManagerRegistry $doctrine, Utils $utils, Request $req $isAdmin = $form->get('isAdmin')->getData(); // Create password for user - if ($username && is_null($user->getPassword())) { + if ($userId && is_null($user->getPassword())) { // The user is not new and does not want to change its password $user->setPassword($oldHash); } else { @@ -119,14 +119,15 @@ public function userCreate(ManagerRegistry $doctrine, Utils $utils, Request $req return $this->render('users/edit.html.twig', [ 'form' => $form->createView(), - 'username' => $username, + 'userId' => $userId, + 'username' => $user->getUsername(), ]); } - #[Route('/delete/{username}', name: 'delete')] - public function userDelete(ManagerRegistry $doctrine, string $username, TranslatorInterface $trans): Response + #[Route('/delete/{userId}', name: 'delete')] + public function userDelete(ManagerRegistry $doctrine, int $userId, TranslatorInterface $trans): Response { - $user = $doctrine->getRepository(User::class)->findOneByUsername($username); + $user = $doctrine->getRepository(User::class)->findOneById($userId); if (!$user) { throw $this->createNotFoundException('User not found'); } @@ -134,7 +135,7 @@ public function userDelete(ManagerRegistry $doctrine, string $username, Translat $entityManager = $doctrine->getManager(); $entityManager->remove($user); - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); + $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$user->getUsername()); $principalProxyRead = $doctrine->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::READ_PROXY_SUFFIX); $principalProxyWrite = $doctrine->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::WRITE_PROXY_SUFFIX); @@ -142,12 +143,14 @@ public function userDelete(ManagerRegistry $doctrine, string $username, Translat $entityManager->remove($principalProxyRead); $entityManager->remove($principalProxyWrite); + $principalUri = Principal::PREFIX.$user->getUsername(); + // Remove calendars and addressbooks - $calendars = $doctrine->getRepository(CalendarInstance::class)->findByPrincipalUri(Principal::PREFIX.$username); + $calendars = $doctrine->getRepository(CalendarInstance::class)->findByPrincipalUri($principalUri); foreach ($calendars ?? [] as $instance) { // We're only removing the calendar objects / changes / and calendar if the deleted user is an owner, // which means that the underlying calendar instance should not have another principal as owner. - $hasDifferentOwner = $doctrine->getRepository(CalendarInstance::class)->hasDifferentOwner($instance->getCalendar()->getId(), Principal::PREFIX.$username); + $hasDifferentOwner = $doctrine->getRepository(CalendarInstance::class)->hasDifferentOwner($instance->getCalendar()->getId(), $principalUri); if (!$hasDifferentOwner) { foreach ($instance->getCalendar()->getObjects() ?? [] as $object) { $entityManager->remove($object); @@ -163,16 +166,16 @@ public function userDelete(ManagerRegistry $doctrine, string $username, Translat } $entityManager->remove($instance); } - $calendarsSubscriptions = $doctrine->getRepository(CalendarSubscription::class)->findByPrincipalUri(Principal::PREFIX.$username); + $calendarsSubscriptions = $doctrine->getRepository(CalendarSubscription::class)->findByPrincipalUri($principalUri); foreach ($calendarsSubscriptions ?? [] as $subscription) { $entityManager->remove($subscription); } - $schedulingObjects = $doctrine->getRepository(SchedulingObject::class)->findByPrincipalUri(Principal::PREFIX.$username); + $schedulingObjects = $doctrine->getRepository(SchedulingObject::class)->findByPrincipalUri($principalUri); foreach ($schedulingObjects ?? [] as $object) { $entityManager->remove($object); } - $addressbooks = $doctrine->getRepository(AddressBook::class)->findByPrincipalUri(Principal::PREFIX.$username); + $addressbooks = $doctrine->getRepository(AddressBook::class)->findByPrincipalUri($principalUri); foreach ($addressbooks ?? [] as $addressbook) { foreach ($addressbook->getCards() ?? [] as $card) { $entityManager->remove($card); @@ -189,12 +192,19 @@ public function userDelete(ManagerRegistry $doctrine, string $username, Translat return $this->redirectToRoute('user_index'); } - #[Route('/delegates/{username}', name: 'delegates')] - public function userDelegates(ManagerRegistry $doctrine, string $username): Response + #[Route('/delegates/{userId}', name: 'delegates')] + public function userDelegates(ManagerRegistry $doctrine, int $userId): Response { - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + + $principalUri = Principal::PREFIX.$user->getUsername(); - $allPrincipalsExcept = $doctrine->getRepository(Principal::class)->findAllExceptPrincipal(Principal::PREFIX.$username); + $principal = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri); + + $allPrincipalsExcept = $doctrine->getRepository(Principal::class)->findAllExceptPrincipal($principalUri); // Get delegates. They are not linked to the principal in itself, but to its proxies $principalProxyRead = $doctrine->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::READ_PROXY_SUFFIX); @@ -202,6 +212,7 @@ public function userDelegates(ManagerRegistry $doctrine, string $username): Resp return $this->render('users/delegates.html.twig', [ 'principal' => $principal, + 'userId' => $userId, 'delegation' => $principalProxyRead && $principalProxyWrite, 'principalProxyRead' => $principalProxyRead, 'principalProxyWrite' => $principalProxyWrite, @@ -209,10 +220,17 @@ public function userDelegates(ManagerRegistry $doctrine, string $username): Resp ]); } - #[Route('/delegation/{username}/{toggle}', name: 'delegation_toggle', requirements: ['toggle' => '(on|off)'])] - public function userToggleDelegation(ManagerRegistry $doctrine, string $username, string $toggle): Response + #[Route('/delegation/{userId}/{toggle}', name: 'delegation_toggle', requirements: ['toggle' => '(on|off)'])] + public function userToggleDelegation(ManagerRegistry $doctrine, int $userId, string $toggle): Response { - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + + $principalUri = Principal::PREFIX.$user->getUsername(); + + $principal = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri); if (!$principal) { throw $this->createNotFoundException('Principal not found'); @@ -243,16 +261,23 @@ public function userToggleDelegation(ManagerRegistry $doctrine, string $username $entityManager->flush(); - return $this->redirectToRoute('user_delegates', ['username' => $username]); + return $this->redirectToRoute('user_delegates', ['userId' => $userId]); } - #[Route('/delegates/{username}/add', name: 'delegate_add')] - public function userDelegateAdd(ManagerRegistry $doctrine, Request $request, string $username): Response + #[Route('/delegates/{userId}/add', name: 'delegate_add')] + public function userDelegateAdd(ManagerRegistry $doctrine, Request $request, int $userId): Response { if (!is_numeric($request->get('principalId'))) { throw new BadRequestHttpException(); } + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + + $principalUri = Principal::PREFIX.$user->getUsername(); + $newMemberToAdd = $doctrine->getRepository(Principal::class)->findOneById($request->get('principalId')); if (!$newMemberToAdd) { @@ -262,15 +287,15 @@ public function userDelegateAdd(ManagerRegistry $doctrine, Request $request, str // Depending on write access or not, attach to the correct principal if ('true' === $request->get('write')) { // Let's check that there wasn't a read proxy first - $principalProxyRead = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username.Principal::READ_PROXY_SUFFIX); + $principalProxyRead = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri.Principal::READ_PROXY_SUFFIX); if (!$principalProxyRead) { throw $this->createNotFoundException('Principal linked to this calendar not found'); } $principalProxyRead->removeDelegee($newMemberToAdd); // And then add the Write access - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username.Principal::WRITE_PROXY_SUFFIX); + $principal = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri.Principal::WRITE_PROXY_SUFFIX); } else { - $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username.Principal::READ_PROXY_SUFFIX); + $principal = $doctrine->getRepository(Principal::class)->findOneByUri($principalUri.Principal::READ_PROXY_SUFFIX); } if (!$principal) { @@ -281,12 +306,17 @@ public function userDelegateAdd(ManagerRegistry $doctrine, Request $request, str $entityManager = $doctrine->getManager(); $entityManager->flush(); - return $this->redirectToRoute('user_delegates', ['username' => $username]); + return $this->redirectToRoute('user_delegates', ['userId' => $userId]); } - #[Route('/delegates/{username}/remove/{principalProxyId}/{delegateId}', name: 'delegate_remove', requirements: ['principalProxyId' => "\d+", 'delegateId' => "\d+"])] - public function userDelegateRemove(ManagerRegistry $doctrine, Request $request, string $username, int $principalProxyId, int $delegateId): Response + #[Route('/delegates/{userId}/remove/{principalProxyId}/{delegateId}', name: 'delegate_remove', requirements: ['principalProxyId' => "\d+", 'delegateId' => "\d+"])] + public function userDelegateRemove(ManagerRegistry $doctrine, Request $request, int $userId, int $principalProxyId, int $delegateId): Response { + $user = $doctrine->getRepository(User::class)->findOneById($userId); + if (!$user) { + throw $this->createNotFoundException('User not found'); + } + $principalProxy = $doctrine->getRepository(Principal::class)->findOneById($principalProxyId); if (!$principalProxy) { throw $this->createNotFoundException('Principal linked to this calendar not found'); @@ -301,6 +331,6 @@ public function userDelegateRemove(ManagerRegistry $doctrine, Request $request, $entityManager = $doctrine->getManager(); $entityManager->flush(); - return $this->redirectToRoute('user_delegates', ['username' => $username]); + return $this->redirectToRoute('user_delegates', ['userId' => $userId]); } } diff --git a/src/Controller/Api/ApiController.php b/src/Controller/Api/ApiController.php index 7325f651..81b45022 100644 --- a/src/Controller/Api/ApiController.php +++ b/src/Controller/Api/ApiController.php @@ -7,6 +7,7 @@ use App\Entity\CalendarSubscription; use App\Entity\Principal; use App\Entity\SchedulingObject; +use App\Entity\User; use Doctrine\Persistence\ManagerRegistry; use Sabre\DAV\Sharing\Plugin as SharingPlugin; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -26,7 +27,7 @@ class ApiController extends AbstractController */ private function validateUsername(string $username): bool { - return !empty($username) && is_string($username) && !preg_match('/[^a-zA-Z0-9_-]/', $username); + return !empty($username) && is_string($username) && !preg_match('/[^a-zA-Z0-9_.@-]/', $username); } /** @@ -39,6 +40,16 @@ private function getTimestamp(): string return date('c'); } + /** + * Resolves a User entity from a userId, or returns a JSON error response. + * + * @return User|null The User entity, or null if not found + */ + private function resolveUser(ManagerRegistry $doctrine, int $userId): ?User + { + return $doctrine->getRepository(User::class)->findOneById($userId); + } + /** * Health check endpoint. * @@ -53,7 +64,7 @@ public function healthCheck(Request $request): JsonResponse } /** - * Retrieves a list of users (with their id, uri, username, and displayname). + * Retrieves a list of users (with their user_id, principal_id, uri, username, and displayname). * * @param Request $request The HTTP GET request * @@ -62,11 +73,13 @@ public function healthCheck(Request $request): JsonResponse #[Route('/users', name: 'users', methods: ['GET'])] public function getUsers(Request $request, ManagerRegistry $doctrine): JsonResponse { - $principals = $doctrine->getRepository(Principal::class)->findByIsMain(true); + $results = $doctrine->getRepository(Principal::class)->findAllMainPrincipalsWithUserIds(); $users = []; - foreach ($principals as $principal) { + foreach ($results as $result) { + $principal = $result[0]; $users[] = [ + 'user_id' => $result['userId'], 'principal_id' => $principal->getId(), 'uri' => $principal->getUri(), 'username' => $principal->getUsername(), @@ -83,28 +96,34 @@ public function getUsers(Request $request, ManagerRegistry $doctrine): JsonRespo } /** - * Retrieves details of a specific user (id, uri, username, displayname, email). + * Retrieves details of a specific user (user_id, principal_id, uri, username, displayname, email). * - * @param Request $request The HTTP GET request - * @param string $username The username of the user whose details are to be retrieved + * @param Request $request The HTTP GET request + * @param int $userId The ID of the user whose details are to be retrieved * * @return JsonResponse A JSON response containing the user details */ - #[Route('/users/{username}', name: 'user_detail', methods: ['GET'], requirements: ['username' => '[a-zA-Z0-9_-]+'])] - public function getUserDetails(Request $request, ManagerRegistry $doctrine, string $username): JsonResponse + #[Route('/users/{userId}', name: 'user_detail', methods: ['GET'], requirements: ['userId' => '\d+'])] + public function getUserDetails(Request $request, ManagerRegistry $doctrine, int $userId): JsonResponse { - $user = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); - + $user = $this->resolveUser($doctrine, $userId); if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } + $principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$user->getUsername()); + + if (!$principal) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + $data = [ - 'principal_id' => $user->getId(), - 'uri' => $user->getUri(), - 'username' => $user->getUsername(), - 'displayname' => $user->getDisplayName(), - 'email' => $user->getEmail(), + 'user_id' => $user->getId(), + 'principal_id' => $principal->getId(), + 'uri' => $principal->getUri(), + 'username' => $principal->getUsername(), + 'displayname' => $principal->getDisplayName(), + 'email' => $principal->getEmail(), ]; $response = [ @@ -119,20 +138,27 @@ public function getUserDetails(Request $request, ManagerRegistry $doctrine, stri /** * Retrieves a list of calendars for a specific user, including user calendars, shared calendars, and subscriptions. * - * @param Request $request The HTTP GET request - * @param string $username The username of the user whose calendars are to be retrieved + * @param Request $request The HTTP GET request + * @param int $userId The ID of the user whose calendars are to be retrieved * * @return JsonResponse A JSON response containing the list of calendars for the specified user */ - #[Route('/calendars/{username}', name: 'calendars', methods: ['GET'], requirements: ['username' => '[a-zA-Z0-9_-]+'])] - public function getUserCalendars(Request $request, string $username, ManagerRegistry $doctrine): JsonResponse + #[Route('/calendars/{userId}', name: 'calendars', methods: ['GET'], requirements: ['userId' => '\d+'])] + public function getUserCalendars(Request $request, int $userId, ManagerRegistry $doctrine): JsonResponse { - if (!$doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username)) { + $user = $this->resolveUser($doctrine, $userId); + if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } - $allCalendars = $doctrine->getRepository(CalendarInstance::class)->findByPrincipalUri(Principal::PREFIX.$username); - $allSubscriptions = $doctrine->getRepository(CalendarSubscription::class)->findByPrincipalUri(Principal::PREFIX.$username); + $principalUri = Principal::PREFIX.$user->getUsername(); + + if (!$doctrine->getRepository(Principal::class)->findOneByUri($principalUri)) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + + $allCalendars = $doctrine->getRepository(CalendarInstance::class)->findByPrincipalUri($principalUri); + $allSubscriptions = $doctrine->getRepository(CalendarSubscription::class)->findByPrincipalUri($principalUri); $calendars = []; $sharedCalendars = []; @@ -191,19 +217,26 @@ public function getUserCalendars(Request $request, string $username, ManagerRegi * Retrieves details of a specific calendar for a specific user (id, uri, displayname, description, number of events, notes, and tasks). * * @param Request $request The HTTP GET request - * @param string $username The username of the user whose calendar details are to be retrieved + * @param int $userId The ID of the user whose calendar details are to be retrieved * @param int $calendar_id The ID of the calendar whose details are to be retrieved * * @return JsonResponse A JSON response containing the calendar details */ - #[Route('/calendars/{username}/{calendar_id}', name: 'calendar_details', methods: ['GET'], requirements: ['calendar_id' => "\d+", 'username' => '[a-zA-Z0-9_-]+'])] - public function getUserCalendarDetails(Request $request, string $username, int $calendar_id, ManagerRegistry $doctrine): JsonResponse + #[Route('/calendars/{userId}/{calendar_id}', name: 'calendar_details', methods: ['GET'], requirements: ['calendar_id' => '\d+', 'userId' => '\d+'])] + public function getUserCalendarDetails(Request $request, int $userId, int $calendar_id, ManagerRegistry $doctrine): JsonResponse { - if (!$doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username)) { + $user = $this->resolveUser($doctrine, $userId); + if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } - $allCalendars = $doctrine->getRepository(CalendarInstance::class)->findByPrincipalUri(Principal::PREFIX.$username); + $principalUri = Principal::PREFIX.$user->getUsername(); + + if (!$doctrine->getRepository(Principal::class)->findOneByUri($principalUri)) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + + $allCalendars = $doctrine->getRepository(CalendarInstance::class)->findByPrincipalUri($principalUri); $calendar_details = []; foreach ($allCalendars as $calendar) { @@ -242,18 +275,25 @@ public function getUserCalendarDetails(Request $request, string $username, int $ /** * Creates a new calendar for a specific user. * - * @param Request $request The HTTP POST request - * @param string $username The username of the user for whom the calendar is to be created + * @param Request $request The HTTP POST request + * @param int $userId The ID of the user for whom the calendar is to be created * * @return JsonResponse A JSON response indicating the success or failure of the operation */ - #[Route('/calendars/{username}/create', name: 'calendar_create', methods: ['POST'], requirements: ['username' => '[a-zA-Z0-9_-]+'])] - public function createNewUserCalendar(Request $request, string $username, ManagerRegistry $doctrine): JsonResponse + #[Route('/calendars/{userId}/create', name: 'calendar_create', methods: ['POST'], requirements: ['userId' => '\d+'])] + public function createNewUserCalendar(Request $request, int $userId, ManagerRegistry $doctrine): JsonResponse { - if (!$doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username)) { + $user = $this->resolveUser($doctrine, $userId); + if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } + $principalUri = Principal::PREFIX.$user->getUsername(); + + if (!$doctrine->getRepository(Principal::class)->findOneByUri($principalUri)) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + // Parse JSON body $data = json_decode($request->getContent(), true); if (JSON_ERROR_NONE !== json_last_error()) { @@ -270,7 +310,7 @@ public function createNewUserCalendar(Request $request, string $username, Manage } $uriCheck = $doctrine->getRepository(CalendarInstance::class)->findOneBy([ - 'principalUri' => Principal::PREFIX.$username, + 'principalUri' => $principalUri, 'uri' => $calendarURI, ]); if ($uriCheck) { @@ -314,7 +354,7 @@ public function createNewUserCalendar(Request $request, string $username, Manage ->setDescription($calendarDescription) ->setDisplayName($calendarName) ->setUri($calendarURI) - ->setPrincipalUri(Principal::PREFIX.$username); + ->setPrincipalUri($principalUri); $entityManager->persist($calendarInstance); $entityManager->flush(); @@ -338,21 +378,28 @@ public function createNewUserCalendar(Request $request, string $username, Manage * Edits an existing calendar for a specific user. * * @param Request $request The HTTP POST request - * @param string $username The username of the user whose calendar is to be edited + * @param int $userId The ID of the user whose calendar is to be edited * @param int $calendar_id The ID of the calendar to be edited * * @return JsonResponse A JSON response indicating the success or failure of the operation */ - #[Route('/calendars/{username}/{calendar_id}/edit', name: 'calendar_edit', methods: ['POST'], requirements: ['calendar_id' => "\d+", 'username' => '[a-zA-Z0-9_-]+'])] - public function editUserCalendar(Request $request, string $username, int $calendar_id, ManagerRegistry $doctrine): JsonResponse + #[Route('/calendars/{userId}/{calendar_id}', name: 'calendar_edit', methods: ['PUT', 'PATCH'], requirements: ['calendar_id' => '\d+', 'userId' => '\d+'])] + public function editUserCalendar(Request $request, int $userId, int $calendar_id, ManagerRegistry $doctrine): JsonResponse { - if (!$doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username)) { + $user = $this->resolveUser($doctrine, $userId); + if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } + $principalUri = Principal::PREFIX.$user->getUsername(); + + if (!$doctrine->getRepository(Principal::class)->findOneByUri($principalUri)) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + $ownerInstance = $doctrine->getRepository(CalendarInstance::class)->findOneBy([ 'id' => $calendar_id, - 'principalUri' => Principal::PREFIX.$username, + 'principalUri' => $principalUri, ]); if (!$ownerInstance) { @@ -418,21 +465,28 @@ public function editUserCalendar(Request $request, string $username, int $calend * Deletes a specific calendar for a specific user. * * @param Request $request The HTTP POST request - * @param string $username The username of the user whose calendar is to be deleted + * @param int $userId The ID of the user whose calendar is to be deleted * @param int $calendar_id The ID of the calendar to be deleted * * @return JsonResponse A JSON response indicating the success or failure of the operation */ - #[Route('/calendars/{username}/{calendar_id}/delete', name: 'calendar_delete', methods: ['POST'], requirements: ['calendar_id' => "\d+", 'username' => '[a-zA-Z0-9_-]+'])] - public function deleteUserCalendar(Request $request, string $username, int $calendar_id, ManagerRegistry $doctrine) + #[Route('/calendars/{userId}/{calendar_id}', name: 'calendar_delete', methods: ['DELETE'], requirements: ['calendar_id' => '\d+', 'userId' => '\d+'])] + public function deleteUserCalendar(Request $request, int $userId, int $calendar_id, ManagerRegistry $doctrine): JsonResponse { - if (!$doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username)) { + $user = $this->resolveUser($doctrine, $userId); + if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } + $principalUri = Principal::PREFIX.$user->getUsername(); + + if (!$doctrine->getRepository(Principal::class)->findOneByUri($principalUri)) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + $instance = $doctrine->getRepository(CalendarInstance::class)->findOneBy([ 'id' => $calendar_id, - 'principalUri' => Principal::PREFIX.$username, + 'principalUri' => $principalUri, ]); if (!$instance) { @@ -480,21 +534,28 @@ public function deleteUserCalendar(Request $request, string $username, int $cale * Retrieves a list of shares for a specific calendar of a specific user (id, username, displayname, email, write_access). * * @param Request $request The HTTP GET request - * @param string $username The username of the user whose calendar shares are to be retrieved + * @param int $userId The ID of the user whose calendar shares are to be retrieved * @param int $calendar_id The ID of the calendar whose shares are to be retrieved * * @return JsonResponse A JSON response containing the list of calendar shares */ - #[Route('/calendars/{username}/shares/{calendar_id}', name: 'calendars_shares', methods: ['GET'], requirements: ['calendar_id' => "\d+", 'username' => '[a-zA-Z0-9_-]+'])] - public function getUserCalendarsShares(Request $request, string $username, int $calendar_id, ManagerRegistry $doctrine): JsonResponse + #[Route('/calendars/{userId}/shares/{calendar_id}', name: 'calendars_shares', methods: ['GET'], requirements: ['calendar_id' => '\d+', 'userId' => '\d+'])] + public function getUserCalendarsShares(Request $request, int $userId, int $calendar_id, ManagerRegistry $doctrine): JsonResponse { - if (!$doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username)) { + $user = $this->resolveUser($doctrine, $userId); + if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } + $principalUri = Principal::PREFIX.$user->getUsername(); + + if (!$doctrine->getRepository(Principal::class)->findOneByUri($principalUri)) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + $ownerInstance = $doctrine->getRepository(CalendarInstance::class)->findOneBy([ 'id' => $calendar_id, - 'principalUri' => Principal::PREFIX.$username, + 'principalUri' => $principalUri, ]); if (!$ownerInstance) { @@ -530,21 +591,28 @@ public function getUserCalendarsShares(Request $request, string $username, int $ * Sets or updates a share for a specific calendar of a specific user. * * @param Request $request The HTTP POST request - * @param string $username The username of the user whose calendar share is to be set or updated + * @param int $userId The ID of the user whose calendar share is to be set or updated * @param string $calendar_id The ID of the calendar whose share is to be set or updated * * @return JsonResponse A JSON response indicating the success or failure of the operation */ - #[Route('/calendars/{username}/share/{calendar_id}/add', name: 'calendars_share', methods: ['POST'], requirements: ['calendar_id' => "\d+", 'username' => '[a-zA-Z0-9_-]+'])] - public function setUserCalendarsShare(Request $request, string $username, int $calendar_id, ManagerRegistry $doctrine): JsonResponse + #[Route('/calendars/{userId}/share/{calendar_id}/add', name: 'calendars_share', methods: ['POST'], requirements: ['calendar_id' => '\d+', 'userId' => '\d+'])] + public function setUserCalendarsShare(Request $request, int $userId, int $calendar_id, ManagerRegistry $doctrine): JsonResponse { - if (!$doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username)) { + $user = $this->resolveUser($doctrine, $userId); + if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } + $principalUri = Principal::PREFIX.$user->getUsername(); + + if (!$doctrine->getRepository(Principal::class)->findOneByUri($principalUri)) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + $ownerInstance = $doctrine->getRepository(CalendarInstance::class)->findOneBy([ 'id' => $calendar_id, - 'principalUri' => Principal::PREFIX.$username, + 'principalUri' => $principalUri, ]); if (!$ownerInstance) { @@ -601,21 +669,28 @@ public function setUserCalendarsShare(Request $request, string $username, int $c * Removes a share for a specific calendar of a specific user. * * @param Request $request The HTTP POST request - * @param string $username The username of the user whose calendar share is to be removed + * @param int $userId The ID of the user whose calendar share is to be removed * @param string $calendar_id The ID of the calendar whose share is to be removed * * @return JsonResponse A JSON response indicating the success or failure of the operation */ - #[Route('/calendars/{username}/share/{calendar_id}/remove', name: 'calendars_share_remove', methods: ['POST'], requirements: ['calendar_id' => "\d+", 'username' => '[a-zA-Z0-9_-]+'])] - public function removeUserCalendarsShare(Request $request, string $username, int $calendar_id, ManagerRegistry $doctrine): JsonResponse + #[Route('/calendars/{userId}/share/{calendar_id}/remove', name: 'calendars_share_remove', methods: ['POST'], requirements: ['calendar_id' => '\d+', 'userId' => '\d+'])] + public function removeUserCalendarsShare(Request $request, int $userId, int $calendar_id, ManagerRegistry $doctrine): JsonResponse { - if (!$doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username)) { + $user = $this->resolveUser($doctrine, $userId); + if (!$user) { return $this->json(['status' => 'error', 'message' => 'User Not Found', 'timestamp' => $this->getTimestamp()], 404); } + $principalUri = Principal::PREFIX.$user->getUsername(); + + if (!$doctrine->getRepository(Principal::class)->findOneByUri($principalUri)) { + return $this->json(['status' => 'error', 'message' => 'Principal Not Found', 'timestamp' => $this->getTimestamp()], 404); + } + $ownerInstance = $doctrine->getRepository(CalendarInstance::class)->findOneBy([ 'id' => $calendar_id, - 'principalUri' => Principal::PREFIX.$username, + 'principalUri' => $principalUri, ]); if (!$ownerInstance) { diff --git a/src/Repository/PrincipalRepository.php b/src/Repository/PrincipalRepository.php index 6247ef16..4fce0d08 100644 --- a/src/Repository/PrincipalRepository.php +++ b/src/Repository/PrincipalRepository.php @@ -32,4 +32,24 @@ public function findAllExceptPrincipal(string $principalUri) ->getQuery() ->getResult(); } + + /** + * @return array + */ + public function findAllMainPrincipalsWithUserIds(): array + { + return $this->createQueryBuilder('p') + ->addSelect('u.id AS userId') + ->leftJoin( + \App\Entity\User::class, + 'u', + \Doctrine\ORM\Query\Expr\Join::WITH, + 'CONCAT(:prefix, u.username) = p.uri' + ) + ->andWhere('p.isMain = :isMain') + ->setParameter('isMain', true) + ->setParameter('prefix', Principal::PREFIX) + ->getQuery() + ->getResult(); + } } diff --git a/templates/_partials/add_delegate_modal.html.twig b/templates/_partials/add_delegate_modal.html.twig index 1a4b157b..6eaba06e 100644 --- a/templates/_partials/add_delegate_modal.html.twig +++ b/templates/_partials/add_delegate_modal.html.twig @@ -26,7 +26,7 @@ diff --git a/templates/_partials/delegate_row.html.twig b/templates/_partials/delegate_row.html.twig index 24d41073..de55e46f 100644 --- a/templates/_partials/delegate_row.html.twig +++ b/templates/_partials/delegate_row.html.twig @@ -12,7 +12,7 @@
⚠ {{ "remove"|trans }} @@ -21,6 +21,6 @@

{{ "users.username"|trans }} : {{ delegate.username }}

{{ "users.uri"|trans }} : {{ delegate.uri }} -
\ No newline at end of file + diff --git a/templates/addressbooks/edit.html.twig b/templates/addressbooks/edit.html.twig index 0d9b1f23..4f165149 100644 --- a/templates/addressbooks/edit.html.twig +++ b/templates/addressbooks/edit.html.twig @@ -3,7 +3,7 @@ {% block body %} -{% include '_partials/back_button.html.twig' with { url: path('addressbook_index', {username: username}), text: "addressbooks.back"|trans({'user': principal.displayName }) } %} +{% include '_partials/back_button.html.twig' with { url: path('addressbook_index', {userId: userId}), text: "addressbooks.back"|trans({'user': principal.displayName }) } %} {% if addressbook.id %}

{{ "addressbooks.edit"|trans({'name': addressbook.displayName }) }}

diff --git a/templates/addressbooks/index.html.twig b/templates/addressbooks/index.html.twig index c31ea98e..ec17373c 100644 --- a/templates/addressbooks/index.html.twig +++ b/templates/addressbooks/index.html.twig @@ -5,7 +5,7 @@ {% include '_partials/back_button.html.twig' with { url: path('user_index'), text: "users.back"|trans } %} -

{{ "addressbooks.for"|trans({'who': principal.displayName}) }} + {{ "addressbooks.new"|trans }}

+

{{ "addressbooks.for"|trans({'who': principal.displayName}) }} + {{ "addressbooks.new"|trans }}

{% for addressbook in addressbooks %} @@ -13,10 +13,10 @@
{{ addressbook.displayName }}
- ✎ {{ "edit"|trans }} + ✎ {{ "edit"|trans }} ⚠ {{ "delete"|trans }} @@ -25,10 +25,10 @@

{{ addressbook.description }}

{{ "addressbooks.uri"|trans }} : {{ addressbook.uri }} — {{ "addressbooks.contacts"|trans({'%count%': addressbook.cards|length}) }}
- ✎ {{ "edit"|trans }} + ✎ {{ "edit"|trans }} ⚠ {{ "delete"|trans }} diff --git a/templates/calendars/edit.html.twig b/templates/calendars/edit.html.twig index d6ced7f9..a499cd26 100644 --- a/templates/calendars/edit.html.twig +++ b/templates/calendars/edit.html.twig @@ -3,7 +3,7 @@ {% block body %} -{% include '_partials/back_button.html.twig' with { url: path('calendar_index', {username: username}), text: "calendars.back"|trans({'user': principal.displayName }) } %} +{% include '_partials/back_button.html.twig' with { url: path('calendar_index', {userId: userId}), text: "calendars.back"|trans({'user': principal.displayName }) } %} {% if calendar.id %}

{{ "calendars.edit"|trans({'name': calendar.displayName }) }}

diff --git a/templates/calendars/index.html.twig b/templates/calendars/index.html.twig index 0a106ec9..822dc5ce 100644 --- a/templates/calendars/index.html.twig +++ b/templates/calendars/index.html.twig @@ -5,7 +5,7 @@ {% include '_partials/back_button.html.twig' with { url: path('user_index'), text: "users.back"|trans } %} -

{{ "calendars.for"|trans({'who': principal.displayName}) }} + {{ "calendars.new"|trans }}

+

{{ "calendars.for"|trans({'who': principal.displayName}) }} + {{ "calendars.new"|trans }}

{% for compoundObject in calendars %} @@ -25,15 +25,15 @@ {% if not calendar.isPublic() %} {% endif %} - ✎ {{ "edit"|trans }} + ✎ {{ "edit"|trans }} ⚠ {{ "delete"|trans }} @@ -51,15 +51,15 @@ {% if not calendar.isPublic() %} {% endif %} - ✎ {{ "edit"|trans }} + ✎ {{ "edit"|trans }} ⚠ {{ "delete"|trans }} @@ -88,10 +88,10 @@  
- ✎ {{ "edit"|trans }} + ✎ {{ "edit"|trans }} 🚫 {{ "revoke"|trans }} @@ -106,10 +106,10 @@ — {{ "calendars.entries"|trans({'%count%': calendar.calendar.objects|length}) }}

{{ calendar.description }}

@@ -150,7 +150,7 @@ — {{ "calendars.entries"|trans({'%count%': calendar.calendar.objects|length}) }}
{% endfor %} diff --git a/templates/users/delegates.html.twig b/templates/users/delegates.html.twig index b60a00e2..d3a05698 100644 --- a/templates/users/delegates.html.twig +++ b/templates/users/delegates.html.twig @@ -17,7 +17,7 @@ {% if delegation %} {% for delegate in principalProxyRead.delegees %} {% include '_partials/delegate_row.html.twig' with {has_write: false} %} @@ -28,7 +28,7 @@ {% else %} {% endif %} diff --git a/templates/users/index.html.twig b/templates/users/index.html.twig index 02df668b..099fb403 100644 --- a/templates/users/index.html.twig +++ b/templates/users/index.html.twig @@ -6,19 +6,21 @@

{{ "title.users_and_resources"|trans }}+ {{ "users.new"|trans }}

-{% for principal in principals %} +{% for result in results %} + {% set principal = result[0] %} + {% set userId = result.userId %}
{{ principal.displayName }} ‹{{ principal.email }}›
- 💁 {{ "users.delegates"|trans }} - 🗓 {{ "users.calendars"|trans }} - 📖 {{ "users.addressbooks"|trans }} - ✎ {{ "edit"|trans }} + 💁 {{ "users.delegates"|trans }} + 🗓 {{ "users.calendars"|trans }} + 📖 {{ "users.addressbooks"|trans }} + ✎ {{ "edit"|trans }} ⚠ {{ "delete"|trans }} @@ -27,13 +29,13 @@

{{ "users.username"|trans }} : {{ principal.username }}

{{ "users.uri"|trans }} : {{ principal.uri }}{% if principal.isAdmin %} — {{ "users.administrator"|trans }}{% endif %}
- 💁 {{ "users.delegates"|trans }} - 🗓 {{ "users.calendars"|trans }} - 📖 {{ "users.addressbooks"|trans }} - ✎ {{ "edit"|trans }} + 💁 {{ "users.delegates"|trans }} + 🗓 {{ "users.calendars"|trans }} + 📖 {{ "users.addressbooks"|trans }} + ✎ {{ "edit"|trans }} ⚠ {{ "delete"|trans }} diff --git a/tests/Functional/AddressBookControllerTest.php b/tests/Functional/AddressBookControllerTest.php index c6bb6865..1642fdd8 100644 --- a/tests/Functional/AddressBookControllerTest.php +++ b/tests/Functional/AddressBookControllerTest.php @@ -3,18 +3,30 @@ namespace App\Tests\Functional; use App\Entity\AddressBook; +use App\Entity\User; use App\Security\AdminUser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class AddressBookControllerTest extends WebTestCase { + private function getUserId($client, string $username): int + { + $userRepository = static::getContainer()->get('doctrine.orm.entity_manager')->getRepository(User::class); + $user = $userRepository->findOneByUsername($username); + + return $user->getId(); + } + public function testAddressBookIndex(): void { $user = new AdminUser('admin', 'test'); $client = static::createClient(); $client->loginUser($user); - $client->request('GET', '/addressbooks/test_user'); + + $userId = $this->getUserId($client, 'test_user'); + + $client->request('GET', '/addressbooks/'.$userId); $this->assertResponseIsSuccessful(); @@ -31,10 +43,12 @@ public function testAddressBookEdit(): void $client = static::createClient(); $client->loginUser($user); + $userId = $this->getUserId($client, 'test_user'); + $addressbookRepository = static::getContainer()->get('doctrine.orm.entity_manager')->getRepository(AddressBook::class); $addressbook = $addressbookRepository->findOneByDisplayName('default.addressbook.title'); - $client->request('GET', '/addressbooks/test_user/edit/'.$addressbook->getId()); + $client->request('GET', '/addressbooks/'.$userId.'/edit/'.$addressbook->getId()); $this->assertResponseIsSuccessful(); @@ -43,7 +57,7 @@ public function testAddressBookEdit(): void $client->submitForm('address_book_save'); - $this->assertResponseRedirects('/addressbooks/test_user'); + $this->assertResponseRedirects('/addressbooks/'.$userId); $client->followRedirect(); $this->assertSelectorTextContains('h5', 'default.addressbook.title'); @@ -55,7 +69,10 @@ public function testAddressBookNew(): void $client = static::createClient(); $client->loginUser($user); - $crawler = $client->request('GET', '/addressbooks/test_user/new'); + + $userId = $this->getUserId($client, 'test_user'); + + $crawler = $client->request('GET', '/addressbooks/'.$userId.'/new'); $this->assertResponseIsSuccessful(); @@ -71,7 +88,7 @@ public function testAddressBookNew(): void 'address_book[description]' => 'new address book', ]); - $this->assertResponseRedirects('/addressbooks/test_user'); + $this->assertResponseRedirects('/addressbooks/'.$userId); $client->followRedirect(); $this->assertSelectorTextContains('h5', 'default.addressbook.title'); @@ -85,12 +102,14 @@ public function testAddressBookDelete(): void $client = static::createClient(); $client->loginUser($user); + $userId = $this->getUserId($client, 'test_user'); + $addressbookRepository = static::getContainer()->get('doctrine.orm.entity_manager')->getRepository(AddressBook::class); $addressbook = $addressbookRepository->findOneByDisplayName('default.addressbook.title'); - $client->request('GET', '/addressbooks/test_user/delete/'.$addressbook->getId()); + $client->request('GET', '/addressbooks/'.$userId.'/delete/'.$addressbook->getId()); - $this->assertResponseRedirects('/addressbooks/test_user'); + $this->assertResponseRedirects('/addressbooks/'.$userId); $client->followRedirect(); $this->assertSelectorTextNotContains('h5', 'default.addressbook.title'); diff --git a/tests/Functional/ApiControllerTest.php b/tests/Functional/ApiControllerTest.php index db250f68..995b56c7 100644 --- a/tests/Functional/ApiControllerTest.php +++ b/tests/Functional/ApiControllerTest.php @@ -6,6 +6,32 @@ class ApiControllerTest extends WebTestCase { + /* + * Helper function to get an existing user ID from the user list + * + * @param int $index Index of the user in the list (0 - first user, 1 - second user) + * @param mixed $client + * + * @return int User ID + */ + private function getUserId($client, int $index): int + { + $client->request('GET', '/api/v1/users', [], [], [ + 'HTTP_ACCEPT' => 'application/json', + 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], + ]); + + $this->assertResponseIsSuccessful(); + $this->assertResponseHeaderSame('Content-Type', 'application/json'); + + $data = json_decode($client->getResponse()->getContent(), true); + + $this->assertArrayHasKey('data', $data); + $this->assertStringContainsString('test_user', $data['data'][$index]['username']); + + return $data['data'][$index]['user_id']; + } + /* * Helper function to get an existing username from the user list * @@ -35,15 +61,15 @@ private function getUserUsername($client, int $index): string /* * Helper function to get an existing calendar ID from the user calendar list * - * @param mixed $client - * @param string $username - * @param bool $default Whether to get the default calendar (true) or the second calendar (false) + * @param mixed $client + * @param int $userId + * @param bool $default Whether to get the default calendar (true) or the second calendar (false) * * @return int Calendar ID */ - private function getCalendarId($client, string $username, bool $default = true): int + private function getCalendarId($client, int $userId, bool $default = true): int { - $client->request('GET', '/api/v1/calendars/'.$username, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -130,6 +156,7 @@ public function testUserList(): void $data = json_decode($client->getResponse()->getContent(), true); // Check if user1 is present in db + $this->assertArrayHasKey('user_id', $data['data'][0]); $this->assertArrayHasKey('principal_id', $data['data'][0]); $this->assertArrayHasKey('uri', $data['data'][0]); $this->assertStringContainsString('principals/test_user', $data['data'][0]['uri']); @@ -137,6 +164,7 @@ public function testUserList(): void $this->assertStringContainsString('test_user', $data['data'][0]['username']); // Check if user2 is present in db + $this->assertArrayHasKey('user_id', $data['data'][1]); $this->assertArrayHasKey('principal_id', $data['data'][1]); $this->assertArrayHasKey('uri', $data['data'][1]); $this->assertStringContainsString('principals/test_user2', $data['data'][1]['uri']); @@ -152,11 +180,12 @@ public function testUserDetails(): void // Create client once $client = static::createClient(); - // Get username from existing user lists + // Get userId and username from existing user lists + $userId = $this->getUserId($client, 0); $username = $this->getUserUsername($client, 0); // Check user details endpoint - $client->request('GET', '/api/v1/users/'.$username, [], [], [ + $client->request('GET', '/api/v1/users/'.$userId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -166,6 +195,8 @@ public function testUserDetails(): void $data = json_decode($client->getResponse()->getContent(), true); // Check if user details are correct + $this->assertArrayHasKey('user_id', $data['data']); + $this->assertEquals($userId, $data['data']['user_id']); $this->assertArrayHasKey('displayname', $data['data']); $this->assertStringContainsString('Test User', $data['data']['displayname']); $this->assertArrayHasKey('email', $data['data']); @@ -179,9 +210,9 @@ public function testUserDetails(): void public function testUserCalendarsList(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); + $userId = $this->getUserId($client, 0); - $client->request('GET', '/api/v1/calendars/'.$username, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -206,10 +237,10 @@ public function testUserCalendarsList(): void public function testUserCalendarDetails(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); + $userId = $this->getUserId($client, 0); // Get calendar list to retrieve calendar ID - $client->request('GET', '/api/v1/calendars/'.$username, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -220,7 +251,7 @@ public function testUserCalendarDetails(): void $calendar_id = $data['data']['user_calendars'][0]['id']; // Check calendar details endpoint - $client->request('GET', '/api/v1/calendars/'.$username.'/'.$calendar_id, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId.'/'.$calendar_id, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -250,7 +281,7 @@ public function testUserCalendarDetails(): void public function testCreateUserCalendar(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); + $userId = $this->getUserId($client, 0); // Create calendar API request with JSON body $payload = [ @@ -262,7 +293,7 @@ public function testCreateUserCalendar(): void 'notes_support' => false, ]; - $client->request('POST', '/api/v1/calendars/'.$username.'/create', [], [], [ + $client->request('POST', '/api/v1/calendars/'.$userId.'/create', [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], 'CONTENT_TYPE' => 'application/json', @@ -281,7 +312,7 @@ public function testCreateUserCalendar(): void // Check if calendar is created $calendarId = $data['data']['calendar_id']; - $client->request('GET', '/api/v1/calendars/'.$username.'/'.$calendarId, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId.'/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -304,8 +335,8 @@ public function testCreateUserCalendar(): void public function testEditUserCalendar(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); - $calendarId = $this->getCalendarId($client, $username, true); + $userId = $this->getUserId($client, 0); + $calendarId = $this->getCalendarId($client, $userId, true); // Edit user default calendar $payload = [ @@ -315,7 +346,7 @@ public function testEditUserCalendar(): void 'tasks_support' => true, 'notes_support' => true, ]; - $client->request('POST', '/api/v1/calendars/'.$username.'/'.$calendarId.'/edit', [], [], [ + $client->request('PATCH', '/api/v1/calendars/'.$userId.'/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], 'CONTENT_TYPE' => 'application/json', @@ -328,7 +359,7 @@ public function testEditUserCalendar(): void $this->assertEquals('success', $data['status']); // Check if edits were applied - $client->request('GET', '/api/v1/calendars/'.$username.'/'.$calendarId, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId.'/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -354,11 +385,11 @@ public function testEditUserCalendar(): void public function testGetUserCalendarSharesEmpty(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); - $calendarId = $this->getCalendarId($client, $username, true); + $userId = $this->getUserId($client, 0); + $calendarId = $this->getCalendarId($client, $userId, true); // Get shares for user default calendar - $client->request('GET', '/api/v1/calendars/'.$username.'/shares/'.$calendarId, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId.'/shares/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -378,16 +409,16 @@ public function testGetUserCalendarSharesEmpty(): void public function testShareUserCalendar(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); + $userId = $this->getUserId($client, 0); $shareeUsername = $this->getUserUsername($client, 1); - $calendarId = $this->getCalendarId($client, $username, true); + $calendarId = $this->getCalendarId($client, $userId, true); // Share user default calendar to test_user2 $payload = [ 'username' => $shareeUsername, 'write_access' => false, ]; - $client->request('POST', '/api/v1/calendars/'.$username.'/share/'.$calendarId.'/add', [], [], [ + $client->request('POST', '/api/v1/calendars/'.$userId.'/share/'.$calendarId.'/add', [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], 'CONTENT_TYPE' => 'application/json', @@ -400,7 +431,7 @@ public function testShareUserCalendar(): void $this->assertEquals('success', $data['status']); // Check if share was applied - $client->request('GET', '/api/v1/calendars/'.$username.'/shares/'.$calendarId, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId.'/shares/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -420,15 +451,15 @@ public function testShareUserCalendar(): void public function testUnshareUserCalendar(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); + $userId = $this->getUserId($client, 0); $shareeUsername = $this->getUserUsername($client, 1); - $calendarId = $this->getCalendarId($client, $username, true); + $calendarId = $this->getCalendarId($client, $userId, true); // Unshare user default calendar from test_user2 $payload = [ 'username' => $shareeUsername, ]; - $client->request('POST', '/api/v1/calendars/'.$username.'/share/'.$calendarId.'/remove', [], [], [ + $client->request('POST', '/api/v1/calendars/'.$userId.'/share/'.$calendarId.'/remove', [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], 'CONTENT_TYPE' => 'application/json', @@ -440,7 +471,7 @@ public function testUnshareUserCalendar(): void $this->assertEquals('success', $data['status']); // Check if unshare was applied - $client->request('GET', '/api/v1/calendars/'.$username.'/shares/'.$calendarId, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId.'/shares/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); @@ -459,7 +490,7 @@ public function testUnshareUserCalendar(): void public function testCreateUserCalendarNoComponents(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); + $userId = $this->getUserId($client, 0); // Create calendar API request with no components enabled $payload = [ @@ -471,7 +502,7 @@ public function testCreateUserCalendarNoComponents(): void 'notes_support' => false, ]; - $client->request('POST', '/api/v1/calendars/'.$username.'/create', [], [], [ + $client->request('POST', '/api/v1/calendars/'.$userId.'/create', [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], 'CONTENT_TYPE' => 'application/json', @@ -491,8 +522,8 @@ public function testCreateUserCalendarNoComponents(): void public function testEditUserCalendarNoComponents(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); - $calendarId = $this->getCalendarId($client, $username, true); + $userId = $this->getUserId($client, 0); + $calendarId = $this->getCalendarId($client, $userId, true); // Edit calendar API request with no components enabled $payload = [ @@ -503,7 +534,7 @@ public function testEditUserCalendarNoComponents(): void 'notes_support' => false, ]; - $client->request('POST', '/api/v1/calendars/'.$username.'/'.$calendarId.'/edit', [], [], [ + $client->request('PUT', '/api/v1/calendars/'.$userId.'/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], 'CONTENT_TYPE' => 'application/json', @@ -523,11 +554,11 @@ public function testEditUserCalendarNoComponents(): void public function testDeleteUserCalendar(): void { $client = static::createClient(); - $username = $this->getUserUsername($client, 0); - $calendarId = $this->getCalendarId($client, $username, true); + $userId = $this->getUserId($client, 0); + $calendarId = $this->getCalendarId($client, $userId, true); // Delete the calendar - $client->request('POST', '/api/v1/calendars/'.$username.'/'.$calendarId.'/delete', [], [], [ + $client->request('DELETE', '/api/v1/calendars/'.$userId.'/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], 'CONTENT_TYPE' => 'application/json', @@ -540,7 +571,7 @@ public function testDeleteUserCalendar(): void $this->assertEquals('success', $data['status']); // Check if calendar is deleted - $client->request('GET', '/api/v1/calendars/'.$username.'/'.$calendarId, [], [], [ + $client->request('GET', '/api/v1/calendars/'.$userId.'/'.$calendarId, [], [], [ 'HTTP_ACCEPT' => 'application/json', 'HTTP_X_DAVIS_API_TOKEN' => $_ENV['API_KEY'], ]); diff --git a/tests/Functional/CalendarControllerTest.php b/tests/Functional/CalendarControllerTest.php index 8b9f181c..a7a001dc 100644 --- a/tests/Functional/CalendarControllerTest.php +++ b/tests/Functional/CalendarControllerTest.php @@ -2,19 +2,31 @@ namespace App\Tests\Functional; +use App\Entity\User; use App\Repository\CalendarInstanceRepository; use App\Security\AdminUser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class CalendarControllerTest extends WebTestCase { + private function getUserId($client, string $username): int + { + $userRepository = static::getContainer()->get('doctrine.orm.entity_manager')->getRepository(User::class); + $user = $userRepository->findOneByUsername($username); + + return $user->getId(); + } + public function testCalendarIndex(): void { $user = new AdminUser('admin', 'test'); $client = static::createClient(); $client->loginUser($user); - $client->request('GET', '/calendars/test_user'); + + $userId = $this->getUserId($client, 'test_user'); + + $client->request('GET', '/calendars/'.$userId); $this->assertResponseIsSuccessful(); @@ -31,10 +43,12 @@ public function testCalendarEdit(): void $client = static::createClient(); $client->loginUser($user); + $userId = $this->getUserId($client, 'test_user'); + $calendarRepository = static::getContainer()->get(CalendarInstanceRepository::class); $calendar = $calendarRepository->findOneByDisplayName('default.calendar.title'); - $client->request('GET', '/calendars/test_user/edit/'.$calendar->getId()); + $client->request('GET', '/calendars/'.$userId.'/edit/'.$calendar->getId()); $this->assertResponseIsSuccessful(); @@ -43,7 +57,7 @@ public function testCalendarEdit(): void $client->submitForm('calendar_instance_save'); - $this->assertResponseRedirects('/calendars/test_user'); + $this->assertResponseRedirects('/calendars/'.$userId); $client->followRedirect(); $this->assertSelectorTextContains('h5', 'default.calendar.title'); @@ -55,7 +69,10 @@ public function testCalendarNew(): void $client = static::createClient(); $client->loginUser($user); - $crawler = $client->request('GET', '/calendars/test_user/new'); + + $userId = $this->getUserId($client, 'test_user'); + + $crawler = $client->request('GET', '/calendars/'.$userId.'/new'); $this->assertResponseIsSuccessful(); @@ -72,7 +89,7 @@ public function testCalendarNew(): void 'calendar_instance[calendarColor]' => '#00112233', ]); - $this->assertResponseRedirects('/calendars/test_user'); + $this->assertResponseRedirects('/calendars/'.$userId); $client->followRedirect(); $this->assertSelectorTextContains('h5', 'default.calendar.title'); @@ -86,12 +103,14 @@ public function testCalendarDelete(): void $client = static::createClient(); $client->loginUser($user); + $userId = $this->getUserId($client, 'test_user'); + $calendarRepository = static::getContainer()->get(CalendarInstanceRepository::class); $calendar = $calendarRepository->findOneByDisplayName('default.calendar.title'); - $client->request('GET', '/calendars/test_user/delete/'.$calendar->getId()); + $client->request('GET', '/calendars/'.$userId.'/delete/'.$calendar->getId()); - $this->assertResponseRedirects('/calendars/test_user'); + $this->assertResponseRedirects('/calendars/'.$userId); $client->followRedirect(); $this->assertSelectorTextNotContains('h5', 'default.calendar.title'); diff --git a/tests/Functional/UserControllerTest.php b/tests/Functional/UserControllerTest.php index a52acd24..b6643fda 100644 --- a/tests/Functional/UserControllerTest.php +++ b/tests/Functional/UserControllerTest.php @@ -2,11 +2,20 @@ namespace App\Tests\Functional; +use App\Entity\User; use App\Security\AdminUser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class UserControllerTest extends WebTestCase { + private function getUserId($client, string $username): int + { + $userRepository = static::getContainer()->get('doctrine.orm.entity_manager')->getRepository(User::class); + $user = $userRepository->findOneByUsername($username); + + return $user->getId(); + } + public function testUserIndex(): void { $user = new AdminUser('admin', 'test'); @@ -29,7 +38,10 @@ public function testUserEdit(): void $client = static::createClient(); $client->loginUser($user); - $client->request('GET', '/users/edit/test_user'); + + $userId = $this->getUserId($client, 'test_user'); + + $client->request('GET', '/users/edit/'.$userId); $this->assertResponseIsSuccessful(); @@ -82,14 +94,18 @@ public function testUserDelete(): void $client = static::createClient(); $client->loginUser($user); - $client->request('GET', '/users/delete/test_user'); + + $userId1 = $this->getUserId($client, 'test_user'); + $userId2 = $this->getUserId($client, 'test_user2'); + + $client->request('GET', '/users/delete/'.$userId1); $this->assertResponseRedirects('/users/'); $client->followRedirect(); $this->assertAnySelectorTextContains('h5', 'Test User 2'); - $client->request('GET', '/users/delete/test_user2'); + $client->request('GET', '/users/delete/'.$userId2); $this->assertResponseRedirects('/users/'); $client->followRedirect(); @@ -103,7 +119,10 @@ public function testUserDelegates(): void $client = static::createClient(); $client->loginUser($user); - $client->request('GET', '/users/delegates/test_user'); + + $userId = $this->getUserId($client, 'test_user'); + + $client->request('GET', '/users/delegates/'.$userId); $this->assertResponseIsSuccessful(); @@ -115,7 +134,7 @@ public function testUserDelegates(): void $client->clickLink('Disable it'); - $this->assertResponseRedirects('/users/delegates/test_user'); + $this->assertResponseRedirects('/users/delegates/'.$userId); $client->followRedirect(); $this->assertSelectorExists('nav.navbar');