Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
209
src/Widget/DataProvider/DailyWorkingTimeChartProvider.php
Normal file
209
src/Widget/DataProvider/DailyWorkingTimeChartProvider.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?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\Widget\DataProvider;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Model\Statistic\Day;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* This class should really be deleted and replaced by TimesheetStatisticService::getDailyStatistics()
|
||||
* @deprecated since 2.0
|
||||
* @codeCoverageIgnore
|
||||
* @internal
|
||||
* @final
|
||||
*/
|
||||
class DailyWorkingTimeChartProvider
|
||||
{
|
||||
public function __construct(private TimesheetRepository $repository)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* In case this method is called with one timezone and the results are from another timezone,
|
||||
* it might return rows outside the time-range.
|
||||
*
|
||||
* @param DateTimeInterface $begin
|
||||
* @param DateTimeInterface $end
|
||||
* @param User|null $user
|
||||
* @return array<mixed>
|
||||
*/
|
||||
protected function getDailyData(DateTimeInterface $begin, DateTimeInterface $end, ?User $user = null): array
|
||||
{
|
||||
$qb = $this->repository->createQueryBuilder('t');
|
||||
|
||||
$or = $qb->expr()->orX();
|
||||
$or->add($qb->expr()->between(':begin', 't.begin', 't.end'));
|
||||
$or->add($qb->expr()->between(':end', 't.begin', 't.end'));
|
||||
$or->add($qb->expr()->between('t.begin', ':begin', ':end'));
|
||||
$or->add($qb->expr()->between('t.end', ':begin', ':end'));
|
||||
|
||||
$qb->select('t, p, a, c')
|
||||
->andWhere($qb->expr()->isNotNull('t.end'))
|
||||
->andWhere($or)
|
||||
->orderBy('t.begin', 'DESC')
|
||||
->setParameter('begin', $begin)
|
||||
->setParameter('end', $end)
|
||||
->leftJoin('t.activity', 'a')
|
||||
->leftJoin('t.project', 'p')
|
||||
->leftJoin('p.customer', 'c')
|
||||
;
|
||||
|
||||
if (null !== $user) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->eq('t.user', ':user'))
|
||||
->setParameter('user', $user)
|
||||
;
|
||||
}
|
||||
|
||||
$timesheets = $qb->getQuery()->getResult();
|
||||
|
||||
$results = [];
|
||||
/** @var Timesheet $result */
|
||||
foreach ($timesheets as $result) {
|
||||
/** @var DateTime $beginTmp */
|
||||
$beginTmp = $result->getBegin();
|
||||
/** @var DateTime $endTmp */
|
||||
$endTmp = $result->getEnd();
|
||||
$dateKeyEnd = $endTmp->format('Ymd');
|
||||
|
||||
do {
|
||||
$dateKey = $beginTmp->format('Ymd');
|
||||
|
||||
if ($dateKey !== $dateKeyEnd) {
|
||||
$newDateBegin = clone $beginTmp;
|
||||
$newDateBegin->add(new \DateInterval('P1D'));
|
||||
// overlapping records should always start at midnight
|
||||
$newDateBegin->setTime(0, 0, 0);
|
||||
} else {
|
||||
$newDateBegin = clone $endTmp;
|
||||
}
|
||||
|
||||
// make sure to exclude entries that are outside the requested time-range:
|
||||
// these entries can exist if you have long running entries that started before $begin
|
||||
// for statistical reasons we have to include everything between $begin and $end while
|
||||
// excluding everything that is outside of that range
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Be aware that this will NOT filter every record, in case there is a timezone mismatch between the
|
||||
// begin/end dates and the ones from the database (eg. recorded in UTC) - which might actually be
|
||||
// before $begin (which happens thanks to the timezone conversion when querying the database)
|
||||
if ($newDateBegin > $begin && $beginTmp < $end) {
|
||||
if (!isset($results[$dateKey])) {
|
||||
$results[$dateKey] = [
|
||||
'rate' => 0,
|
||||
'duration' => 0,
|
||||
'billable' => 0, // duration
|
||||
'month' => $beginTmp->format('n'),
|
||||
'year' => $beginTmp->format('Y'),
|
||||
'day' => $beginTmp->format('j'),
|
||||
'details' => []
|
||||
];
|
||||
}
|
||||
$duration = $newDateBegin->getTimestamp() - $beginTmp->getTimestamp();
|
||||
$durationPercent = 0;
|
||||
if ($result->getDuration() !== null && $result->getDuration() > 0) {
|
||||
$durationPercent = $duration / $result->getDuration();
|
||||
}
|
||||
$rate = $result->getRate() * $durationPercent;
|
||||
|
||||
$results[$dateKey]['rate'] += $rate;
|
||||
$results[$dateKey]['duration'] += $duration;
|
||||
if ($result->isBillable()) {
|
||||
$results[$dateKey]['billable'] += $duration;
|
||||
}
|
||||
$detailsId =
|
||||
$result->getProject()->getCustomer()->getId()
|
||||
. '_' . $result->getProject()->getId()
|
||||
. '_' . $result->getActivity()->getId()
|
||||
;
|
||||
|
||||
if (!isset($results[$dateKey]['details'][$detailsId])) {
|
||||
$results[$dateKey]['details'][$detailsId] = [
|
||||
'project' => $result->getProject(),
|
||||
'activity' => $result->getActivity(),
|
||||
'duration' => 0,
|
||||
'rate' => 0,
|
||||
'billable' => 0, // duration
|
||||
];
|
||||
}
|
||||
|
||||
$results[$dateKey]['details'][$detailsId]['duration'] += $duration;
|
||||
$results[$dateKey]['details'][$detailsId]['rate'] += $rate;
|
||||
if ($result->isBillable()) {
|
||||
$results[$dateKey]['details'][$detailsId]['billable'] += $duration;
|
||||
}
|
||||
}
|
||||
|
||||
$beginTmp = $newDateBegin;
|
||||
|
||||
// yes, we only want to compare the day, not the time
|
||||
if ((int) $end->format('Ymd') < (int) $newDateBegin->format('Ymd')) {
|
||||
break;
|
||||
}
|
||||
} while ($dateKey !== $dateKeyEnd);
|
||||
}
|
||||
|
||||
ksort($results);
|
||||
|
||||
foreach ($results as $key => $value) {
|
||||
$results[$key]['details'] = array_values($results[$key]['details']);
|
||||
}
|
||||
|
||||
return array_values($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 2.0 - use TimesheetStatisticService::getDailyStatistics() instead
|
||||
*
|
||||
* @param User|null $user
|
||||
* @param DateTimeInterface $begin
|
||||
* @param DateTimeInterface $end
|
||||
* @return Day[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getData(?User $user, DateTimeInterface $begin, DateTimeInterface $end): array
|
||||
{
|
||||
/** @var Day[] $days */
|
||||
$days = [];
|
||||
|
||||
// prefill the array
|
||||
$tmp = DateTime::createFromInterface($end);
|
||||
$until = (int) $begin->format('Ymd');
|
||||
while ((int) $tmp->format('Ymd') >= $until) {
|
||||
$last = clone $tmp;
|
||||
$days[$last->format('Ymd')] = new Day($last, 0, 0.00);
|
||||
$tmp->modify('-1 day');
|
||||
}
|
||||
|
||||
$results = $this->getDailyData($begin, $end, $user);
|
||||
|
||||
foreach ($results as $statRow) {
|
||||
$dateTime = DateTime::createFromInterface($begin);
|
||||
$dateTime->setDate($statRow['year'], $statRow['month'], $statRow['day']);
|
||||
$dateTime->setTime(0, 0, 0);
|
||||
$day = new Day($dateTime, (int) $statRow['duration'], (float) $statRow['rate']);
|
||||
$day->setTotalDurationBillable($statRow['billable']);
|
||||
$day->setDetails($statRow['details']);
|
||||
$dateKey = $dateTime->format('Ymd');
|
||||
// make sure entries from other timezones are filtered
|
||||
if (!\array_key_exists($dateKey, $days)) {
|
||||
continue;
|
||||
}
|
||||
$days[$dateKey] = $day;
|
||||
}
|
||||
|
||||
ksort($days);
|
||||
|
||||
return array_values($days);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +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\Widget\Renderer;
|
||||
|
||||
use App\Widget\WidgetRendererInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
abstract class AbstractTwigRenderer implements WidgetRendererInterface
|
||||
{
|
||||
/**
|
||||
* @var Environment
|
||||
*/
|
||||
protected $twig;
|
||||
|
||||
/**
|
||||
* @param Environment $twig
|
||||
*/
|
||||
public function __construct(Environment $twig)
|
||||
{
|
||||
$this->twig = $twig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
protected function renderTemplate(string $template, array $data): string
|
||||
{
|
||||
return $this->twig->render($template, $data);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +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\Widget\Renderer;
|
||||
|
||||
use App\Widget\Type\CompoundChart;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
class CompoundChartRenderer extends AbstractTwigRenderer
|
||||
{
|
||||
public function supports(WidgetInterface $widget): bool
|
||||
{
|
||||
return ($widget instanceof CompoundChart);
|
||||
}
|
||||
|
||||
public function render(WidgetInterface $widget, array $options = []): string
|
||||
{
|
||||
return $this->renderTemplate('widget/section-chart.html.twig', [
|
||||
'title' => $widget->getTitle(),
|
||||
'widgets' => $widget->getData(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +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\Widget\Renderer;
|
||||
|
||||
use App\Widget\Type\CompoundRow;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
class CompoundRowRenderer extends AbstractTwigRenderer
|
||||
{
|
||||
public function supports(WidgetInterface $widget): bool
|
||||
{
|
||||
return ($widget instanceof CompoundRow);
|
||||
}
|
||||
|
||||
public function render(WidgetInterface $widget, array $options = []): string
|
||||
{
|
||||
return $this->renderTemplate('widget/section-simple.html.twig', [
|
||||
'title' => $widget->getTitle(),
|
||||
'widgets' => $widget->getData(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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\Widget\Renderer;
|
||||
|
||||
use App\Widget\Type\SimpleWidget;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
class SimpleWidgetRenderer extends AbstractTwigRenderer
|
||||
{
|
||||
public function supports(WidgetInterface $widget): bool
|
||||
{
|
||||
return $widget instanceof SimpleWidget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SimpleWidget $widget
|
||||
* @param array $options
|
||||
* @return string
|
||||
* @throws \ReflectionException
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
public function render(WidgetInterface $widget, array $options = []): string
|
||||
{
|
||||
return $this->renderTemplate($widget->getTemplateName(), [
|
||||
'data' => $widget->getData($options),
|
||||
'options' => $widget->getOptions($options),
|
||||
'title' => $widget->getTitle(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
45
src/Widget/Type/AbstractActiveUsers.php
Normal file
45
src/Widget/Type/AbstractActiveUsers.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Repository\TimesheetRepository;
|
||||
|
||||
abstract class AbstractActiveUsers extends AbstractSimpleStatisticChart
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'icon' => 'users',
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(false);
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_USER);
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.' . lcfirst($this->getId());
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['ROLE_TEAMLEAD'];
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter.html.twig';
|
||||
}
|
||||
}
|
||||
@@ -13,44 +13,47 @@ use App\Event\RevenueStatisticEvent;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
abstract class AbstractAmountPeriod extends SimpleStatisticChart
|
||||
abstract class AbstractAmountPeriod extends AbstractWidget
|
||||
{
|
||||
private $dispatcher;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, EventDispatcherInterface $dispatcher)
|
||||
public function __construct(private TimesheetRepository $repository, private EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
parent::__construct($repository);
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.' . $this->getId();
|
||||
return 'stats.' . lcfirst($this->getId());
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter.html.twig';
|
||||
return 'widget/widget-counter-money.html.twig';
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'icon' => 'money',
|
||||
'dataType' => 'money',
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
protected function getRevenue(?string $begin, ?string $end, array $options = [])
|
||||
{
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_RATE);
|
||||
$this->setQueryWithUser(false);
|
||||
$user = $this->getUser();
|
||||
$timezone = new \DateTimeZone($user->getTimezone());
|
||||
|
||||
$data = parent::getData($options);
|
||||
if ($begin !== null) {
|
||||
$begin = new \DateTime($begin, $timezone);
|
||||
}
|
||||
|
||||
$event = new RevenueStatisticEvent($this->begin, $this->end);
|
||||
if ($data !== null) {
|
||||
$event->addRevenue($data);
|
||||
if ($end !== null) {
|
||||
$end = new \DateTime($end, $timezone);
|
||||
}
|
||||
|
||||
$data = $this->repository->getRevenue($begin, $end, null);
|
||||
|
||||
$event = new RevenueStatisticEvent($begin, $end);
|
||||
foreach ($data as $row) {
|
||||
$event->addRevenue($row->getCurrency(), $row->getAmount());
|
||||
}
|
||||
$this->dispatcher->dispatch($event);
|
||||
|
||||
|
||||
@@ -1,95 +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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetContainerInterface;
|
||||
use App\Widget\WidgetInterface;
|
||||
use BadMethodCallException;
|
||||
|
||||
abstract class AbstractContainer implements WidgetContainerInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
/**
|
||||
* @var WidgetInterface[]
|
||||
*/
|
||||
protected $widgets = [];
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $order = 0;
|
||||
|
||||
public function setTitle(string $title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return WidgetInterface[]|array|mixed|null
|
||||
*/
|
||||
public function getData(array $options = [])
|
||||
{
|
||||
return $this->getWidgets();
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setOption(string $name, $value): void
|
||||
{
|
||||
throw new BadMethodCallException('setOption() is not supported on AbstractContainer');
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getOrder(): int
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
public function setOrder(int $order)
|
||||
{
|
||||
$this->order = $order;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WidgetInterface[]
|
||||
*/
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return $this->widgets;
|
||||
}
|
||||
|
||||
public function addWidget(WidgetInterface $widget)
|
||||
{
|
||||
$this->widgets[] = $widget;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
39
src/Widget/Type/AbstractCounterDuration.php
Normal file
39
src/Widget/Type/AbstractCounterDuration.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Repository\TimesheetRepository;
|
||||
|
||||
abstract class AbstractCounterDuration extends AbstractSimpleStatisticChart
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'icon' => 'duration',
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_DURATION);
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.' . lcfirst($this->getId());
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter-duration.html.twig';
|
||||
}
|
||||
}
|
||||
56
src/Widget/Type/AbstractCounterYear.php
Normal file
56
src/Widget/Type/AbstractCounterYear.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
|
||||
abstract class AbstractCounterYear extends AbstractSimpleStatisticChart
|
||||
{
|
||||
private bool $isFinancialYear = false;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, private SystemConfiguration $systemConfiguration)
|
||||
{
|
||||
parent::__construct($repository);
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('01 january this year 00:00:00');
|
||||
$this->setEnd('31 december this year 23:59:59');
|
||||
|
||||
if (null !== ($financialYear = $this->systemConfiguration->getFinancialYearStart())) {
|
||||
$factory = new DateTimeFactory($this->getTimezone());
|
||||
$begin = $factory->createStartOfFinancialYear($financialYear);
|
||||
$this->setBegin($begin);
|
||||
$this->setEnd($factory->createEndOfFinancialYear($begin));
|
||||
$this->isFinancialYear = true;
|
||||
}
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
|
||||
abstract protected function getFinancialYearTitle(): string;
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
if ($this->isFinancialYear) {
|
||||
return $this->getFinancialYearTitle();
|
||||
}
|
||||
|
||||
return 'stats.' . lcfirst($this->getId());
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter.html.twig';
|
||||
}
|
||||
}
|
||||
139
src/Widget/Type/AbstractSimpleStatisticChart.php
Normal file
139
src/Widget/Type/AbstractSimpleStatisticChart.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Widget\WidgetException;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
abstract class AbstractSimpleStatisticChart extends AbstractWidgetType
|
||||
{
|
||||
/**
|
||||
* @var TimesheetRepository::STATS_QUERY_*
|
||||
*/
|
||||
private string $query;
|
||||
/**
|
||||
* @var string|\DateTime|null
|
||||
*/
|
||||
private $begin;
|
||||
/**
|
||||
* @var string|\DateTime|null
|
||||
*/
|
||||
private $end;
|
||||
private bool $queryWithUser = false;
|
||||
|
||||
public function __construct(private TimesheetRepository $repository)
|
||||
{
|
||||
}
|
||||
|
||||
public function getWidth(): int
|
||||
{
|
||||
return WidgetInterface::WIDTH_SMALL;
|
||||
}
|
||||
|
||||
public function getHeight(): int
|
||||
{
|
||||
return WidgetInterface::HEIGHT_SMALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetRepository::STATS_QUERY_* $query
|
||||
* @return void
|
||||
*/
|
||||
public function setQuery(string $query): void
|
||||
{
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
public function setBegin(null|string|\DateTime $begin): self
|
||||
{
|
||||
$this->begin = $begin;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBegin(): ?\DateTime
|
||||
{
|
||||
if ($this->begin === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->begin instanceof \DateTime) {
|
||||
return $this->begin;
|
||||
}
|
||||
|
||||
return new \DateTime($this->begin, $this->getTimezone());
|
||||
}
|
||||
|
||||
public function setEnd(null|string|\DateTime $end): self
|
||||
{
|
||||
$this->end = $end;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEnd(): ?\DateTime
|
||||
{
|
||||
if ($this->end === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->end instanceof \DateTime) {
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
return new \DateTime($this->end, $this->getTimezone());
|
||||
}
|
||||
|
||||
public function setQueryWithUser(bool $queryWithUser): self
|
||||
{
|
||||
$this->queryWithUser = $queryWithUser;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTimezone(): \DateTimeZone
|
||||
{
|
||||
$timezone = date_default_timezone_get();
|
||||
if (null !== $this->getUser()) {
|
||||
$timezone = $this->getUser()->getTimezone();
|
||||
}
|
||||
|
||||
return new \DateTimeZone($timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return mixed|null
|
||||
* @throws WidgetException
|
||||
*/
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
try {
|
||||
$user = null;
|
||||
if (true === $this->queryWithUser) {
|
||||
$user = $this->getUser();
|
||||
}
|
||||
|
||||
return $this->repository->getStatistic($this->query, $this->getBegin(), $this->getEnd(), $user);
|
||||
} catch (\Exception $ex) {
|
||||
throw new WidgetException(
|
||||
'Failed loading widget data: ' . $ex->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
$name = (new \ReflectionClass($this))->getShortName();
|
||||
|
||||
return sprintf('widget/widget-%s.html.twig', strtolower($name));
|
||||
}
|
||||
}
|
||||
@@ -1,59 +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\Widget\Type;
|
||||
|
||||
use App\Event\UserRevenueStatisticEvent;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
abstract class AbstractUserAmountPeriod extends SimpleStatisticChart
|
||||
{
|
||||
private $dispatcher;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
parent::__construct($repository);
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.' . str_replace('userA', 'a', $this->getId());
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter.html.twig';
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'icon' => 'money',
|
||||
'dataType' => 'money',
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
{
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_RATE);
|
||||
$this->setQueryWithUser(true);
|
||||
|
||||
$data = parent::getData($options);
|
||||
|
||||
$event = new UserRevenueStatisticEvent($this->user, $this->begin, $this->end);
|
||||
if ($data !== null) {
|
||||
$event->addRevenue($data);
|
||||
}
|
||||
$this->dispatcher->dispatch($event);
|
||||
|
||||
return $event->getRevenue();
|
||||
}
|
||||
}
|
||||
67
src/Widget/Type/AbstractUserRevenuePeriod.php
Normal file
67
src/Widget/Type/AbstractUserRevenuePeriod.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Event\UserRevenueStatisticEvent;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
abstract class AbstractUserRevenuePeriod extends AbstractWidget
|
||||
{
|
||||
public function __construct(private TimesheetRepository $repository, private EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.' . lcfirst($this->getId());
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter-money.html.twig';
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_rate_own_timesheet'];
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'icon' => 'money',
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
protected function getRevenue(?string $begin, ?string $end, array $options = [])
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$timezone = new \DateTimeZone($user->getTimezone());
|
||||
|
||||
if ($begin !== null) {
|
||||
$begin = new \DateTime($begin, $timezone);
|
||||
}
|
||||
|
||||
if ($end !== null) {
|
||||
$end = new \DateTime($end, $timezone);
|
||||
}
|
||||
|
||||
$data = $this->repository->getRevenue($begin, $end, $user);
|
||||
|
||||
$event = new UserRevenueStatisticEvent($user, $begin, $end);
|
||||
foreach ($data as $row) {
|
||||
$event->addRevenue($row->getCurrency(), $row->getAmount());
|
||||
}
|
||||
$this->dispatcher->dispatch($event);
|
||||
|
||||
return $event->getRevenue();
|
||||
}
|
||||
}
|
||||
74
src/Widget/Type/AbstractWidget.php
Normal file
74
src/Widget/Type/AbstractWidget.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Widget\WidgetInterface;
|
||||
use Symfony\Component\Form\Form;
|
||||
|
||||
abstract class AbstractWidget implements WidgetInterface
|
||||
{
|
||||
private array $options = [];
|
||||
private ?User $user = null;
|
||||
|
||||
public function hasForm(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getForm(): ?Form
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getHeight(): int
|
||||
{
|
||||
return WidgetInterface::HEIGHT_SMALL;
|
||||
}
|
||||
|
||||
public function getWidth(): int
|
||||
{
|
||||
return WidgetInterface::WIDTH_SMALL;
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setOption(string $name, $value): void
|
||||
{
|
||||
$this->options[$name] = $value;
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
public function isInternal(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -11,24 +11,40 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
abstract class AbstractWidgetType implements WidgetInterface
|
||||
abstract class AbstractWidgetType extends AbstractWidget
|
||||
{
|
||||
private ?string $id = null;
|
||||
private string $title = '';
|
||||
private int $height = WidgetInterface::HEIGHT_SMALL;
|
||||
private int $width = WidgetInterface::WIDTH_SMALL;
|
||||
/**
|
||||
* @var string
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $id;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
private array $permissions = [];
|
||||
|
||||
public function getHeight(): int
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
public function setHeight(int $height): AbstractWidgetType
|
||||
{
|
||||
$this->height = $height;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWidth(): int
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
public function setWidth(int $width): AbstractWidgetType
|
||||
{
|
||||
$this->width = $width;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setId(string $id): self
|
||||
{
|
||||
@@ -46,22 +62,6 @@ abstract class AbstractWidgetType implements WidgetInterface
|
||||
return (new \ReflectionClass($this))->getShortName();
|
||||
}
|
||||
|
||||
public function setData($data): self
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getData(array $options = [])
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): self
|
||||
{
|
||||
$this->title = $title;
|
||||
@@ -77,37 +77,21 @@ abstract class AbstractWidgetType implements WidgetInterface
|
||||
public function setOptions(array $options): self
|
||||
{
|
||||
foreach ($options as $key => $value) {
|
||||
$this->options[$key] = $value;
|
||||
$this->setOption($key, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setOption(string $name, $value): void
|
||||
public function getPermissions(): array
|
||||
{
|
||||
$this->options[$name] = $value;
|
||||
return $this->permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed|null $default
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getOption(string $name, $default = null)
|
||||
public function setPermissions(array $permissions): AbstractWidgetType
|
||||
{
|
||||
if (\array_key_exists($name, $this->options)) {
|
||||
return $this->options[$name];
|
||||
}
|
||||
$this->permissions = $permissions;
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge($this->options, $options);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
49
src/Widget/Type/ActiveTimesheets.php
Normal file
49
src/Widget/Type/ActiveTimesheets.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class ActiveTimesheets extends AbstractSimpleStatisticChart
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_TOTAL, 'icon' => 'duration'], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_all_data'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'activeRecordings';
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.activeRecordings';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(false);
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_ACTIVE);
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter.html.twig';
|
||||
}
|
||||
}
|
||||
33
src/Widget/Type/ActiveUsersMonth.php
Normal file
33
src/Widget/Type/ActiveUsersMonth.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class ActiveUsersMonth extends AbstractActiveUsers
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_MONTH], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'activeUsersMonth';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('first day of this month 00:00:00');
|
||||
$this->setEnd('last day of this month 23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
33
src/Widget/Type/ActiveUsersToday.php
Normal file
33
src/Widget/Type/ActiveUsersToday.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class ActiveUsersToday extends AbstractActiveUsers
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_TODAY], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'activeUsersToday';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('00:00:00');
|
||||
$this->setEnd('23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
30
src/Widget/Type/ActiveUsersTotal.php
Normal file
30
src/Widget/Type/ActiveUsersTotal.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class ActiveUsersTotal extends AbstractActiveUsers
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_TOTAL], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'activeUsersTotal';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
33
src/Widget/Type/ActiveUsersWeek.php
Normal file
33
src/Widget/Type/ActiveUsersWeek.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class ActiveUsersWeek extends AbstractActiveUsers
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_WEEK], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'activeUsersWeek';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('monday this week 00:00:00');
|
||||
$this->setEnd('sunday this week 23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
@@ -9,26 +9,39 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class ActiveUsersYear extends CounterYear
|
||||
final class ActiveUsersYear extends AbstractCounterYear
|
||||
{
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration)
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
parent::__construct($repository, $systemConfiguration);
|
||||
$this->setId('activeUsersYear');
|
||||
$this->setOption('icon', 'user');
|
||||
$this->setOption('color', 'yellow');
|
||||
$this->setTitle('stats.userActiveYear');
|
||||
return array_merge([
|
||||
'icon' => 'users',
|
||||
'color' => WidgetInterface::COLOR_YEAR,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->titleYear = 'stats.userActiveFinancialYear';
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_USER);
|
||||
$this->setQueryWithUser(false);
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
|
||||
protected function getFinancialYearTitle(): string
|
||||
{
|
||||
return 'stats.activeUsersFinancialYear';
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['ROLE_TEAMLEAD'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'activeUsersYear';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,16 @@ final class AmountMonth extends AbstractAmountPeriod
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'amountMonth';
|
||||
return 'AmountMonth';
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('first day of this month 00:00:00');
|
||||
$this->setEnd('last day of this month 23:59:59');
|
||||
return $this->getRevenue('first day of this month 00:00:00', 'last day of this month 23:59:59', $options);
|
||||
}
|
||||
|
||||
return parent::getData($options);
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_all_data'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,16 @@ final class AmountToday extends AbstractAmountPeriod
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'amountToday';
|
||||
return 'AmountToday';
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('00:00:00');
|
||||
$this->setEnd('23:59:59');
|
||||
return $this->getRevenue('00:00:00', '23:59:59', $options);
|
||||
}
|
||||
|
||||
return parent::getData($options);
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_all_data'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,14 @@ final class AmountTotal extends AbstractAmountPeriod
|
||||
{
|
||||
return 'amountTotal';
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_all_data'];
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
return $this->getRevenue(null, null, $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,16 @@ final class AmountWeek extends AbstractAmountPeriod
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'amountWeek';
|
||||
return 'AmountWeek';
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('monday this week 00:00:00');
|
||||
$this->setEnd('sunday this week 23:59:59');
|
||||
return $this->getRevenue('monday this week 00:00:00', 'sunday this week 23:59:59', $options);
|
||||
}
|
||||
|
||||
return parent::getData($options);
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_all_data'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,39 +11,60 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Event\RevenueStatisticEvent;
|
||||
use App\Model\Revenue;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Widget\WidgetInterface;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
final class AmountYear extends CounterYear
|
||||
final class AmountYear extends AbstractCounterYear
|
||||
{
|
||||
private $dispatcher;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration, EventDispatcherInterface $dispatcher)
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration, private EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
parent::__construct($repository, $systemConfiguration);
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->setId('amountYear');
|
||||
$this->setOption('dataType', 'money');
|
||||
$this->setOption('icon', 'money');
|
||||
$this->setOption('color', WidgetInterface::COLOR_YEAR);
|
||||
$this->setTitle('stats.amountYear');
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'icon' => 'money',
|
||||
'color' => WidgetInterface::COLOR_YEAR,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->titleYear = 'stats.amountFinancialYear';
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_RATE);
|
||||
$this->setQueryWithUser(false);
|
||||
|
||||
/** @var array<Revenue> $data */
|
||||
$data = parent::getData($options);
|
||||
|
||||
$event = new RevenueStatisticEvent($this->begin, $this->end);
|
||||
if ($data !== null) {
|
||||
$event->addRevenue($data);
|
||||
$event = new RevenueStatisticEvent($this->getBegin(), $this->getEnd());
|
||||
foreach ($data as $row) {
|
||||
$event->addRevenue($row->getCurrency(), $row->getAmount());
|
||||
}
|
||||
$this->dispatcher->dispatch($event);
|
||||
|
||||
return $event->getRevenue();
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'AmountYear';
|
||||
}
|
||||
|
||||
protected function getFinancialYearTitle(): string
|
||||
{
|
||||
return 'stats.amountFinancialYear';
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter-money.html.twig';
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_all_data'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +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\Widget\Type;
|
||||
|
||||
interface AuthorizedWidget
|
||||
{
|
||||
/**
|
||||
* Return a list of granted syntax string.
|
||||
* If ANY of the given permission strings matches, access is granted.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPermissions(): array;
|
||||
}
|
||||
@@ -1,14 +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\Widget\Type;
|
||||
|
||||
class CompoundChart extends AbstractContainer
|
||||
{
|
||||
}
|
||||
@@ -1,14 +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\Widget\Type;
|
||||
|
||||
class CompoundRow extends AbstractContainer
|
||||
{
|
||||
}
|
||||
@@ -9,13 +9,10 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Repository\TimesheetRepository;
|
||||
|
||||
final class Counter extends SimpleStatisticChart
|
||||
final class Counter extends AbstractSimpleStatisticChart
|
||||
{
|
||||
public function __construct(TimesheetRepository $repository)
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
parent::__construct($repository);
|
||||
$this->setOption('dataType', 'int');
|
||||
return 'widget/widget-counter.html.twig';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +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\Widget\Type;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
|
||||
class CounterYear extends SimpleStatisticChart
|
||||
{
|
||||
private $systemConfiguration;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $titleYear;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration)
|
||||
{
|
||||
parent::__construct($repository);
|
||||
$this->systemConfiguration = $systemConfiguration;
|
||||
$this->setOption('dataType', 'int');
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
{
|
||||
$this->begin = '01 january this year 00:00:00';
|
||||
$this->end = '31 december this year 23:59:59';
|
||||
|
||||
if (null !== ($financialYear = $this->systemConfiguration->getFinancialYearStart())) {
|
||||
$factory = new DateTimeFactory($this->getTimezone());
|
||||
$this->begin = $factory->createStartOfFinancialYear($financialYear);
|
||||
$this->end = $factory->createEndOfFinancialYear($this->begin);
|
||||
if (!empty($this->titleYear)) {
|
||||
$this->setTitle($this->titleYear);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter.html.twig';
|
||||
}
|
||||
}
|
||||
@@ -11,62 +11,54 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\User;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
use App\Widget\DataProvider\DailyWorkingTimeChartProvider;
|
||||
use App\Widget\WidgetInterface;
|
||||
use DateTime;
|
||||
|
||||
class DailyWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
/**
|
||||
* This is rendered inside the PaginatedWorkingTimeChart.
|
||||
*/
|
||||
final class DailyWorkingTimeChart extends AbstractWidget
|
||||
{
|
||||
public const DEFAULT_CHART = 'bar';
|
||||
|
||||
/**
|
||||
* @var TimesheetRepository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
public function __construct(TimesheetRepository $repository)
|
||||
public function __construct(private DailyWorkingTimeChartProvider $dailyWorkingTimeChartProvider)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->setId('DailyWorkingTimeChart');
|
||||
$this->setTitle('stats.yourWorkingHours');
|
||||
$this->setOptions([
|
||||
'begin' => null,
|
||||
'end' => null,
|
||||
'color' => '',
|
||||
'type' => self::DEFAULT_CHART,
|
||||
'id' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
public function getWidth(): int
|
||||
{
|
||||
$this->setOption('user', $user);
|
||||
return WidgetInterface::WIDTH_FULL;
|
||||
}
|
||||
|
||||
public function getHeight(): int
|
||||
{
|
||||
return WidgetInterface::HEIGHT_LARGE;
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_own_timesheet'];
|
||||
}
|
||||
|
||||
public function isInternal(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
$options = parent::getOptions($options);
|
||||
|
||||
if (!\in_array($options['type'], ['bar', 'line'])) {
|
||||
$options['type'] = self::DEFAULT_CHART;
|
||||
}
|
||||
|
||||
if (empty($options['id'])) {
|
||||
$options['id'] = uniqid('DailyWorkingTimeChart_');
|
||||
}
|
||||
|
||||
return $options;
|
||||
return array_merge([
|
||||
'begin' => null,
|
||||
'end' => null,
|
||||
'color' => '',
|
||||
'type' => 'bar',
|
||||
'id' => uniqid('DailyWorkingTimeChart_'),
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
|
||||
$user = $options['user'];
|
||||
if (null === $user || !($user instanceof User)) {
|
||||
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
|
||||
}
|
||||
$user = $this->getUser();
|
||||
|
||||
$dateTimeFactory = DateTimeFactory::createByUser($user);
|
||||
|
||||
@@ -74,7 +66,7 @@ class DailyWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
$options['begin'] = $dateTimeFactory->getStartOfWeek();
|
||||
}
|
||||
|
||||
if ($options['begin'] instanceof DateTime) {
|
||||
if ($options['begin'] instanceof \DateTimeInterface) {
|
||||
$begin = $options['begin'];
|
||||
} else {
|
||||
$begin = new DateTime($options['begin'], new \DateTimeZone($user->getTimezone()));
|
||||
@@ -84,14 +76,14 @@ class DailyWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
$options['end'] = $dateTimeFactory->getEndOfWeek($begin);
|
||||
}
|
||||
|
||||
if ($options['end'] instanceof DateTime) {
|
||||
if ($options['end'] instanceof \DateTimeInterface) {
|
||||
$end = $options['end'];
|
||||
} else {
|
||||
$end = new DateTime($options['end'], new \DateTimeZone($user->getTimezone()));
|
||||
}
|
||||
|
||||
$activities = [];
|
||||
$statistics = $this->repository->getDailyStats($user, $begin, $end);
|
||||
$statistics = $this->dailyWorkingTimeChartProvider->getData($user, $begin, $end);
|
||||
|
||||
foreach ($statistics as $day) {
|
||||
foreach ($day->getDetails() as $entry) {
|
||||
@@ -114,4 +106,19 @@ class DailyWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
'data' => $statistics,
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.yourWorkingHours';
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'DailyWorkingTimeChart';
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-dailyworkingtimechart.html.twig';
|
||||
}
|
||||
}
|
||||
|
||||
44
src/Widget/Type/DurationMonth.php
Normal file
44
src/Widget/Type/DurationMonth.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class DurationMonth extends AbstractCounterDuration
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_MONTH], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_other_timesheet'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'DurationMonth';
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.durationMonth';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(false);
|
||||
$this->setBegin('first day of this month 00:00:00');
|
||||
$this->setEnd('last day of this month 23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
44
src/Widget/Type/DurationToday.php
Normal file
44
src/Widget/Type/DurationToday.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class DurationToday extends AbstractCounterDuration
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_TODAY], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_other_timesheet'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'DurationToday';
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.durationToday';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(false);
|
||||
$this->setBegin('00:00:00');
|
||||
$this->setEnd('23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
42
src/Widget/Type/DurationTotal.php
Normal file
42
src/Widget/Type/DurationTotal.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class DurationTotal extends AbstractCounterDuration
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_TOTAL], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_other_timesheet'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'durationTotal';
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.durationTotal';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(false);
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
44
src/Widget/Type/DurationWeek.php
Normal file
44
src/Widget/Type/DurationWeek.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class DurationWeek extends AbstractCounterDuration
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_WEEK], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_other_timesheet'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'DurationWeek';
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.durationWeek';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(false);
|
||||
$this->setBegin('monday this week 00:00:00');
|
||||
$this->setEnd('sunday this week 23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
@@ -9,27 +9,44 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class DurationYear extends CounterYear
|
||||
final class DurationYear extends AbstractCounterYear
|
||||
{
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration)
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
parent::__construct($repository, $systemConfiguration);
|
||||
$this->setId('durationYear');
|
||||
$this->setOption('dataType', 'duration');
|
||||
$this->setOption('icon', 'duration');
|
||||
$this->setOption('color', 'yellow');
|
||||
$this->setTitle('stats.durationYear');
|
||||
return array_merge([
|
||||
'icon' => 'duration',
|
||||
'color' => WidgetInterface::COLOR_YEAR,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->titleYear = 'stats.durationFinancialYear';
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_DURATION);
|
||||
$this->setQueryWithUser(false);
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_other_timesheet'];
|
||||
}
|
||||
|
||||
protected function getFinancialYearTitle(): string
|
||||
{
|
||||
return 'stats.durationFinancialYear';
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter-duration.html.twig';
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'DurationYear';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,28 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
class More extends SimpleWidget
|
||||
class More extends AbstractWidgetType
|
||||
{
|
||||
public function __construct()
|
||||
private mixed $data = null;
|
||||
|
||||
public function setData($data): self
|
||||
{
|
||||
$this->setOption('dataType', 'int');
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-more.html.twig';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,31 +10,40 @@
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\User;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
use App\Widget\WidgetInterface;
|
||||
use DateTime;
|
||||
|
||||
final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
final class PaginatedWorkingTimeChart extends AbstractWidget
|
||||
{
|
||||
private $repository;
|
||||
private $systemConfiguration;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration)
|
||||
public function __construct(private TimesheetRepository $repository, private SystemConfiguration $systemConfiguration)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->systemConfiguration = $systemConfiguration;
|
||||
$this->setTitle('stats.yourWorkingHours');
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
public function getWidth(): int
|
||||
{
|
||||
$this->setOption('user', $user);
|
||||
$now = new DateTime('now', new \DateTimeZone($user->getTimezone()));
|
||||
$this->setOptions([
|
||||
'year' => $now->format('o'),
|
||||
'week' => $now->format('W'),
|
||||
]);
|
||||
return WidgetInterface::WIDTH_FULL;
|
||||
}
|
||||
|
||||
public function getHeight(): int
|
||||
{
|
||||
return WidgetInterface::HEIGHT_MAXIMUM;
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_own_timesheet'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.yourWorkingHours';
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-paginatedworkingtimechart.html.twig';
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
@@ -45,9 +54,20 @@ final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
$options['type'] = 'bar';
|
||||
}
|
||||
|
||||
if (!\array_key_exists('year', $options)) {
|
||||
$options['year'] = (new DateTime('now'))->format('o');
|
||||
$options['week'] = (new DateTime('now'))->format('W');
|
||||
if (!\array_key_exists('year', $options) || !\array_key_exists('week', $options)) {
|
||||
$timezone = date_default_timezone_get();
|
||||
if ($this->getUser() !== null) {
|
||||
$timezone = $this->getUser()->getTimezone();
|
||||
}
|
||||
$now = new DateTime('now', new \DateTimeZone($timezone));
|
||||
|
||||
if (!\array_key_exists('year', $options)) {
|
||||
$options['year'] = $now->format('o');
|
||||
}
|
||||
|
||||
if (!\array_key_exists('week', $options)) {
|
||||
$options['week'] = $now->format('W');
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
@@ -61,14 +81,9 @@ final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
return $lastWeekInYear->format('W') === '53' ? 53 : 52;
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
|
||||
$user = $options['user'];
|
||||
if (null === $user || !($user instanceof User)) {
|
||||
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
|
||||
}
|
||||
$user = $this->getUser();
|
||||
|
||||
$dateTimeFactory = DateTimeFactory::createByUser($user);
|
||||
|
||||
@@ -96,7 +111,7 @@ final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
|
||||
$yearBegin = $dateTimeFactory->createDateTime(sprintf('01 january %s 00:00:00', $year));
|
||||
$yearEnd = $dateTimeFactory->createDateTime(sprintf('31 december %s 23:59:59', $year));
|
||||
$yearData = $this->repository->getStatistic('duration', $yearBegin, $yearEnd, $user);
|
||||
$yearData = $this->repository->getStatistic(TimesheetRepository::STATS_QUERY_DURATION, $yearBegin, $yearEnd, $user);
|
||||
|
||||
$financialYearData = null;
|
||||
$financialYearBegin = null;
|
||||
@@ -104,22 +119,26 @@ final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
|
||||
if (null !== ($financialYear = $this->systemConfiguration->getFinancialYearStart())) {
|
||||
$financialYearBegin = $dateTimeFactory->createStartOfFinancialYear($financialYear);
|
||||
$financialYearEnd = $dateTimeFactory->createEndOfFinancialYear($financialYearBegin);
|
||||
$financialYearData = $this->repository->getStatistic('duration', $financialYearBegin, $financialYearEnd, $user);
|
||||
$financialYearData = $this->repository->getStatistic(TimesheetRepository::STATS_QUERY_DURATION, $financialYearBegin, $financialYearEnd, $user);
|
||||
}
|
||||
|
||||
return [
|
||||
'begin' => clone $weekBegin,
|
||||
'end' => clone $weekEnd,
|
||||
'stats' => $this->repository->getDailyStats($user, $weekBegin, $weekEnd),
|
||||
'thisMonth' => $thisMonth,
|
||||
'lastWeekInYear' => $lastWeekInYear,
|
||||
'lastWeekInLastYear' => $lastWeekInLastYear,
|
||||
'day' => $this->repository->getStatistic('duration', $dayBegin, $dayEnd, $user),
|
||||
'week' => $this->repository->getStatistic('duration', $weekBegin, $weekEnd, $user),
|
||||
'month' => $this->repository->getStatistic('duration', $monthBegin, $monthEnd, $user),
|
||||
'day' => $this->repository->getStatistic(TimesheetRepository::STATS_QUERY_DURATION, $dayBegin, $dayEnd, $user),
|
||||
'week' => $this->repository->getStatistic(TimesheetRepository::STATS_QUERY_DURATION, $weekBegin, $weekEnd, $user),
|
||||
'month' => $this->repository->getStatistic(TimesheetRepository::STATS_QUERY_DURATION, $monthBegin, $monthEnd, $user),
|
||||
'year' => $yearData,
|
||||
'financial' => $financialYearData,
|
||||
'financialBegin' => $financialYearBegin,
|
||||
];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'PaginatedWorkingTimeChart';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,129 +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\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Widget\WidgetException;
|
||||
|
||||
class SimpleStatisticChart extends SimpleWidget implements UserWidget
|
||||
{
|
||||
/**
|
||||
* @var TimesheetRepository
|
||||
*/
|
||||
private $repository;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $query;
|
||||
/**
|
||||
* @var string|\DateTime
|
||||
*/
|
||||
protected $begin;
|
||||
/**
|
||||
* @var string|\DateTime
|
||||
*/
|
||||
protected $end;
|
||||
/**
|
||||
* @var User|null
|
||||
*/
|
||||
protected $user;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $queryWithUser = false;
|
||||
|
||||
public function __construct(TimesheetRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function setQuery(string $query): SimpleStatisticChart
|
||||
{
|
||||
$this->query = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBegin(?string $begin): SimpleStatisticChart
|
||||
{
|
||||
$this->begin = $begin;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEnd(?string $end): SimpleStatisticChart
|
||||
{
|
||||
$this->end = $end;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function setData($data): AbstractWidgetType
|
||||
{
|
||||
throw new \InvalidArgumentException('Cannot set data on instances of SimpleStatisticChart');
|
||||
}
|
||||
|
||||
public function setQueryWithUser(bool $queryWithUser): SimpleStatisticChart
|
||||
{
|
||||
$this->queryWithUser = $queryWithUser;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTimezone(): \DateTimeZone
|
||||
{
|
||||
$timezone = date_default_timezone_get();
|
||||
if (null !== $this->user) {
|
||||
$timezone = $this->user->getTimezone();
|
||||
}
|
||||
|
||||
return new \DateTimeZone($timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return mixed|null
|
||||
* @throws WidgetException
|
||||
*/
|
||||
public function getData(array $options = [])
|
||||
{
|
||||
$timezone = $this->getTimezone();
|
||||
|
||||
$begin = $this->begin;
|
||||
$end = $this->end;
|
||||
|
||||
if (!empty($begin) && \is_string($begin)) {
|
||||
$this->begin = new \DateTime($begin, $timezone);
|
||||
}
|
||||
|
||||
if (!empty($end) && \is_string($end)) {
|
||||
$this->end = new \DateTime($end, $timezone);
|
||||
}
|
||||
|
||||
try {
|
||||
$user = null;
|
||||
if (true === $this->queryWithUser) {
|
||||
$user = $this->user;
|
||||
}
|
||||
|
||||
return $this->repository->getStatistic($this->query, $this->begin, $this->end, $user);
|
||||
} catch (\Exception $ex) {
|
||||
throw new WidgetException(
|
||||
'Failed loading widget data: ' . $ex->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +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\Widget\Type;
|
||||
|
||||
class SimpleWidget extends AbstractWidgetType
|
||||
{
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
$name = (new \ReflectionClass($this))->getShortName();
|
||||
|
||||
return sprintf('widget/widget-%s.html.twig', strtolower($name));
|
||||
}
|
||||
}
|
||||
@@ -9,20 +9,19 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\Query\ActivityQuery;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class TotalsActivity extends SimpleWidget implements UserWidget, AuthorizedWidget
|
||||
final class TotalsActivity extends AbstractWidget
|
||||
{
|
||||
use UserWidgetTrait;
|
||||
|
||||
private $activity;
|
||||
|
||||
public function __construct(ActivityRepository $activity)
|
||||
public function __construct(private ActivityRepository $activity)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
$this->setTitle('stats.activityTotal');
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.activityTotal';
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
@@ -30,20 +29,13 @@ final class TotalsActivity extends SimpleWidget implements UserWidget, Authorize
|
||||
return array_merge([
|
||||
'route' => 'admin_activity',
|
||||
'icon' => 'activity',
|
||||
'color' => 'primary',
|
||||
'dataType' => 'int',
|
||||
'color' => WidgetInterface::COLOR_TOTAL,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
|
||||
$user = $options['user'];
|
||||
if (null === $user || !($user instanceof User)) {
|
||||
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
|
||||
}
|
||||
|
||||
$user = $this->getUser();
|
||||
$query = new ActivityQuery();
|
||||
$query->setCurrentUser($user);
|
||||
|
||||
@@ -62,4 +54,9 @@ final class TotalsActivity extends SimpleWidget implements UserWidget, Authorize
|
||||
{
|
||||
return 'widget/widget-more.html.twig';
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'TotalsActivity';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,20 +9,19 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\Query\CustomerQuery;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class TotalsCustomer extends SimpleWidget implements UserWidget, AuthorizedWidget
|
||||
final class TotalsCustomer extends AbstractWidget
|
||||
{
|
||||
use UserWidgetTrait;
|
||||
|
||||
private $customer;
|
||||
|
||||
public function __construct(CustomerRepository $customer)
|
||||
public function __construct(private CustomerRepository $customer)
|
||||
{
|
||||
$this->customer = $customer;
|
||||
$this->setTitle('stats.customerTotal');
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.customerTotal';
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
@@ -30,20 +29,13 @@ final class TotalsCustomer extends SimpleWidget implements UserWidget, Authorize
|
||||
return array_merge([
|
||||
'route' => 'admin_customer',
|
||||
'icon' => 'customer',
|
||||
'color' => 'primary',
|
||||
'dataType' => 'int',
|
||||
'color' => WidgetInterface::COLOR_TOTAL,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
|
||||
$user = $options['user'];
|
||||
if (null === $user || !($user instanceof User)) {
|
||||
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
|
||||
}
|
||||
|
||||
$user = $this->getUser();
|
||||
$query = new CustomerQuery();
|
||||
$query->setCurrentUser($user);
|
||||
|
||||
@@ -62,4 +54,9 @@ final class TotalsCustomer extends SimpleWidget implements UserWidget, Authorize
|
||||
{
|
||||
return 'widget/widget-more.html.twig';
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'TotalsCustomer';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,20 +9,19 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\ProjectQuery;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class TotalsProject extends SimpleWidget implements UserWidget, AuthorizedWidget
|
||||
final class TotalsProject extends AbstractWidget
|
||||
{
|
||||
use UserWidgetTrait;
|
||||
|
||||
private $project;
|
||||
|
||||
public function __construct(ProjectRepository $project)
|
||||
public function __construct(private ProjectRepository $project)
|
||||
{
|
||||
$this->project = $project;
|
||||
$this->setTitle('stats.projectTotal');
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.projectTotal';
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
@@ -30,20 +29,13 @@ final class TotalsProject extends SimpleWidget implements UserWidget, Authorized
|
||||
return array_merge([
|
||||
'route' => 'admin_project',
|
||||
'icon' => 'project',
|
||||
'color' => 'primary',
|
||||
'dataType' => 'int',
|
||||
'color' => WidgetInterface::COLOR_TOTAL,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
|
||||
$user = $options['user'];
|
||||
if (null === $user || !($user instanceof User)) {
|
||||
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
|
||||
}
|
||||
|
||||
$user = $this->getUser();
|
||||
$query = new ProjectQuery();
|
||||
$query->setCurrentUser($user);
|
||||
|
||||
@@ -62,4 +54,9 @@ final class TotalsProject extends SimpleWidget implements UserWidget, Authorized
|
||||
{
|
||||
return 'widget/widget-more.html.twig';
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'TotalsProject';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,20 +9,14 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\Query\UserQuery;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class TotalsUser extends SimpleWidget implements UserWidget, AuthorizedWidget
|
||||
final class TotalsUser extends AbstractWidget
|
||||
{
|
||||
use UserWidgetTrait;
|
||||
|
||||
private $user;
|
||||
|
||||
public function __construct(UserRepository $user)
|
||||
public function __construct(private UserRepository $repository)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->setTitle('stats.userTotal');
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
@@ -30,29 +24,24 @@ final class TotalsUser extends SimpleWidget implements UserWidget, AuthorizedWid
|
||||
return array_merge([
|
||||
'route' => 'admin_user',
|
||||
'icon' => 'user',
|
||||
'color' => 'primary',
|
||||
'dataType' => 'int',
|
||||
'color' => WidgetInterface::COLOR_TOTAL,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
|
||||
$user = $options['user'];
|
||||
if (null === $user || !($user instanceof User)) {
|
||||
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
|
||||
}
|
||||
|
||||
$user = $this->getUser();
|
||||
$query = new UserQuery();
|
||||
$query->setCurrentUser($user);
|
||||
|
||||
return $this->user->countUsersForQuery($query);
|
||||
return $this->repository->countUsersForQuery($query);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'stats.userTotal';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_user'];
|
||||
@@ -62,4 +51,9 @@ final class TotalsUser extends SimpleWidget implements UserWidget, AuthorizedWid
|
||||
{
|
||||
return 'widget/widget-more.html.twig';
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'TotalsUser';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserAmountMonth extends AbstractUserAmountPeriod
|
||||
final class UserAmountMonth extends AbstractUserRevenuePeriod
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
@@ -20,14 +20,11 @@ final class UserAmountMonth extends AbstractUserAmountPeriod
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'userAmountMonth';
|
||||
return 'UserAmountMonth';
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('first day of this month 00:00:00');
|
||||
$this->setEnd('last day of this month 23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
return $this->getRevenue('first day of this month 00:00:00', 'last day of this month 23:59:59', $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserAmountToday extends AbstractUserAmountPeriod
|
||||
final class UserAmountToday extends AbstractUserRevenuePeriod
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
@@ -20,14 +20,11 @@ final class UserAmountToday extends AbstractUserAmountPeriod
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'userAmountToday';
|
||||
return 'UserAmountToday';
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('00:00:00');
|
||||
$this->setEnd('23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
return $this->getRevenue('00:00:00', '23:59:59', $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserAmountTotal extends AbstractUserAmountPeriod
|
||||
final class UserAmountTotal extends AbstractUserRevenuePeriod
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
@@ -20,6 +20,11 @@ final class UserAmountTotal extends AbstractUserAmountPeriod
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'userAmountTotal';
|
||||
return 'UserAmountTotal';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
return $this->getRevenue(null, null, $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserAmountWeek extends AbstractUserAmountPeriod
|
||||
final class UserAmountWeek extends AbstractUserRevenuePeriod
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
@@ -20,14 +20,11 @@ final class UserAmountWeek extends AbstractUserAmountPeriod
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'userAmountWeek';
|
||||
return 'UserAmountWeek';
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setBegin('monday this week 00:00:00');
|
||||
$this->setEnd('sunday this week 23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
return $this->getRevenue('monday this week 00:00:00', 'sunday this week 23:59:59', $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,36 +11,57 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Event\UserRevenueStatisticEvent;
|
||||
use App\Model\Revenue;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Widget\WidgetInterface;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
final class UserAmountYear extends CounterYear
|
||||
final class UserAmountYear extends AbstractCounterYear
|
||||
{
|
||||
private $dispatcher;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration, EventDispatcherInterface $dispatcher)
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration, private EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
parent::__construct($repository, $systemConfiguration);
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->setId('userAmountYear');
|
||||
$this->setOption('dataType', 'money');
|
||||
$this->setOption('icon', 'money');
|
||||
$this->setOption('color', WidgetInterface::COLOR_YEAR);
|
||||
$this->setTitle('stats.amountYear');
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter-money.html.twig';
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_rate_own_timesheet'];
|
||||
}
|
||||
|
||||
protected function getFinancialYearTitle(): string
|
||||
{
|
||||
return 'stats.amountFinancialYear';
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'UserAmountYear';
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'icon' => 'money',
|
||||
'color' => WidgetInterface::COLOR_YEAR,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->titleYear = 'stats.amountFinancialYear';
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_RATE);
|
||||
$this->setQueryWithUser(true);
|
||||
|
||||
/** @var array<Revenue> $data */
|
||||
$data = parent::getData($options);
|
||||
|
||||
$event = new UserRevenueStatisticEvent($this->user, $this->begin, $this->end);
|
||||
if ($data !== null) {
|
||||
$event->addRevenue($data);
|
||||
$event = new UserRevenueStatisticEvent($this->getUser(), $this->getBegin(), $this->getEnd());
|
||||
foreach ($data as $row) {
|
||||
$event->addRevenue($row->getCurrency(), $row->getAmount());
|
||||
}
|
||||
$this->dispatcher->dispatch($event);
|
||||
|
||||
|
||||
39
src/Widget/Type/UserDurationMonth.php
Normal file
39
src/Widget/Type/UserDurationMonth.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserDurationMonth extends AbstractCounterDuration
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_MONTH], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_own_timesheet'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'userDurationMonth';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(true);
|
||||
$this->setBegin('first day of this month 00:00:00');
|
||||
$this->setEnd('last day of this month 23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
39
src/Widget/Type/UserDurationToday.php
Normal file
39
src/Widget/Type/UserDurationToday.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserDurationToday extends AbstractCounterDuration
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_TODAY], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_own_timesheet'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'userDurationToday';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(true);
|
||||
$this->setBegin('00:00:00');
|
||||
$this->setEnd('23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
37
src/Widget/Type/UserDurationTotal.php
Normal file
37
src/Widget/Type/UserDurationTotal.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserDurationTotal extends AbstractCounterDuration
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_TOTAL], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_own_timesheet'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'userDurationTotal';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(true);
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
39
src/Widget/Type/UserDurationWeek.php
Normal file
39
src/Widget/Type/UserDurationWeek.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserDurationWeek extends AbstractCounterDuration
|
||||
{
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge(['color' => WidgetInterface::COLOR_WEEK], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return ['view_own_timesheet'];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'userDurationWeek';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setQueryWithUser(true);
|
||||
$this->setBegin('monday this week 00:00:00');
|
||||
$this->setEnd('sunday this week 23:59:59');
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
}
|
||||
@@ -9,27 +9,39 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
final class UserDurationYear extends CounterYear
|
||||
final class UserDurationYear extends AbstractCounterYear
|
||||
{
|
||||
public function __construct(TimesheetRepository $repository, SystemConfiguration $systemConfiguration)
|
||||
public function getId(): string
|
||||
{
|
||||
parent::__construct($repository, $systemConfiguration);
|
||||
$this->setId('userDurationYear');
|
||||
$this->setOption('dataType', 'duration');
|
||||
$this->setOption('icon', 'duration');
|
||||
$this->setOption('color', 'yellow');
|
||||
return 'userDurationYear';
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-counter-duration.html.twig';
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'icon' => 'duration',
|
||||
'color' => WidgetInterface::COLOR_YEAR,
|
||||
], parent::getOptions($options));
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$this->setTitle('stats.durationYear');
|
||||
$this->titleYear = 'stats.durationFinancialYear';
|
||||
$this->setQuery(TimesheetRepository::STATS_QUERY_DURATION);
|
||||
$this->setQueryWithUser(true);
|
||||
|
||||
return parent::getData($options);
|
||||
}
|
||||
|
||||
protected function getFinancialYearTitle(): string
|
||||
{
|
||||
return 'stats.durationFinancialYear';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,42 +11,57 @@ namespace App\Widget\Type;
|
||||
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Team;
|
||||
use App\Entity\User;
|
||||
use App\Project\ProjectStatisticService;
|
||||
use App\Repository\Loader\ProjectLoader;
|
||||
use App\Repository\Loader\TeamLoader;
|
||||
use App\Widget\WidgetInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class UserTeamProjects extends SimpleWidget implements AuthorizedWidget, UserWidget
|
||||
final class UserTeamProjects extends AbstractWidget
|
||||
{
|
||||
private $statisticService;
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(ProjectStatisticService $statisticService, EntityManagerInterface $entityManager)
|
||||
public function __construct(private ProjectStatisticService $statisticService, private EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->setId('UserTeamProjects');
|
||||
$this->setTitle('label.my_team_projects');
|
||||
$this->setOption('id', '');
|
||||
$this->statisticService = $statisticService;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
public function getWidth(): int
|
||||
{
|
||||
$options = parent::getOptions($options);
|
||||
|
||||
if (empty($options['id'])) {
|
||||
$options['id'] = 'WidgetUserTeamProjects';
|
||||
}
|
||||
|
||||
return $options;
|
||||
return WidgetInterface::WIDTH_HALF;
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getHeight(): int
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
/** @var User $user */
|
||||
$user = $options['user'];
|
||||
return WidgetInterface::HEIGHT_LARGE;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'my_team_projects';
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-userteamprojects.html.twig';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return [
|
||||
'budget_team_project', 'budget_teamlead_project', 'budget_project',
|
||||
'time_team_project', 'time_teamlead_project', 'time_project',
|
||||
];
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'UserTeamProjects';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$now = new \DateTime('now', new \DateTimeZone($user->getTimezone()));
|
||||
|
||||
$loader = new TeamLoader($this->entityManager);
|
||||
@@ -65,7 +80,7 @@ class UserTeamProjects extends SimpleWidget implements AuthorizedWidget, UserWid
|
||||
}
|
||||
}
|
||||
|
||||
$loader = new ProjectLoader($this->entityManager);
|
||||
$loader = new ProjectLoader($this->entityManager, false, false, false);
|
||||
$loader->loadResults($teamProjects);
|
||||
|
||||
foreach ($teamProjects as $id => $project) {
|
||||
@@ -77,20 +92,4 @@ class UserTeamProjects extends SimpleWidget implements AuthorizedWidget, UserWid
|
||||
|
||||
return $this->statisticService->getBudgetStatisticModelForProjects($projects, $now);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return [
|
||||
'budget_team_project', 'budget_teamlead_project', 'budget_project',
|
||||
'time_team_project', 'time_teamlead_project', 'time_project',
|
||||
];
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->setOption('user', $user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,35 +9,34 @@
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\Loader\UserLoader;
|
||||
use App\Widget\WidgetInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class UserTeams extends SimpleWidget implements AuthorizedWidget, UserWidget
|
||||
final class UserTeams extends AbstractWidget
|
||||
{
|
||||
public function __construct()
|
||||
public function __construct(private EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->setId('UserTeams');
|
||||
$this->setTitle('label.my_teams');
|
||||
$this->setOption('id', '');
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
public function getWidth(): int
|
||||
{
|
||||
$options = parent::getOptions($options);
|
||||
|
||||
if (empty($options['id'])) {
|
||||
$options['id'] = 'WidgetUserTeams';
|
||||
}
|
||||
|
||||
return $options;
|
||||
return WidgetInterface::WIDTH_HALF;
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
public function getHeight(): int
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
/** @var User $user */
|
||||
$user = $options['user'];
|
||||
return WidgetInterface::HEIGHT_LARGE;
|
||||
}
|
||||
|
||||
return $user->getTeams();
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'my_teams';
|
||||
}
|
||||
|
||||
public function getTemplateName(): string
|
||||
{
|
||||
return 'widget/widget-userteams.html.twig';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,8 +47,19 @@ class UserTeams extends SimpleWidget implements AuthorizedWidget, UserWidget
|
||||
return ['view_team_member', 'view_team'];
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
public function getId(): string
|
||||
{
|
||||
$this->setOption('user', $user);
|
||||
return 'UserTeams';
|
||||
}
|
||||
|
||||
public function getData(array $options = []): mixed
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
// without this, every user would be lazy loaded
|
||||
$loader = new UserLoader($this->entityManager, true);
|
||||
$loader->loadResults([$user->getId()]);
|
||||
|
||||
return $user->getTeams();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +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\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
interface UserWidget
|
||||
{
|
||||
/**
|
||||
* Sets the current user.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void;
|
||||
}
|
||||
@@ -1,24 +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\Widget\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* Needs to be used on a SimpleWidget
|
||||
* @internal
|
||||
*/
|
||||
trait UserWidgetTrait
|
||||
{
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->setOption('user', $user);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +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\Widget\Type;
|
||||
|
||||
final class YearChart extends SimpleStatisticChart
|
||||
{
|
||||
}
|
||||
@@ -1,24 +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\Widget;
|
||||
|
||||
interface WidgetContainerInterface extends WidgetInterface
|
||||
{
|
||||
public function getOrder(): int;
|
||||
|
||||
public function setOrder(int $order);
|
||||
|
||||
/**
|
||||
* @return WidgetInterface[]
|
||||
*/
|
||||
public function getWidgets(): array;
|
||||
|
||||
public function addWidget(WidgetInterface $widget);
|
||||
}
|
||||
@@ -9,6 +9,6 @@
|
||||
|
||||
namespace App\Widget;
|
||||
|
||||
class WidgetException extends \Exception
|
||||
final class WidgetException extends \Exception
|
||||
{
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
namespace App\Widget;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\Form\Form;
|
||||
|
||||
interface WidgetInterface
|
||||
{
|
||||
public const COLOR_TODAY = 'green';
|
||||
@@ -17,6 +20,16 @@ interface WidgetInterface
|
||||
public const COLOR_YEAR = 'yellow';
|
||||
public const COLOR_TOTAL = 'red';
|
||||
|
||||
public const WIDTH_FULL = 4;
|
||||
public const WIDTH_LARGE = 3;
|
||||
public const WIDTH_HALF = 2;
|
||||
public const WIDTH_SMALL = 1;
|
||||
|
||||
public const HEIGHT_MAXIMUM = 6;
|
||||
public const HEIGHT_LARGE = 5;
|
||||
public const HEIGHT_MEDIUM = 3;
|
||||
public const HEIGHT_SMALL = 1;
|
||||
|
||||
/**
|
||||
* Returns a unique ID for this widget.
|
||||
*
|
||||
@@ -25,16 +38,36 @@ interface WidgetInterface
|
||||
public function getId(): string;
|
||||
|
||||
/**
|
||||
* Returns the widget title.
|
||||
*
|
||||
* If no title is necessary, return an empty string.
|
||||
* Returns the widget title (must be non-empty).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string;
|
||||
|
||||
/**
|
||||
* Returns the widgets data, to be used in the frontend rendering.
|
||||
* Returns the height for this widget.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHeight(): int;
|
||||
|
||||
/**
|
||||
* Returns the width for this widget.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWidth(): int;
|
||||
|
||||
/**
|
||||
* Injects the current user.
|
||||
*
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function setUser(User $user): void;
|
||||
|
||||
/**
|
||||
* Returns the widget data, to be used in the frontend rendering.
|
||||
*
|
||||
* If your widget relies on options to dynamically change the result data,
|
||||
* make sure that the given $options will overwrite the internal option for
|
||||
@@ -65,7 +98,43 @@ interface WidgetInterface
|
||||
* The given option should be persisted and permanently overwrite the internal option.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @param string|bool|int $value
|
||||
*/
|
||||
public function setOption(string $name, $value): void;
|
||||
public function setOption(string $name, string|bool|int $value): void;
|
||||
|
||||
/**
|
||||
* Return a list of granted syntax string.
|
||||
* If ANY of the given permission strings matches, access is granted.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPermissions(): array;
|
||||
|
||||
/**
|
||||
* Returns the template, which is used to render the widget.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplateName(): string;
|
||||
|
||||
/**
|
||||
* Whether this widget can be configured with options.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasForm(): bool;
|
||||
|
||||
/**
|
||||
* A form to edit the widget options or null, if it can't be configured.
|
||||
*
|
||||
* @return Form|null
|
||||
*/
|
||||
public function getForm(): ?Form;
|
||||
|
||||
/**
|
||||
* Whether this is a widget that is supposed to be selectable by the end-user.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInternal(): bool;
|
||||
}
|
||||
|
||||
@@ -1,35 +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\Widget;
|
||||
|
||||
interface WidgetRendererInterface
|
||||
{
|
||||
/**
|
||||
* Checks if the given widget can be rendered.
|
||||
*
|
||||
* Most renderer check something like:
|
||||
* return $widget instanceof MyWidgetType;
|
||||
*
|
||||
* @param WidgetInterface $widget
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(WidgetInterface $widget): bool;
|
||||
|
||||
/**
|
||||
* Renders the given widget.
|
||||
*
|
||||
* The given $options array overwrites the widgets internal options for this call.
|
||||
*
|
||||
* @param WidgetInterface $widget
|
||||
* @param array<string, mixed> $options
|
||||
* @return string
|
||||
*/
|
||||
public function render(WidgetInterface $widget, array $options = []): string;
|
||||
}
|
||||
@@ -9,70 +9,41 @@
|
||||
|
||||
namespace App\Widget;
|
||||
|
||||
use App\Repository\WidgetRepository;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class WidgetService
|
||||
{
|
||||
/**
|
||||
* @var WidgetRendererInterface[]
|
||||
*/
|
||||
private $renderer;
|
||||
/**
|
||||
* @var WidgetRepository
|
||||
*/
|
||||
private $repository;
|
||||
private array $widgets = [];
|
||||
|
||||
/**
|
||||
* @param WidgetRepository $repository
|
||||
* @param WidgetRendererInterface[] $renderer
|
||||
*/
|
||||
public function __construct(WidgetRepository $repository, iterable $renderer)
|
||||
public function hasWidget(string $id): bool
|
||||
{
|
||||
$this->renderer = $renderer;
|
||||
$this->repository = $repository;
|
||||
return \array_key_exists($id, $this->widgets);
|
||||
}
|
||||
|
||||
public function hasWidget(string $widget): bool
|
||||
public function registerWidget(WidgetInterface $widget): void
|
||||
{
|
||||
return $this->repository->has($widget);
|
||||
}
|
||||
|
||||
public function getWidget(string $widget): WidgetInterface
|
||||
{
|
||||
return $this->repository->get($widget);
|
||||
}
|
||||
|
||||
public function addRenderer(WidgetRendererInterface $renderer): WidgetService
|
||||
{
|
||||
$this->renderer[] = $renderer;
|
||||
|
||||
return $this;
|
||||
$id = trim($widget->getId());
|
||||
if ($id === '') {
|
||||
throw new \InvalidArgumentException('Widget needs a non-empty ID');
|
||||
}
|
||||
$this->widgets[$id] = $widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WidgetInterface $widget
|
||||
* @return WidgetRendererInterface
|
||||
* @throws WidgetException
|
||||
* @return array<string, WidgetInterface>
|
||||
*/
|
||||
public function findRenderer(WidgetInterface $widget): WidgetRendererInterface
|
||||
public function getAllWidgets(): array
|
||||
{
|
||||
foreach ($this->renderer as $renderer) {
|
||||
if ($renderer->supports($widget)) {
|
||||
return $renderer;
|
||||
}
|
||||
return $this->widgets;
|
||||
}
|
||||
|
||||
public function getWidget(string $id): WidgetInterface
|
||||
{
|
||||
if (!$this->hasWidget($id)) {
|
||||
throw new \InvalidArgumentException(sprintf('Cannot find widget: %s', $id));
|
||||
}
|
||||
|
||||
throw new WidgetException(sprintf('No renderer available for widget "%s"', \get_class($widget)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WidgetRendererInterface[]
|
||||
*/
|
||||
public function getRenderer(): iterable
|
||||
{
|
||||
return $this->renderer;
|
||||
return $this->widgets[$id];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user