From b84a9590ead6f4cd7ee105f577e3e3086d72c866 Mon Sep 17 00:00:00 2001 From: tchapi Date: Tue, 15 Dec 2020 00:21:02 +0100 Subject: [PATCH 1/4] [WIP] Refactor delegation Delegation must be directly managed at the user level, not the calendar level Add individual calendar sharing and revocation --- public/js/app.js | 5 +- src/Controller/AdminController.php | 274 +++++++++--------- src/Form/CalendarInstanceType.php | 4 + .../_partials/add_delegate_modal.html.twig | 2 +- templates/_partials/delegate_row.html.twig | 4 +- templates/_partials/delete_modal.html.twig | 2 +- templates/calendars/index.html.twig | 39 ++- .../{calendars => users}/delegates.html.twig | 8 +- templates/users/index.html.twig | 2 + translations/messages+intl-icu.en.xlf | 20 ++ 10 files changed, 215 insertions(+), 145 deletions(-) rename templates/{calendars => users}/delegates.html.twig (56%) diff --git a/public/js/app.js b/public/js/app.js index 4e0884f..2f23915 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -8,12 +8,13 @@ $(document).ready(function() { $('a.delete-modal').click(function() { // Grab real target url for deletion let targetUrl = $(this).attr('data-href'); + let modalFlavour = $(this).attr('data-flavour'); // Put it into the modal's OK button - $('#delete .target-url').attr('href', targetUrl); + $('#delete-' + modalFlavour + ' .target-url').attr('href', targetUrl); // Show the modal - $('#delete').modal('show'); + $('#delete-' + modalFlavour).modal('show'); }) // Color swatch : update it live (not working in IE ¯\_(ツ)_/¯ but it's just a nice to have) diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index eb44db2..d1ffd38 100644 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -204,16 +204,146 @@ public function userDelete(string $username, TranslatorInterface $trans) return $this->redirectToRoute('users'); } + /** + * @Route("/users/delegates/{username}", name="delegates") + */ + public function userDelegates(string $username) + { + $principal = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); + + $allPrincipalsExcept = $this->get('doctrine')->getRepository(Principal::class)->findAllExceptPrincipal(Principal::PREFIX.$username); + + // Get delegates. They are not linked to the principal in itself, but to its proxies + $principalProxyRead = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::READ_PROXY_SUFFIX); + $principalProxyWrite = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::WRITE_PROXY_SUFFIX); + + return $this->render('users/delegates.html.twig', [ + 'principal' => $principal, + 'delegation' => $principalProxyRead && $principalProxyWrite, + 'principalProxyRead' => $principalProxyRead, + 'principalProxyWrite' => $principalProxyWrite, + 'allPrincipals' => $allPrincipalsExcept, + ]); + } + + /** + * @Route("/users/delegation/{username}/{toggle}", name="user_delegation_toggle", requirements={"toggle":"(on|off)"}) + */ + public function userToggleDelegation(string $username, string $toggle) + { + $principal = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); + + if (!$principal) { + throw $this->createNotFoundException('Principal not found'); + } + + $entityManager = $this->get('doctrine')->getManager(); + + if ('on' === $toggle) { + $principalProxyRead = new Principal(); + $principalProxyRead->setUri($principal->getUri().Principal::READ_PROXY_SUFFIX) + ->setIsMain(false); + $entityManager->persist($principalProxyRead); + + $principalProxyWrite = new Principal(); + $principalProxyWrite->setUri($principal->getUri().Principal::WRITE_PROXY_SUFFIX) + ->setIsMain(false); + $entityManager->persist($principalProxyWrite); + } else { + $principalProxyRead = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::READ_PROXY_SUFFIX); + $principalProxyRead && $entityManager->remove($principalProxyRead); + + $principalProxyWrite = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::WRITE_PROXY_SUFFIX); + $principalProxyWrite && $entityManager->remove($principalProxyWrite); + + // Remove also delegates + $principal->removeAllDelegees(); + } + + $entityManager->flush(); + + return $this->redirectToRoute('delegates', ['username' => $username]); + } + + /** + * @Route("/users/delegates/{username}/add", name="user_delegate_add") + */ + public function userDelegateAdd(Request $request, string $username) + { + $newMemberToAdd = $this->get('doctrine')->getRepository(Principal::class)->findOneById($request->get('principalId')); + + if (!$newMemberToAdd) { + throw $this->createNotFoundException('Member not found'); + } + + // 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 = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username.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 = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username.Principal::WRITE_PROXY_SUFFIX); + } else { + $principal = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username.Principal::READ_PROXY_SUFFIX); + } + + if (!$principal) { + throw $this->createNotFoundException('Principal linked to this calendar not found'); + } + + $principal->addDelegee($newMemberToAdd); + $entityManager = $this->get('doctrine')->getManager(); + $entityManager->flush(); + + return $this->redirectToRoute('delegates', ['username' => $username]); + } + + /** + * @Route("/users/delegates/{username}/remove/{principalProxyId}/{delegateId}", name="user_delegate_remove", requirements={"principalProxyId":"\d+", "delegateId":"\d+"}) + */ + public function userDelegateRemove(Request $request, string $username, int $principalProxyId, int $delegateId) + { + $principalProxy = $this->get('doctrine')->getRepository(Principal::class)->findOneById($principalProxyId); + if (!$principalProxy) { + throw $this->createNotFoundException('Principal linked to this calendar not found'); + } + + $memberToRemove = $this->get('doctrine')->getRepository(Principal::class)->findOneById($delegateId); + if (!$memberToRemove) { + throw $this->createNotFoundException('Member not found'); + } + + $principalProxy->removeDelegee($memberToRemove); + $entityManager = $this->get('doctrine')->getManager(); + $entityManager->flush(); + + return $this->redirectToRoute('delegates', ['username' => $username]); + } + /** * @Route("/calendars/{username}", name="calendars") */ public function calendars(string $username) { $principal = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username); - $calendars = $this->get('doctrine')->getRepository(CalendarInstance::class)->findByPrincipalUri(Principal::PREFIX.$username); - + $allCalendars = $this->get('doctrine')->getRepository(CalendarInstance::class)->findByPrincipalUri(Principal::PREFIX.$username); + + // Separate shared calendars + $calendars = []; + $shared = []; + foreach($allCalendars as $calendar) { + if ($calendar->getAccess() === CalendarInstance::ACCESS_OWNER){ + $calendars[] = $calendar; + } else { + $shared[] = $calendar; + } + } return $this->render('calendars/index.html.twig', [ 'calendars' => $calendars, + 'shared' => $shared, 'principal' => $principal, 'username' => $username, ]); @@ -242,7 +372,10 @@ public function calendarEdit(Request $request, string $username, ?int $id, Trans $calendarInstance->setCalendar($calendar); } - $form = $this->createForm(CalendarInstanceType::class, $calendarInstance, ['new' => !$id]); + $form = $this->createForm(CalendarInstanceType::class, $calendarInstance, [ + 'new' => !$id, + 'shared' => $calendarInstance->getAccess() !== CalendarInstance::ACCESS_OWNER + ]); $components = explode(',', $calendarInstance->getCalendar()->getComponents()); @@ -323,143 +456,22 @@ public function calendarDelete(string $username, string $id, TranslatorInterface } /** - * @Route("/calendars/delegates/{id}", name="calendar_delegates", requirements={"id":"\d+"}) + * @Route("/calendars/{username}/revoke/{id}", name="calendar_revoke", requirements={"id":"\d+"}) */ - public function calendarDelegates(int $id) + public function calendarRevoke(string $username, string $id, TranslatorInterface $trans) { - $calendar = $this->get('doctrine')->getRepository(CalendarInstance::class)->findOneById($id); - - if (!$calendar) { - throw $this->createNotFoundException('Calendar not found'); - } - - $principal = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($calendar->getPrincipalUri()); - - $allPrincipalsExcept = $this->get('doctrine')->getRepository(Principal::class)->findAllExceptPrincipal($calendar->getPrincipalUri()); - - // Get delegates. They are not linked to the principal in itself, but to its proxies - $principalProxyRead = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::READ_PROXY_SUFFIX); - $principalProxyWrite = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::WRITE_PROXY_SUFFIX); - - return $this->render('calendars/delegates.html.twig', [ - 'calendar' => $calendar, - 'principal' => $principal, - 'delegation' => $principalProxyRead && $principalProxyWrite, - 'principalProxyRead' => $principalProxyRead, - 'principalProxyWrite' => $principalProxyWrite, - 'allPrincipals' => $allPrincipalsExcept, - ]); - } - - /** - * @Route("/calendars/delegates/{id}/add", name="calendar_delegate_add", requirements={"id":"\d+"}) - */ - public function calendarDelegateAdd(Request $request, int $id) - { - $calendar = $this->get('doctrine')->getRepository(CalendarInstance::class)->findOneById($id); - - if (!$calendar) { - throw $this->createNotFoundException('Calendar not found'); - } - - $newMemberToAdd = $this->get('doctrine')->getRepository(Principal::class)->findOneById($request->get('principalId')); - - if (!$newMemberToAdd) { - throw $this->createNotFoundException('Member not found'); - } - - // 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 = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($calendar->getPrincipalUri().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 = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($calendar->getPrincipalUri().Principal::WRITE_PROXY_SUFFIX); - } else { - $principal = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($calendar->getPrincipalUri().Principal::READ_PROXY_SUFFIX); - } - - if (!$principal) { - throw $this->createNotFoundException('Principal linked to this calendar not found'); - } - - $principal->addDelegee($newMemberToAdd); - $entityManager = $this->get('doctrine')->getManager(); - $entityManager->flush(); - - return $this->redirectToRoute('calendar_delegates', ['id' => $id]); - } - - /** - * @Route("/calendars/delegates/{id}/remove/{principalProxyId}/{delegateId}", name="calendar_delegate_remove", requirements={"id":"\d+", "principalProxyId":"\d+", "delegateId":"\d+"}) - */ - public function calendarDelegateRemove(Request $request, int $id, int $principalProxyId, int $delegateId) - { - $principalProxy = $this->get('doctrine')->getRepository(Principal::class)->findOneById($principalProxyId); - - if (!$principalProxy) { - throw $this->createNotFoundException('Principal linked to this calendar not found'); - } - - $memberToRemove = $this->get('doctrine')->getRepository(Principal::class)->findOneById($delegateId); - - if (!$memberToRemove) { - throw $this->createNotFoundException('Member not found'); - } - - $principalProxy->removeDelegee($memberToRemove); - $entityManager = $this->get('doctrine')->getManager(); - $entityManager->flush(); - - return $this->redirectToRoute('calendar_delegates', ['id' => $id]); - } - - /** - * @Route("/calendars/delegation/{id}/{toggle}", name="calendar_delegation_toggle", requirements={"id":"\d+", "toggle":"(on|off)"}) - */ - public function calendarToggleDelegation(int $id, string $toggle) - { - $calendar = $this->get('doctrine')->getRepository(CalendarInstance::class)->findOneById($id); - - if (!$calendar) { + $instance = $this->get('doctrine')->getRepository(CalendarInstance::class)->findOneById($id); + if (!$instance) { throw $this->createNotFoundException('Calendar not found'); } - $principal = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($calendar->getPrincipalUri()); - - if (!$principal) { - throw $this->createNotFoundException('Principal linked to this calendar not found'); - } - $entityManager = $this->get('doctrine')->getManager(); - - if ('on' === $toggle) { - $principalProxyRead = new Principal(); - $principalProxyRead->setUri($principal->getUri().Principal::READ_PROXY_SUFFIX) - ->setIsMain(false); - $entityManager->persist($principalProxyRead); - - $principalProxyWrite = new Principal(); - $principalProxyWrite->setUri($principal->getUri().Principal::WRITE_PROXY_SUFFIX) - ->setIsMain(false); - $entityManager->persist($principalProxyWrite); - } else { - $principalProxyRead = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::READ_PROXY_SUFFIX); - $principalProxyRead && $entityManager->remove($principalProxyRead); - - $principalProxyWrite = $this->get('doctrine')->getRepository(Principal::class)->findOneByUri($principal->getUri().Principal::WRITE_PROXY_SUFFIX); - $principalProxyWrite && $entityManager->remove($principalProxyWrite); - - // Remove also delegates - $principal->removeAllDelegees(); - } + $entityManager->remove($instance); $entityManager->flush(); + $this->addFlash('success', $trans->trans('calendar.revoked')); - return $this->redirectToRoute('calendar_delegates', ['id' => $id]); + return $this->redirectToRoute('calendars', ['username' => $username]); } /** diff --git a/src/Form/CalendarInstanceType.php b/src/Form/CalendarInstanceType.php index 321d44c..8ef161c 100644 --- a/src/Form/CalendarInstanceType.php +++ b/src/Form/CalendarInstanceType.php @@ -43,18 +43,21 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('events', CheckboxType::class, [ 'label' => 'form.events', 'mapped' => false, + 'disabled' => $options['shared'], 'help' => 'form.events.help', 'required' => false, ]) ->add('todos', CheckboxType::class, [ 'label' => 'form.todos', 'mapped' => false, + 'disabled' => $options['shared'], 'help' => 'form.todos.help', 'required' => false, ]) ->add('notes', CheckboxType::class, [ 'label' => 'form.notes', 'mapped' => false, + 'disabled' => $options['shared'], 'help' => 'form.notes.help', 'required' => false, ]) @@ -67,6 +70,7 @@ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'new' => false, + 'shared' => false, 'data_class' => CalendarInstance::class, ]); } diff --git a/templates/_partials/add_delegate_modal.html.twig b/templates/_partials/add_delegate_modal.html.twig index 3a04c86..3a4238e 100644 --- a/templates/_partials/add_delegate_modal.html.twig +++ b/templates/_partials/add_delegate_modal.html.twig @@ -27,7 +27,7 @@ diff --git a/templates/_partials/delegate_row.html.twig b/templates/_partials/delegate_row.html.twig index bfddc43..cc6bf97 100644 --- a/templates/_partials/delegate_row.html.twig +++ b/templates/_partials/delegate_row.html.twig @@ -10,12 +10,12 @@ {% endif %}
- ⚠ {{ "remove"|trans }} + ⚠ {{ "remove"|trans }}

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

{{ "users.uri"|trans }} : {{ delegate.uri }}
- ⚠ {{ "remove"|trans }} + ⚠ {{ "remove"|trans }}
\ No newline at end of file diff --git a/templates/_partials/delete_modal.html.twig b/templates/_partials/delete_modal.html.twig index b6970ea..e9d47d0 100644 --- a/templates/_partials/delete_modal.html.twig +++ b/templates/_partials/delete_modal.html.twig @@ -1,4 +1,4 @@ -