From 70f7f3500961b2e33d8b9a59c51347d32a01def7 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sat, 6 Mar 2021 12:59:13 +0100 Subject: [PATCH] allow to select reports as initial view (#2403) --- src/Form/Type/InitialViewType.php | 50 +++++++++++-------- .../ReportingService.php} | 18 +------ src/Twig/Runtime/ReportingExtension.php | 34 +++++++++++++ src/Twig/RuntimeExtensions.php | 2 + .../ReportingServiceTest.php} | 34 +++++-------- tests/Twig/Runtime/ReportingExtensionTest.php | 43 ++++++++++++++++ tests/Twig/RuntimeExtensionsTest.php | 12 ++++- 7 files changed, 134 insertions(+), 59 deletions(-) rename src/{Twig/ReportingExtension.php => Reporting/ReportingService.php} (79%) create mode 100644 src/Twig/Runtime/ReportingExtension.php rename tests/{Twig/ReportingExtensionTest.php => Reporting/ReportingServiceTest.php} (58%) create mode 100644 tests/Twig/Runtime/ReportingExtensionTest.php diff --git a/src/Form/Type/InitialViewType.php b/src/Form/Type/InitialViewType.php index cfc3cc6e..fd49127a 100644 --- a/src/Form/Type/InitialViewType.php +++ b/src/Form/Type/InitialViewType.php @@ -9,10 +9,14 @@ namespace App\Form\Type; +use App\Entity\User; +use App\Reporting\ReportingService; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * Custom form field type to select the initial view, where the user should be redirected to after login. @@ -21,7 +25,7 @@ class InitialViewType extends AbstractType { public const DEFAULT_VIEW = 'timesheet'; - public const ALLOWED_VIEWS = [ + private const ALLOWED_VIEWS = [ 'dashboard' => 'menu.homepage', 'timesheet' => 'menu.timesheet', 'calendar' => 'calendar.title', @@ -34,7 +38,7 @@ class InitialViewType extends AbstractType 'admin_activity' => 'menu.admin_activity', ]; - protected const ROUTE_PERMISSION = [ + private const ROUTE_PERMISSION = [ 'dashboard' => '', 'timesheet' => 'view_own_timesheet', 'calendar' => 'view_own_timesheet', @@ -47,17 +51,15 @@ class InitialViewType extends AbstractType 'admin_activity' => 'view_activity', ]; - /** - * @var AuthorizationCheckerInterface - */ - protected $voter; + private $voter; + private $reportingService; + private $translator; - /** - * @param AuthorizationCheckerInterface $voter - */ - public function __construct(AuthorizationCheckerInterface $voter) + public function __construct(AuthorizationCheckerInterface $voter, ReportingService $reportingService, TranslatorInterface $translator) { $this->voter = $voter; + $this->reportingService = $reportingService; + $this->translator = $translator; } /** @@ -65,18 +67,26 @@ class InitialViewType extends AbstractType */ public function configureOptions(OptionsResolver $resolver) { - $choices = []; - foreach (self::ROUTE_PERMISSION as $route => $permission) { - if (empty($permission) || $this->voter->isGranted($permission)) { - $name = self::ALLOWED_VIEWS[$route]; - $choices[$name] = $route; + $resolver->setDefault('required', true); + $resolver->setDefault('choices', function (Options $options) { + $choices = []; + foreach (self::ROUTE_PERMISSION as $route => $permission) { + if (empty($permission) || $this->voter->isGranted($permission)) { + $name = self::ALLOWED_VIEWS[$route]; + $choices[$name] = $route; + } } - } - $resolver->setDefaults([ - 'required' => true, - 'choices' => $choices, - ]); + /** @var User $user */ + $user = $options['user']; + + foreach ($this->reportingService->getAvailableReports($user) as $report) { + $label = $this->translator->trans('menu.reporting') . ': ' . $this->translator->trans($report->getLabel(), [], 'reporting'); + $choices[$label] = $report->getRoute(); + } + + return $choices; + }); } /** diff --git a/src/Twig/ReportingExtension.php b/src/Reporting/ReportingService.php similarity index 79% rename from src/Twig/ReportingExtension.php rename to src/Reporting/ReportingService.php index 08a3e27c..34f9607e 100644 --- a/src/Twig/ReportingExtension.php +++ b/src/Reporting/ReportingService.php @@ -7,18 +7,14 @@ * file that was distributed with this source code. */ -namespace App\Twig; +namespace App\Reporting; use App\Entity\User; use App\Event\ReportingEvent; -use App\Reporting\Report; -use App\Reporting\ReportInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; -final class ReportingExtension extends AbstractExtension +final class ReportingService { /** * @var EventDispatcherInterface @@ -35,16 +31,6 @@ final class ReportingExtension extends AbstractExtension $this->security = $security; } - /** - * {@inheritdoc} - */ - public function getFunctions() - { - return [ - new TwigFunction('available_reports', [$this, 'getAvailableReports'], []), - ]; - } - /** * @param User $user * @return ReportInterface[] diff --git a/src/Twig/Runtime/ReportingExtension.php b/src/Twig/Runtime/ReportingExtension.php new file mode 100644 index 00000000..a321a0e9 --- /dev/null +++ b/src/Twig/Runtime/ReportingExtension.php @@ -0,0 +1,34 @@ +service = $reportingService; + } + + /** + * @param User $user + * @return ReportInterface[] + */ + public function getAvailableReports(User $user): array + { + return $this->service->getAvailableReports($user); + } +} diff --git a/src/Twig/RuntimeExtensions.php b/src/Twig/RuntimeExtensions.php index f8d21d76..1d084877 100644 --- a/src/Twig/RuntimeExtensions.php +++ b/src/Twig/RuntimeExtensions.php @@ -12,6 +12,7 @@ namespace App\Twig; use App\Twig\Runtime\EncoreExtension; use App\Twig\Runtime\ExporterExtension; use App\Twig\Runtime\MarkdownExtension; +use App\Twig\Runtime\ReportingExtension; use App\Twig\Runtime\ThemeExtension; use App\Twig\Runtime\TimesheetExtension; use App\Twig\Runtime\WidgetExtension; @@ -34,6 +35,7 @@ class RuntimeExtensions extends AbstractExtension new TwigFunction('active_timesheets', [TimesheetExtension::class, 'activeEntries']), new TwigFunction('encore_entry_css_source', [EncoreExtension::class, 'getEncoreEntryCssSource']), new TwigFunction('render_widget', [WidgetExtension::class, 'renderWidget'], ['is_safe' => ['html']]), + new TwigFunction('available_reports', [ReportingExtension::class, 'getAvailableReports'], []), ]; } diff --git a/tests/Twig/ReportingExtensionTest.php b/tests/Reporting/ReportingServiceTest.php similarity index 58% rename from tests/Twig/ReportingExtensionTest.php rename to tests/Reporting/ReportingServiceTest.php index 6216d5fe..b42250db 100644 --- a/tests/Twig/ReportingExtensionTest.php +++ b/tests/Reporting/ReportingServiceTest.php @@ -7,41 +7,31 @@ * file that was distributed with this source code. */ -namespace App\Tests\Twig; +namespace App\Tests\Reporting; use App\Entity\User; -use App\Twig\ReportingExtension; +use App\Event\ReportingEvent; +use App\Reporting\ReportingService; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Twig\TwigFunction; /** - * @covers \App\Twig\ReportingExtension + * @covers \App\Reporting\ReportingService */ -class ReportingExtensionTest extends TestCase +class ReportingServiceTest extends TestCase { - protected function getSut(bool $isGranted = false): ReportingExtension + protected function getSut(bool $isGranted = false): ReportingService { $dispatcher = $this->createMock(EventDispatcherInterface::class); + $dispatcher->expects($this->once())->method('dispatch')->willReturnCallback(function ($event) { + $this->assertInstanceOf(ReportingEvent::class, $event); + }); + $security = $this->createMock(AuthorizationCheckerInterface::class); - $security->method('isGranted')->willReturn($isGranted); + $security->expects($this->any())->method('isGranted')->willReturn($isGranted); - return new ReportingExtension($dispatcher, $security); - } - - public function testGetFunctions() - { - $sut = $this->getSut(); - $functions = ['available_reports']; - $twigFunctions = $sut->getFunctions(); - $this->assertCount(\count($functions), $twigFunctions); - $i = 0; - /** @var TwigFunction $function */ - foreach ($twigFunctions as $function) { - $this->assertInstanceOf(TwigFunction::class, $function); - $this->assertEquals($functions[$i++], $function->getName()); - } + return new ReportingService($dispatcher, $security); } public function testGetAvailableReports() diff --git a/tests/Twig/Runtime/ReportingExtensionTest.php b/tests/Twig/Runtime/ReportingExtensionTest.php new file mode 100644 index 00000000..2ceda40e --- /dev/null +++ b/tests/Twig/Runtime/ReportingExtensionTest.php @@ -0,0 +1,43 @@ +createMock(AuthorizationCheckerInterface::class); + $authorization->expects($this->any())->method('isGranted')->willReturn($isGranted); + + $service = new ReportingService($eventDispatcher, $authorization); + + return new ReportingExtension($service); + } + + public function testRenderWidgetForInvalidValue() + { + $sut = $this->getSut(true); + $reports = $sut->getAvailableReports(new User()); + + $this->assertCount(3, $reports); + } +} diff --git a/tests/Twig/RuntimeExtensionsTest.php b/tests/Twig/RuntimeExtensionsTest.php index 21e2cd5a..f304cb47 100644 --- a/tests/Twig/RuntimeExtensionsTest.php +++ b/tests/Twig/RuntimeExtensionsTest.php @@ -37,7 +37,17 @@ class RuntimeExtensionsTest extends TestCase public function testGetFunctions() { - $expected = ['trigger', 'actions', 'javascript_translations', 'timesheet_exporter', 'active_timesheets', 'encore_entry_css_source', 'render_widget']; + $expected = [ + 'trigger', + 'actions', + 'javascript_translations', + 'timesheet_exporter', + 'active_timesheets', + 'encore_entry_css_source', + 'render_widget', + 'available_reports' + ]; + $i = 0; $sut = new RuntimeExtensions();