allow to select reports as initial view (#2403)

This commit is contained in:
Kevin Papst
2021-03-06 12:59:13 +01:00
committed by GitHub
parent 5583caa9e4
commit 70f7f35009
7 changed files with 134 additions and 59 deletions

View File

@@ -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;
});
}
/**

View File

@@ -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[]

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Twig\Runtime;
use App\Entity\User;
use App\Reporting\ReportingService;
use App\Reporting\ReportInterface;
use Twig\Extension\RuntimeExtensionInterface;
final class ReportingExtension implements RuntimeExtensionInterface
{
private $service;
public function __construct(ReportingService $reportingService)
{
$this->service = $reportingService;
}
/**
* @param User $user
* @return ReportInterface[]
*/
public function getAvailableReports(User $user): array
{
return $this->service->getAvailableReports($user);
}
}

View File

@@ -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'], []),
];
}

View File

@@ -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()

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Twig\Runtime;
use App\Entity\User;
use App\Reporting\ReportingService;
use App\Twig\Runtime\ReportingExtension;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
/**
* @covers \App\Twig\Runtime\ReportingExtension
*/
class ReportingExtensionTest extends TestCase
{
protected function getSut(bool $isGranted): ReportingExtension
{
$eventDispatcher = new EventDispatcher();
$authorization = $this->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);
}
}

View File

@@ -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();