performance improvements (#2329)
* remove work from constructor * refactor twig extensions * upgrade theme * replace sub-request with direct template rendering
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
<?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\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Event\RecentActivityEvent;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Used for the (initial) page rendering.
|
||||
*/
|
||||
class LayoutController extends AbstractController
|
||||
{
|
||||
public function activeEntries(TimesheetRepository $repository, SystemConfiguration $configuration, EventDispatcherInterface $dispatcher): Response
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$activeEntries = $repository->getActiveEntries($user);
|
||||
|
||||
$recentActivity = new RecentActivityEvent($this->getUser(), $activeEntries);
|
||||
$dispatcher->dispatch($recentActivity);
|
||||
|
||||
return $this->render(
|
||||
'navbar/active-entries.html.twig',
|
||||
[
|
||||
'entries' => $recentActivity->getRecentActivities(),
|
||||
'soft_limit' => $configuration->getTimesheetActiveEntriesSoftLimit(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -20,22 +20,17 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt
|
||||
/**
|
||||
* Allows dynamic injection of theme related options.
|
||||
*/
|
||||
class ThemeOptionsSubscriber implements EventSubscriberInterface
|
||||
final class ThemeOptionsSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
private $storage;
|
||||
/**
|
||||
* @var ContextHelper
|
||||
*/
|
||||
protected $helper;
|
||||
private $helper;
|
||||
|
||||
/**
|
||||
* @param TokenStorageInterface $storage
|
||||
* @param ContextHelper $helper
|
||||
*/
|
||||
public function __construct(TokenStorageInterface $storage, ContextHelper $helper)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
@@ -52,18 +47,24 @@ class ThemeOptionsSubscriber implements EventSubscriberInterface
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param KernelEvent $event
|
||||
*/
|
||||
public function setThemeOptions(KernelEvent $event)
|
||||
public function setThemeOptions(KernelEvent $event): void
|
||||
{
|
||||
if (!$this->canHandleEvent($event)) {
|
||||
// Ignore sub-requests
|
||||
if (!$event->isMasterRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore events like the toolbar where we do not have a token
|
||||
if (null === $this->storage->getToken()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = $this->storage->getToken()->getUser();
|
||||
|
||||
if (!($user instanceof User)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var UserPreference $ref */
|
||||
foreach ($user->getPreferences() as $ref) {
|
||||
$name = $ref->getName();
|
||||
@@ -90,26 +91,4 @@ class ThemeOptionsSubscriber implements EventSubscriberInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param KernelEvent $event
|
||||
* @return bool
|
||||
*/
|
||||
protected function canHandleEvent(KernelEvent $event): bool
|
||||
{
|
||||
// Ignore sub-requests
|
||||
if (!$event->isMasterRequest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore events like the toolbar where we do not have a token
|
||||
if (null === $this->storage->getToken()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = $this->storage->getToken()->getUser();
|
||||
|
||||
return ($user instanceof User);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
|
||||
class UserEnvironmentSubscriber implements EventSubscriberInterface
|
||||
final class UserEnvironmentSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
@@ -40,20 +40,22 @@ class UserEnvironmentSubscriber implements EventSubscriberInterface
|
||||
];
|
||||
}
|
||||
|
||||
public function prepareEnvironment(RequestEvent $event)
|
||||
public function prepareEnvironment(RequestEvent $event): void
|
||||
{
|
||||
// ignore sub-requests
|
||||
if (!$event->isMasterRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $this->storage->getToken()) {
|
||||
// the locale depends on the request, not on the user configuration
|
||||
\Locale::setDefault($event->getRequest()->getLocale());
|
||||
|
||||
// ignore events like the toolbar where we do not have a token
|
||||
if (null === ($token = $this->storage->getToken())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->storage->getToken()->getUser();
|
||||
|
||||
// the locale depends on the request, not on the user configuration
|
||||
\Locale::setDefault($event->getRequest()->getLocale());
|
||||
$user = $token->getUser();
|
||||
|
||||
if ($user instanceof User) {
|
||||
date_default_timezone_set($user->getTimezone());
|
||||
|
||||
@@ -28,20 +28,20 @@ use Symfony\Component\Form\Extension\Core\Type\TimezoneType;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Validator\Constraints\Range;
|
||||
|
||||
class UserPreferenceSubscriber implements EventSubscriberInterface
|
||||
final class UserPreferenceSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
private $eventDispatcher;
|
||||
/**
|
||||
* @var AuthorizationCheckerInterface
|
||||
*/
|
||||
protected $voter;
|
||||
private $voter;
|
||||
/**
|
||||
* @var SystemConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
private $configuration;
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher, AuthorizationCheckerInterface $voter, SystemConfiguration $formConfig)
|
||||
{
|
||||
@@ -57,43 +57,23 @@ class UserPreferenceSubscriber implements EventSubscriberInterface
|
||||
];
|
||||
}
|
||||
|
||||
private function getDefaultTheme(): ?string
|
||||
{
|
||||
return $this->configuration->getUserDefaultTheme();
|
||||
}
|
||||
|
||||
private function getDefaultCurrency(): string
|
||||
{
|
||||
return $this->configuration->getUserDefaultCurrency();
|
||||
}
|
||||
|
||||
private function getDefaultLanguage(): string
|
||||
{
|
||||
return $this->configuration->getUserDefaultLanguage();
|
||||
}
|
||||
|
||||
private function getDefaultTimezone(): string
|
||||
{
|
||||
$timezone = $this->configuration->getUserDefaultTimezone();
|
||||
if (null === $timezone) {
|
||||
$timezone = date_default_timezone_get();
|
||||
}
|
||||
|
||||
return $timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return UserPreference[]
|
||||
*/
|
||||
public function getDefaultPreferences(User $user)
|
||||
{
|
||||
$timezone = $this->configuration->getUserDefaultTimezone();
|
||||
if (null === $timezone) {
|
||||
$timezone = date_default_timezone_get();
|
||||
}
|
||||
|
||||
$enableHourlyRate = false;
|
||||
$hourlyRateOptions = [];
|
||||
|
||||
if ($this->voter->isGranted('hourly-rate', $user)) {
|
||||
$enableHourlyRate = true;
|
||||
$hourlyRateOptions = ['currency' => $this->getDefaultCurrency()];
|
||||
$hourlyRateOptions = ['currency' => $this->configuration->getUserDefaultCurrency()];
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -119,14 +99,14 @@ class UserPreferenceSubscriber implements EventSubscriberInterface
|
||||
|
||||
(new UserPreference())
|
||||
->setName(UserPreference::TIMEZONE)
|
||||
->setValue($this->getDefaultTimezone())
|
||||
->setValue($timezone)
|
||||
->setOrder(200)
|
||||
->setSection('locale')
|
||||
->setType(TimezoneType::class),
|
||||
|
||||
(new UserPreference())
|
||||
->setName(UserPreference::LOCALE)
|
||||
->setValue($this->getDefaultLanguage())
|
||||
->setValue($this->configuration->getUserDefaultLanguage())
|
||||
->setOrder(250)
|
||||
->setSection('locale')
|
||||
->setType(LanguageType::class),
|
||||
@@ -140,7 +120,7 @@ class UserPreferenceSubscriber implements EventSubscriberInterface
|
||||
|
||||
(new UserPreference())
|
||||
->setName(UserPreference::SKIN)
|
||||
->setValue($this->getDefaultTheme())
|
||||
->setValue($this->configuration->getUserDefaultTheme())
|
||||
->setOrder(400)
|
||||
->setSection('theme')
|
||||
->setType(SkinType::class),
|
||||
|
||||
@@ -43,32 +43,21 @@ final class UserProfileSubscriber implements EventSubscriberInterface
|
||||
|
||||
public function prepareUserProfile(KernelEvent $event): void
|
||||
{
|
||||
if (!$this->canHandleEvent($event)) {
|
||||
// ignore sub-requests
|
||||
if (!$event->isMasterRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = $this->storage->getToken()->getUser();
|
||||
|
||||
$event = new PrepareUserEvent($user);
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
}
|
||||
|
||||
private function canHandleEvent(KernelEvent $event): bool
|
||||
{
|
||||
// Ignore sub-requests
|
||||
if (!$event->isMasterRequest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore events like the toolbar where we do not have a token
|
||||
if (null === $this->storage->getToken()) {
|
||||
return false;
|
||||
if (null === ($token = $this->storage->getToken())) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = $this->storage->getToken()->getUser();
|
||||
$user = $token->getUser();
|
||||
|
||||
return ($user instanceof User);
|
||||
if ($user instanceof User) {
|
||||
$event = new PrepareUserEvent($user);
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use App\Event\UserPreferenceDisplayEvent;
|
||||
use App\Export\ExportItemInterface;
|
||||
use App\Repository\Query\CustomerQuery;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Twig\DateExtensions;
|
||||
use App\Twig\LocaleFormatExtensions;
|
||||
use DateTime;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
@@ -47,7 +47,7 @@ abstract class AbstractSpreadsheetRenderer
|
||||
public const RATE_FORMAT = self::RATE_FORMAT_LEFT;
|
||||
|
||||
/**
|
||||
* @var DateExtensions
|
||||
* @var LocaleFormatExtensions
|
||||
*/
|
||||
protected $dateExtension;
|
||||
/**
|
||||
@@ -92,7 +92,7 @@ abstract class AbstractSpreadsheetRenderer
|
||||
'user-meta' => [],
|
||||
];
|
||||
|
||||
public function __construct(TranslatorInterface $translator, DateExtensions $dateExtension, EventDispatcherInterface $dispatcher, AuthorizationCheckerInterface $voter)
|
||||
public function __construct(TranslatorInterface $translator, LocaleFormatExtensions $dateExtension, EventDispatcherInterface $dispatcher, AuthorizationCheckerInterface $voter)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
$this->dateExtension = $dateExtension;
|
||||
|
||||
@@ -12,8 +12,7 @@ namespace App\Invoice\Renderer;
|
||||
use App\Entity\InvoiceDocument;
|
||||
use App\Invoice\InvoiceModel;
|
||||
use App\Invoice\RendererInterface;
|
||||
use App\Twig\DateExtensions;
|
||||
use App\Twig\LocaleExtensions;
|
||||
use App\Twig\LocaleFormatExtensions;
|
||||
use Symfony\Bridge\Twig\Extension\TranslationExtension;
|
||||
use Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||
use Twig\Environment;
|
||||
@@ -61,12 +60,8 @@ abstract class AbstractTwigRenderer implements RendererInterface
|
||||
|
||||
$translator->setLocale($locale);
|
||||
|
||||
/** @var LocaleExtensions $extension */
|
||||
$extension = $twig->getExtension(LocaleExtensions::class);
|
||||
$extension->setLocale($locale);
|
||||
|
||||
/** @var DateExtensions $extension */
|
||||
$extension = $twig->getExtension(DateExtensions::class);
|
||||
/** @var LocaleFormatExtensions $extension */
|
||||
$extension = $twig->getExtension(LocaleFormatExtensions::class);
|
||||
$extension->setLocale($locale);
|
||||
|
||||
return $previousLocale;
|
||||
|
||||
@@ -31,17 +31,30 @@ class WidgetRepository
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $definitions = [];
|
||||
private $definitions;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $customDefinition;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, array $widgets)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->definitions = array_merge($this->getDefaultWidgets(), $widgets);
|
||||
$this->customDefinition = $widgets;
|
||||
}
|
||||
|
||||
private function getDefinedWidgets(): array
|
||||
{
|
||||
if (null === $this->definitions) {
|
||||
$this->definitions = array_merge($this->getDefaultWidgets(), $this->customDefinition);
|
||||
}
|
||||
|
||||
return $this->definitions;
|
||||
}
|
||||
|
||||
public function has(string $id): bool
|
||||
{
|
||||
return isset($this->definitions[$id]) || isset($this->widgets[$id]);
|
||||
return isset($this->getDefinedWidgets()[$id]) || isset($this->widgets[$id]);
|
||||
}
|
||||
|
||||
public function registerWidget(WidgetInterface $widget): WidgetRepository
|
||||
@@ -64,7 +77,7 @@ class WidgetRepository
|
||||
}
|
||||
|
||||
// this code should ONLY be reached for internal (pre-registered) widgets
|
||||
$this->registerWidget($this->create($id, $this->definitions[$id]));
|
||||
$this->registerWidget($this->create($id, $this->getDefinedWidgets()[$id]));
|
||||
|
||||
return $this->widgets[$id];
|
||||
}
|
||||
|
||||
@@ -9,18 +9,18 @@
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Configuration\ThemeConfiguration;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class ConfigExtension extends AbstractExtension
|
||||
final class ConfigExtension extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var ThemeConfiguration
|
||||
* @var SystemConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
private $configuration;
|
||||
|
||||
public function __construct(ThemeConfiguration $configuration)
|
||||
public function __construct(SystemConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
@@ -41,6 +41,15 @@ class ConfigExtension extends AbstractExtension
|
||||
*/
|
||||
public function getThemeConfig(string $name)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'soft_limit':
|
||||
return $this->configuration->getTimesheetActiveEntriesSoftLimit();
|
||||
|
||||
default:
|
||||
$name = 'theme.' . $name;
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->configuration->find($name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use App\Configuration\LanguageFormattings;
|
||||
use App\Constants;
|
||||
use App\Entity\User;
|
||||
use App\Utils\LocaleFormats;
|
||||
use App\Utils\LocaleFormatter;
|
||||
use DateTime;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\TwigTest;
|
||||
|
||||
/**
|
||||
* Date specific twig extensions
|
||||
*/
|
||||
class DateExtensions extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var LocaleFormats|null
|
||||
*/
|
||||
protected $localeFormats = null;
|
||||
/**
|
||||
* @var LocaleFormatter
|
||||
*/
|
||||
private $formatter;
|
||||
/**
|
||||
* @var LanguageFormattings
|
||||
*/
|
||||
private $formats;
|
||||
|
||||
public function __construct(RequestStack $requestStack, LanguageFormattings $formats)
|
||||
{
|
||||
$locale = Constants::DEFAULT_LOCALE;
|
||||
|
||||
// request is null in a console command
|
||||
if (null !== $requestStack->getMasterRequest()) {
|
||||
$locale = $requestStack->getMasterRequest()->getLocale();
|
||||
}
|
||||
|
||||
$this->formats = $formats;
|
||||
$this->setLocale($locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('month_name', [$this, 'monthName']),
|
||||
new TwigFilter('day_name', [$this, 'dayName']),
|
||||
new TwigFilter('date_short', [$this, 'dateShort']),
|
||||
new TwigFilter('date_time', [$this, 'dateTime']),
|
||||
new TwigFilter('date_full', [$this, 'dateTimeFull']),
|
||||
new TwigFilter('date_format', [$this, 'dateFormat']),
|
||||
new TwigFilter('time', [$this, 'time']),
|
||||
new TwigFilter('hour24', [$this, 'hour24']),
|
||||
];
|
||||
}
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
return [
|
||||
new TwigTest('weekend', function ($dateTime) {
|
||||
if (!$dateTime instanceof \DateTime) {
|
||||
return false;
|
||||
}
|
||||
$day = (int) $dateTime->format('w');
|
||||
|
||||
return ($day === 0 || $day === 6);
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('get_format_duration', [$this, 'getDurationFormat']),
|
||||
new TwigFunction('create_date', [$this, 'createDate']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to switch the locale used for all twig filter and functions.
|
||||
*
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
$this->formatter = new LocaleFormatter($this->formats, $locale);
|
||||
$this->localeFormats = new LocaleFormats($this->formats, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @return string
|
||||
*/
|
||||
public function dateShort($date)
|
||||
{
|
||||
return $this->formatter->dateShort($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @return string
|
||||
*/
|
||||
public function dateTime($date)
|
||||
{
|
||||
return $this->formatter->dateTime($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @return bool|false|string
|
||||
*/
|
||||
public function dateTimeFull($date)
|
||||
{
|
||||
return $this->formatter->dateTimeFull($date);
|
||||
}
|
||||
|
||||
public function createDate(string $date, ?User $user = null): \DateTime
|
||||
{
|
||||
$timezone = $user !== null ? $user->getTimezone() : date_default_timezone_get();
|
||||
|
||||
return new DateTime($date, new \DateTimeZone($timezone));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @param string $format
|
||||
* @return false|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function dateFormat($date, string $format)
|
||||
{
|
||||
return $this->formatter->dateFormat($date, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @return string
|
||||
*/
|
||||
public function time($date)
|
||||
{
|
||||
return $this->formatter->time($date);
|
||||
}
|
||||
|
||||
public function monthName(\DateTime $dateTime, bool $withYear = false): string
|
||||
{
|
||||
return $this->formatter->monthName($dateTime, $withYear);
|
||||
}
|
||||
|
||||
public function dayName(\DateTime $dateTime, bool $short = false): string
|
||||
{
|
||||
return $this->formatter->dayName($dateTime, $short);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $twentyFour
|
||||
* @param mixed $twelveHour
|
||||
* @return mixed
|
||||
*/
|
||||
public function hour24($twentyFour, $twelveHour)
|
||||
{
|
||||
return $this->formatter->hour24($twentyFour, $twelveHour);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDurationFormat()
|
||||
{
|
||||
return $this->localeFormats->getDurationFormat();
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use App\Twig\Runtime\ThemeEventExtension;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class EventExtensions extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('trigger', [ThemeEventExtension::class, 'trigger']),
|
||||
new TwigFunction('javascript_translations', [ThemeEventExtension::class, 'getJavascriptTranslations']),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
<?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;
|
||||
|
||||
use App\Configuration\LanguageFormattings;
|
||||
use App\Constants;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Utils\Duration;
|
||||
use App\Utils\LocaleFormatter;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Intl\Locales;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* Locale specific Twig extensions
|
||||
*/
|
||||
final class LocaleExtensions extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var LocaleFormatter
|
||||
*/
|
||||
private $formatter;
|
||||
/**
|
||||
* @var LanguageFormattings
|
||||
*/
|
||||
private $formats;
|
||||
|
||||
public function __construct(RequestStack $requestStack, LanguageFormattings $formats)
|
||||
{
|
||||
$locale = Constants::DEFAULT_LOCALE;
|
||||
|
||||
// request is null in a console command
|
||||
if (null !== $requestStack->getMasterRequest()) {
|
||||
$locale = $requestStack->getMasterRequest()->getLocale();
|
||||
}
|
||||
|
||||
$this->formats = $formats;
|
||||
$this->setLocale($locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('duration', [$this, 'duration']),
|
||||
new TwigFilter('duration_decimal', [$this, 'durationDecimal']),
|
||||
new TwigFilter('money', [$this, 'money']),
|
||||
new TwigFilter('currency', [$this, 'currency']),
|
||||
new TwigFilter('country', [$this, 'country']),
|
||||
new TwigFilter('language', [$this, 'language']),
|
||||
new TwigFilter('amount', [$this, 'amount']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('locales', [$this, 'getLocales']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to switch the locale used for all twig filter and functions.
|
||||
*
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
$this->formatter = new LocaleFormatter($this->formats, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms seconds into a duration string.
|
||||
*
|
||||
* @param int|Timesheet|null $duration
|
||||
* @param bool $decimal
|
||||
* @return string
|
||||
*/
|
||||
public function duration($duration, $decimal = false)
|
||||
{
|
||||
return $this->formatter->duration($duration, $decimal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms seconds into a decimal formatted duration string.
|
||||
*
|
||||
* @param int|Timesheet|null $duration
|
||||
* @return string
|
||||
*/
|
||||
public function durationDecimal($duration)
|
||||
{
|
||||
return $this->formatter->durationDecimal($duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|float $amount
|
||||
* @return bool|false|string
|
||||
*/
|
||||
public function amount($amount)
|
||||
{
|
||||
return $this->formatter->amount($amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currency symbol.
|
||||
*
|
||||
* @param string $currency
|
||||
* @return string
|
||||
*/
|
||||
public function currency($currency)
|
||||
{
|
||||
return $this->formatter->currency($currency);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $language
|
||||
* @return string
|
||||
*/
|
||||
public function language($language)
|
||||
{
|
||||
return $this->formatter->language($language);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $country
|
||||
* @return string
|
||||
*/
|
||||
public function country($country)
|
||||
{
|
||||
return $this->formatter->country($country);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $amount
|
||||
* @param string|null $currency
|
||||
* @param bool $withCurrency
|
||||
* @return string
|
||||
*/
|
||||
public function money($amount, ?string $currency = null, bool $withCurrency = true)
|
||||
{
|
||||
return $this->formatter->money($amount, $currency, $withCurrency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the list of codes of the locales (languages) enabled in the
|
||||
* application and returns an array with the name of each locale written
|
||||
* in its own language (e.g. English, Français, Español, etc.)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocales()
|
||||
{
|
||||
return $this->formatter->getLocales();
|
||||
}
|
||||
}
|
||||
298
src/Twig/LocaleFormatExtensions.php
Normal file
298
src/Twig/LocaleFormatExtensions.php
Normal file
@@ -0,0 +1,298 @@
|
||||
<?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;
|
||||
|
||||
use App\Configuration\LanguageFormattings;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Utils\LocaleFormats;
|
||||
use App\Utils\LocaleFormatter;
|
||||
use DateTime;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\TwigTest;
|
||||
|
||||
final class LocaleFormatExtensions extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var LanguageFormattings|null
|
||||
*/
|
||||
private $formats;
|
||||
/**
|
||||
* @var LocaleFormats|null
|
||||
*/
|
||||
private $localeFormats;
|
||||
/**
|
||||
* @var LocaleFormatter|null
|
||||
*/
|
||||
private $formatter;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $locale;
|
||||
|
||||
public function __construct(LanguageFormattings $formats)
|
||||
{
|
||||
$this->formats = $formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('month_name', [$this, 'monthName']),
|
||||
new TwigFilter('day_name', [$this, 'dayName']),
|
||||
new TwigFilter('date_short', [$this, 'dateShort']),
|
||||
new TwigFilter('date_time', [$this, 'dateTime']),
|
||||
new TwigFilter('date_full', [$this, 'dateTimeFull']),
|
||||
new TwigFilter('date_format', [$this, 'dateFormat']),
|
||||
new TwigFilter('time', [$this, 'time']),
|
||||
new TwigFilter('hour24', [$this, 'hour24']),
|
||||
new TwigFilter('duration', [$this, 'duration']),
|
||||
new TwigFilter('duration_decimal', [$this, 'durationDecimal']),
|
||||
new TwigFilter('money', [$this, 'money']),
|
||||
new TwigFilter('currency', [$this, 'currency']),
|
||||
new TwigFilter('country', [$this, 'country']),
|
||||
new TwigFilter('language', [$this, 'language']),
|
||||
new TwigFilter('amount', [$this, 'amount']),
|
||||
];
|
||||
}
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
return [
|
||||
new TwigTest('weekend', function ($dateTime) {
|
||||
if (!$dateTime instanceof \DateTime) {
|
||||
return false;
|
||||
}
|
||||
$day = (int) $dateTime->format('w');
|
||||
|
||||
return ($day === 0 || $day === 6);
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('get_format_duration', [$this, 'getDurationFormat']),
|
||||
new TwigFunction('create_date', [$this, 'createDate']),
|
||||
new TwigFunction('locales', [$this, 'getLocales']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to switch the locale used for all twig filter and functions.
|
||||
*
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setLocale(string $locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
$this->formatter = null;
|
||||
$this->localeFormats = null;
|
||||
}
|
||||
|
||||
private function getLocaleFormats(): LocaleFormats
|
||||
{
|
||||
if (null === $this->localeFormats) {
|
||||
$this->localeFormats = new LocaleFormats($this->formats, $this->getLocale());
|
||||
}
|
||||
|
||||
return $this->localeFormats;
|
||||
}
|
||||
|
||||
private function getFormatter(): LocaleFormatter
|
||||
{
|
||||
if (null === $this->formatter) {
|
||||
$this->formatter = new LocaleFormatter($this->formats, $this->getLocale());
|
||||
}
|
||||
|
||||
return $this->formatter;
|
||||
}
|
||||
|
||||
private function getLocale()
|
||||
{
|
||||
if (null === $this->locale) {
|
||||
$this->locale = \Locale::getDefault();
|
||||
}
|
||||
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @return string
|
||||
*/
|
||||
public function dateShort($date)
|
||||
{
|
||||
return $this->getFormatter()->dateShort($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @return string
|
||||
*/
|
||||
public function dateTime($date)
|
||||
{
|
||||
return $this->getFormatter()->dateTime($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @return bool|false|string
|
||||
*/
|
||||
public function dateTimeFull($date)
|
||||
{
|
||||
return $this->getFormatter()->dateTimeFull($date);
|
||||
}
|
||||
|
||||
public function createDate(string $date, ?User $user = null): \DateTime
|
||||
{
|
||||
$timezone = $user !== null ? $user->getTimezone() : date_default_timezone_get();
|
||||
|
||||
return new DateTime($date, new \DateTimeZone($timezone));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @param string $format
|
||||
* @return false|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function dateFormat($date, string $format)
|
||||
{
|
||||
return $this->getFormatter()->dateFormat($date, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime|string $date
|
||||
* @return string
|
||||
*/
|
||||
public function time($date)
|
||||
{
|
||||
return $this->getFormatter()->time($date);
|
||||
}
|
||||
|
||||
public function monthName(\DateTime $dateTime, bool $withYear = false): string
|
||||
{
|
||||
return $this->getFormatter()->monthName($dateTime, $withYear);
|
||||
}
|
||||
|
||||
public function dayName(\DateTime $dateTime, bool $short = false): string
|
||||
{
|
||||
return $this->getFormatter()->dayName($dateTime, $short);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $twentyFour
|
||||
* @param mixed $twelveHour
|
||||
* @return mixed
|
||||
*/
|
||||
public function hour24($twentyFour, $twelveHour)
|
||||
{
|
||||
return $this->getFormatter()->hour24($twentyFour, $twelveHour);
|
||||
}
|
||||
|
||||
public function getDurationFormat(): string
|
||||
{
|
||||
return $this->getLocaleFormats()->getDurationFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms seconds into a duration string.
|
||||
*
|
||||
* @param int|Timesheet|null $duration
|
||||
* @param bool $decimal
|
||||
* @return string
|
||||
*/
|
||||
public function duration($duration, $decimal = false)
|
||||
{
|
||||
return $this->getFormatter()->duration($duration, $decimal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms seconds into a decimal formatted duration string.
|
||||
*
|
||||
* @param int|Timesheet|null $duration
|
||||
* @return string
|
||||
*/
|
||||
public function durationDecimal($duration)
|
||||
{
|
||||
return $this->getFormatter()->durationDecimal($duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|float $amount
|
||||
* @return bool|false|string
|
||||
*/
|
||||
public function amount($amount)
|
||||
{
|
||||
return $this->getFormatter()->amount($amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currency symbol.
|
||||
*
|
||||
* @param string $currency
|
||||
* @return string
|
||||
*/
|
||||
public function currency($currency)
|
||||
{
|
||||
return $this->getFormatter()->currency($currency);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $language
|
||||
* @return string
|
||||
*/
|
||||
public function language($language)
|
||||
{
|
||||
return $this->getFormatter()->language($language);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $country
|
||||
* @return string
|
||||
*/
|
||||
public function country($country)
|
||||
{
|
||||
return $this->getFormatter()->country($country);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $amount
|
||||
* @param string|null $currency
|
||||
* @param bool $withCurrency
|
||||
* @return string
|
||||
*/
|
||||
public function money($amount, ?string $currency = null, bool $withCurrency = true)
|
||||
{
|
||||
return $this->getFormatter()->money($amount, $currency, $withCurrency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the list of codes of the locales (languages) enabled in the
|
||||
* application and returns an array with the name of each locale written
|
||||
* in its own language (e.g. English, Français, Español, etc.)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocales()
|
||||
{
|
||||
return $this->getFormatter()->getLocales();
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ class PaginationExtension extends AbstractExtension
|
||||
|
||||
public function __construct(UrlGeneratorInterface $router)
|
||||
{
|
||||
$this->view = new TwitterBootstrap3View();
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
@@ -46,6 +45,15 @@ class PaginationExtension extends AbstractExtension
|
||||
];
|
||||
}
|
||||
|
||||
private function getView(): ViewInterface
|
||||
{
|
||||
if (null === $this->view) {
|
||||
$this->view = new TwitterBootstrap3View();
|
||||
}
|
||||
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.8
|
||||
*/
|
||||
@@ -70,7 +78,7 @@ class PaginationExtension extends AbstractExtension
|
||||
$options['prev_message'] = '<i class="fas fa-chevron-left"></i>';
|
||||
$options['next_message'] = '<i class="fas fa-chevron-right"></i>';
|
||||
|
||||
return $this->view->render($pagerfanta, $routeGenerator, $options);
|
||||
return $this->getView()->render($pagerfanta, $routeGenerator, $options);
|
||||
}
|
||||
|
||||
private function createRouteGenerator(array $options = [])
|
||||
|
||||
@@ -7,15 +7,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Twig;
|
||||
namespace App\Twig\Runtime;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Contracts\Service\ServiceSubscriberInterface;
|
||||
use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
|
||||
class EncoreExtension extends AbstractExtension implements ServiceSubscriberInterface
|
||||
final class EncoreExtension implements RuntimeExtensionInterface, ServiceSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -39,16 +38,6 @@ class EncoreExtension extends AbstractExtension implements ServiceSubscriberInte
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('encore_entry_css_source', [$this, 'getEncoreEntryCssSource']),
|
||||
];
|
||||
}
|
||||
|
||||
public function getEncoreEntryCssSource(string $packageName): string
|
||||
{
|
||||
$lookup = $this->container->get(EntrypointLookupInterface::class);
|
||||
@@ -7,13 +7,12 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Twig;
|
||||
namespace App\Twig\Runtime;
|
||||
|
||||
use App\Export\ServiceExport;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
|
||||
class TimesheetExtension extends AbstractExtension
|
||||
final class ExporterExtension implements RuntimeExtensionInterface
|
||||
{
|
||||
/**
|
||||
* @var ServiceExport
|
||||
@@ -25,16 +24,6 @@ class TimesheetExtension extends AbstractExtension
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('timesheet_exporter', [$this, 'getTimesheetExporter'], []),
|
||||
];
|
||||
}
|
||||
|
||||
public function getTimesheetExporter(): array
|
||||
{
|
||||
$ids = [];
|
||||
@@ -7,17 +7,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Twig;
|
||||
namespace App\Twig\Runtime;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Utils\Markdown;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
|
||||
/**
|
||||
* A twig extension to handle markdown content.
|
||||
*/
|
||||
final class MarkdownExtension extends AbstractExtension
|
||||
final class MarkdownExtension implements RuntimeExtensionInterface
|
||||
{
|
||||
/**
|
||||
* @var Markdown
|
||||
@@ -27,26 +23,24 @@ final class MarkdownExtension extends AbstractExtension
|
||||
* @var SystemConfiguration
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
/**
|
||||
* @param Markdown $parser
|
||||
* @var bool|null
|
||||
*/
|
||||
private $markdownEnabled;
|
||||
|
||||
public function __construct(Markdown $parser, SystemConfiguration $configuration)
|
||||
{
|
||||
$this->markdown = $parser;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFilter[]
|
||||
*/
|
||||
public function getFilters()
|
||||
private function isMarkdownEnabled(): bool
|
||||
{
|
||||
return [
|
||||
new TwigFilter('md2html', [$this, 'markdownToHtml'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
new TwigFilter('desc2html', [$this, 'timesheetContent'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
new TwigFilter('comment2html', [$this, 'commentContent'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
];
|
||||
if (null === $this->markdownEnabled) {
|
||||
$this->markdownEnabled = $this->configuration->isTimesheetMarkdownEnabled();
|
||||
}
|
||||
|
||||
return $this->markdownEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +60,7 @@ final class MarkdownExtension extends AbstractExtension
|
||||
$content = trim(substr($content, 0, 100)) . ' …';
|
||||
}
|
||||
|
||||
if ($this->configuration->isTimesheetMarkdownEnabled()) {
|
||||
if ($this->isMarkdownEnabled()) {
|
||||
$content = $this->markdown->toHtml($content, false);
|
||||
} elseif ($fullLength) {
|
||||
$content = '<p>' . nl2br($content) . '</p>';
|
||||
@@ -87,7 +81,7 @@ final class MarkdownExtension extends AbstractExtension
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->configuration->isTimesheetMarkdownEnabled()) {
|
||||
if ($this->isMarkdownEnabled()) {
|
||||
return $this->markdown->toHtml($content, false);
|
||||
}
|
||||
|
||||
@@ -9,37 +9,40 @@
|
||||
|
||||
namespace App\Twig\Runtime;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Event\ThemeEvent;
|
||||
use App\Event\ThemeJavascriptTranslationsEvent;
|
||||
use App\Security\CurrentUser;
|
||||
use Symfony\Bridge\Twig\AppVariable;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
|
||||
final class ThemeEventExtension implements RuntimeExtensionInterface
|
||||
final class ThemeExtension implements RuntimeExtensionInterface
|
||||
{
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
/**
|
||||
* @var CurrentUser
|
||||
*/
|
||||
private $user;
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher, CurrentUser $user)
|
||||
public function __construct(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $dispatcher;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Environment $environment
|
||||
* @param string $eventName
|
||||
* @param mixed|null $payload
|
||||
* @return ThemeEvent
|
||||
*/
|
||||
public function trigger(string $eventName, $payload = null): ThemeEvent
|
||||
public function trigger(Environment $environment, string $eventName, $payload = null): ThemeEvent
|
||||
{
|
||||
$themeEvent = new ThemeEvent($this->user->getUser(), $payload);
|
||||
/** @var AppVariable $app */
|
||||
$app = $environment->getGlobals()['app'];
|
||||
/** @var User $user */
|
||||
$user = $app->getUser();
|
||||
|
||||
$themeEvent = new ThemeEvent($user, $payload);
|
||||
|
||||
if ($this->eventDispatcher->hasListeners($eventName)) {
|
||||
$this->eventDispatcher->dispatch($themeEvent, $eventName);
|
||||
32
src/Twig/Runtime/TimesheetExtension.php
Normal file
32
src/Twig/Runtime/TimesheetExtension.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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\Repository\TimesheetRepository;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
|
||||
final class TimesheetExtension implements RuntimeExtensionInterface
|
||||
{
|
||||
/**
|
||||
* @var TimesheetRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
public function __construct(TimesheetRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function activeEntries(User $user): array
|
||||
{
|
||||
return $this->repository->getActiveEntries($user);
|
||||
}
|
||||
}
|
||||
@@ -7,37 +7,25 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Twig;
|
||||
namespace App\Twig\Runtime;
|
||||
|
||||
use App\Widget\WidgetException;
|
||||
use App\Widget\WidgetInterface;
|
||||
use App\Widget\WidgetService;
|
||||
use InvalidArgumentException;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
|
||||
class WidgetExtension extends AbstractExtension
|
||||
final class WidgetExtension implements RuntimeExtensionInterface
|
||||
{
|
||||
/**
|
||||
* @var WidgetService
|
||||
*/
|
||||
protected $service;
|
||||
private $service;
|
||||
|
||||
public function __construct(WidgetService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('render_widget', [$this, 'renderWidget'], ['is_safe' => ['html']]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WidgetInterface|string $widget
|
||||
* @param array $options
|
||||
@@ -47,12 +35,12 @@ class WidgetExtension extends AbstractExtension
|
||||
public function renderWidget($widget, array $options = [])
|
||||
{
|
||||
if (!($widget instanceof WidgetInterface) && !\is_string($widget)) {
|
||||
throw new InvalidArgumentException('Widget must either implement WidgetInterface or be a string');
|
||||
throw new \InvalidArgumentException('Widget must either implement WidgetInterface or be a string');
|
||||
}
|
||||
|
||||
if (\is_string($widget)) {
|
||||
if (!$this->service->hasWidget($widget)) {
|
||||
throw new InvalidArgumentException(sprintf('Unknown widget "%s" requested', $widget));
|
||||
throw new \InvalidArgumentException(sprintf('Unknown widget "%s" requested', $widget));
|
||||
}
|
||||
|
||||
$widget = $this->service->getWidget($widget);
|
||||
50
src/Twig/RuntimeExtensions.php
Normal file
50
src/Twig/RuntimeExtensions.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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;
|
||||
|
||||
use App\Twig\Runtime\EncoreExtension;
|
||||
use App\Twig\Runtime\ExporterExtension;
|
||||
use App\Twig\Runtime\MarkdownExtension;
|
||||
use App\Twig\Runtime\ThemeExtension;
|
||||
use App\Twig\Runtime\TimesheetExtension;
|
||||
use App\Twig\Runtime\WidgetExtension;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class RuntimeExtensions extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('trigger', [ThemeExtension::class, 'trigger'], ['needs_environment' => true]),
|
||||
new TwigFunction('javascript_translations', [ThemeExtension::class, 'getJavascriptTranslations']),
|
||||
new TwigFunction('timesheet_exporter', [ExporterExtension::class, 'getTimesheetExporter']),
|
||||
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']]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('md2html', [MarkdownExtension::class, 'markdownToHtml'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
new TwigFilter('desc2html', [MarkdownExtension::class, 'timesheetContent'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
new TwigFilter('comment2html', [MarkdownExtension::class, 'commentContent'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,15 @@ namespace App\Widget;
|
||||
|
||||
use App\Repository\WidgetRepository;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class WidgetService
|
||||
{
|
||||
/**
|
||||
* @var WidgetRendererInterface[]
|
||||
*/
|
||||
private $renderer = [];
|
||||
private $renderer;
|
||||
/**
|
||||
* @var WidgetRepository
|
||||
*/
|
||||
@@ -28,9 +31,7 @@ class WidgetService
|
||||
*/
|
||||
public function __construct(WidgetRepository $repository, iterable $renderer)
|
||||
{
|
||||
foreach ($renderer as $render) {
|
||||
$this->addRenderer($render);
|
||||
}
|
||||
$this->renderer = $renderer;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ class WidgetService
|
||||
/**
|
||||
* @return WidgetRendererInterface[]
|
||||
*/
|
||||
public function getRenderer(): array
|
||||
public function getRenderer(): iterable
|
||||
{
|
||||
return $this->renderer;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user