Release 2.0.13 (#3955)

- added missing escape to prevent HTML injection
- added missing color attribute
- upgrade theme
  - use dropdown submenu if title is set, otherwise dropdown tends to get too long
  - allow to use card-table instead of card-body
  - added `required` attribute to username and password field
- fix pagination back to page 1
- prevent tag name too long
- re-add missing user preferences link
This commit is contained in:
Kevin Papst
2023-03-30 01:08:15 +02:00
committed by GitHub
parent 7164ec126f
commit 01226a1243
23 changed files with 102 additions and 153 deletions

View File

@@ -115,18 +115,24 @@ export default class KimaiFormSelect extends KimaiFormPlugin {
if (node.dataset['renderer'] !== undefined && node.dataset['renderer'] === 'color') {
options.render = {...render, ...{
option: function(data, escape) {
let color = data.value;
let item = '<div class="list-group-item border-0 p-1 ps-2 text-nowrap">';
if (data.color !== undefined) {
color = data.color;
item += '<span style="background-color:' + data.color + '" class="color-choice-item">&nbsp;</span>';
} else {
item += '<span class="color-choice-item">&nbsp;</span>';
}
return '<div class="list-group-item border-0 p-1 ps-2 text-nowrap"><span style="background-color:' + color + '" class="color-choice-item">&nbsp;</span>' + escape(data.text) + '</div>';
item += escape(data.text) + '</div>';
return item;
},
item: function(data, escape) {
let color = data.value;
let item = '<div class="text-nowrap">';
if (data.color !== undefined) {
color = data.color;
item += '<span style="background-color:' + data.color + '" class="color-choice-item">&nbsp;</span>';
} else {
item += '<span class="color-choice-item">&nbsp;</span>';
}
return '<div class="text-nowrap"><span style="background-color:' + color + '" class="color-choice-item">&nbsp;</span>' + escape(data.text) + '</div>';
item += escape(data.text) + '</div>';
return item;
}
}};
} else {

View File

@@ -65,6 +65,8 @@ export default class KimaiTeamForm extends KimaiFormPlugin {
*/
_createMember(option)
{
/** @type {KimaiEscape} ESCAPER */
const ESCAPER = this.getPlugin('escape');
const prototype = this._getPrototype();
let counter = prototype.dataset['widgetCounter'] || prototype.childNodes.length;
let newWidget = prototype.dataset['prototype'];
@@ -72,11 +74,11 @@ export default class KimaiTeamForm extends KimaiFormPlugin {
newWidget = newWidget.replace(/__name__/g, counter);
newWidget = newWidget.replace(/#000000/g, KimaiColor.calculateContrastColor(option.dataset.color));
newWidget = newWidget.replace(/__DISPLAY__/g, option.dataset.display);
newWidget = newWidget.replace(/__DISPLAY__/g, ESCAPER.escapeForHtml(option.dataset.display));
newWidget = newWidget.replace(/__COLOR__/g, option.dataset.color);
newWidget = newWidget.replace(/__INITIALS__/g, option.dataset.initials);
newWidget = newWidget.replace(/__TITLE__/g, option.dataset.title);
newWidget = newWidget.replace(/__USERNAME__/g, option.text);
newWidget = newWidget.replace(/__INITIALS__/g, ESCAPER.escapeForHtml(option.dataset.initials));
newWidget = newWidget.replace(/__TITLE__/g, ESCAPER.escapeForHtml(option.dataset.title));
newWidget = newWidget.replace(/__USERNAME__/g, ESCAPER.escapeForHtml(option.text));
prototype.dataset['widgetCounter'] = (++counter).toString();

View File

@@ -163,7 +163,12 @@ export default class KimaiToolbar extends KimaiPlugin {
event.preventDefault();
event.stopPropagation();
let urlParts = target.href.split('/');
pager.value = urlParts[urlParts.length-1];
let pageNumber = urlParts[urlParts.length - 1];
// page number usually is the default value and is therefor missing from the URL
if (!/\d/.test(pageNumber)) {
pageNumber = 1;
}
pager.value = pageNumber;
pager.dispatchEvent(new Event('change'));
document.dispatchEvent(new Event('pagination-change'));
return false;

View File

@@ -34,7 +34,7 @@
"friendsofsymfony/rest-bundle": "^3.0",
"gedmo/doctrine-extensions": "^3.6",
"jms/serializer-bundle": "^5.0",
"kevinpapst/tabler-bundle": "dev-main",
"kevinpapst/tabler-bundle": "^0.14",
"league/csv": "^9.4",
"mpdf/mpdf": "^8.0",
"nelmio/api-doc-bundle": "^4.0",

70
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "dfc85100677cacc71615a6442646ff39",
"content-hash": "1bdbdd73cd12f5111c5e34db51d7ad10",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -816,21 +816,21 @@
},
{
"name": "doctrine/doctrine-bundle",
"version": "2.8.3",
"version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/DoctrineBundle.git",
"reference": "fd67ba64db3c806f626a33dcab15a4db0c77652e"
"reference": "9819c00c2eea750b99902f244309b824911b72b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/fd67ba64db3c806f626a33dcab15a4db0c77652e",
"reference": "fd67ba64db3c806f626a33dcab15a4db0c77652e",
"url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/9819c00c2eea750b99902f244309b824911b72b2",
"reference": "9819c00c2eea750b99902f244309b824911b72b2",
"shasum": ""
},
"require": {
"doctrine/cache": "^1.11 || ^2.0",
"doctrine/dbal": "^3.4.0",
"doctrine/dbal": "^3.6.0",
"doctrine/persistence": "^2.2 || ^3",
"doctrine/sql-formatter": "^1.0.1",
"php": "^7.4 || ^8.0",
@@ -846,11 +846,12 @@
"conflict": {
"doctrine/annotations": ">=3.0",
"doctrine/orm": "<2.11 || >=3.0",
"twig/twig": "<1.34 || >=2.0,<2.4"
"twig/twig": "<1.34 || >=2.0 <2.4"
},
"require-dev": {
"doctrine/annotations": "^1 || ^2",
"doctrine/coding-standard": "^9.0",
"doctrine/deprecations": "^1.0",
"doctrine/orm": "^2.11 || ^3.0",
"friendsofphp/proxy-manager-lts": "^1.0",
"phpunit/phpunit": "^9.5.26 || ^10.0",
@@ -911,7 +912,7 @@
],
"support": {
"issues": "https://github.com/doctrine/DoctrineBundle/issues",
"source": "https://github.com/doctrine/DoctrineBundle/tree/2.8.3"
"source": "https://github.com/doctrine/DoctrineBundle/tree/2.9.0"
},
"funding": [
{
@@ -927,7 +928,7 @@
"type": "tidelift"
}
],
"time": "2023-02-03T09:32:42+00:00"
"time": "2023-03-23T20:02:57+00:00"
},
{
"name": "doctrine/doctrine-migrations-bundle",
@@ -2415,16 +2416,16 @@
},
{
"name": "kevinpapst/tabler-bundle",
"version": "dev-main",
"version": "0.14",
"source": {
"type": "git",
"url": "https://github.com/kevinpapst/TablerBundle.git",
"reference": "f0a8cdae6b4a243ee9acce60e2ffbb5f0c107c03"
"reference": "49db98ed5923cdd45aeb4316f08c914979722731"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/f0a8cdae6b4a243ee9acce60e2ffbb5f0c107c03",
"reference": "f0a8cdae6b4a243ee9acce60e2ffbb5f0c107c03",
"url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/49db98ed5923cdd45aeb4316f08c914979722731",
"reference": "49db98ed5923cdd45aeb4316f08c914979722731",
"shasum": ""
},
"require": {
@@ -2454,7 +2455,6 @@
"suggest": {
"knplabs/knp-menu-bundle": "Allows easy menu integration"
},
"default-branch": true,
"type": "symfony-bundle",
"autoload": {
"psr-4": {
@@ -2474,7 +2474,7 @@
"description": "Admin/Backend theme bundle for Symfony based on Tabler.io",
"support": {
"issues": "https://github.com/kevinpapst/TablerBundle/issues",
"source": "https://github.com/kevinpapst/TablerBundle/tree/main"
"source": "https://github.com/kevinpapst/TablerBundle/tree/0.14"
},
"funding": [
{
@@ -2486,7 +2486,7 @@
"type": "github"
}
],
"time": "2023-03-23T14:18:33+00:00"
"time": "2023-03-29T10:36:05+00:00"
},
{
"name": "laminas/laminas-escaper",
@@ -11165,16 +11165,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.7",
"version": "1.10.8",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "b10ceb526d9607903c5b2673f1fc8775dbe48975"
"reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b10ceb526d9607903c5b2673f1fc8775dbe48975",
"reference": "b10ceb526d9607903c5b2673f1fc8775dbe48975",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0166aef76e066f0dd2adc2799bdadfa1635711e9",
"reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9",
"shasum": ""
},
"require": {
@@ -11223,7 +11223,7 @@
"type": "tidelift"
}
],
"time": "2023-03-16T15:24:20+00:00"
"time": "2023-03-24T10:28:16+00:00"
},
{
"name": "phpstan/phpstan-doctrine",
@@ -11297,16 +11297,16 @@
},
{
"name": "phpstan/phpstan-phpunit",
"version": "1.3.10",
"version": "1.3.11",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "4cc5c6cc38e56bce7ea47c4091814e516d172dc3"
"reference": "9e1b9de6d260461f6e99b6a8f2dbb0bbb98b579c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/4cc5c6cc38e56bce7ea47c4091814e516d172dc3",
"reference": "4cc5c6cc38e56bce7ea47c4091814e516d172dc3",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/9e1b9de6d260461f6e99b6a8f2dbb0bbb98b579c",
"reference": "9e1b9de6d260461f6e99b6a8f2dbb0bbb98b579c",
"shasum": ""
},
"require": {
@@ -11343,22 +11343,22 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.10"
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.11"
},
"time": "2023-03-02T10:25:13+00:00"
"time": "2023-03-25T19:42:13+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "1.5.0",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "b7dd96a5503919a43b3cd06a2dced9d4252492f2"
"reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b7dd96a5503919a43b3cd06a2dced9d4252492f2",
"reference": "b7dd96a5503919a43b3cd06a2dced9d4252492f2",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b21c03d4f6f3a446e4311155f4be9d65048218e6",
"reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6",
"shasum": ""
},
"require": {
@@ -11392,9 +11392,9 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.0"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.1"
},
"time": "2023-02-21T10:17:10+00:00"
"time": "2023-03-29T14:47:40+00:00"
},
{
"name": "phpstan/phpstan-symfony",
@@ -13345,9 +13345,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"kevinpapst/tabler-bundle": 20
},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

View File

@@ -1310,41 +1310,11 @@ parameters:
count: 1
path: src/Controller/Reporting/ProjectViewController.php
-
message: "#^Cannot call method modify\\(\\) on DateTime\\|null\\.$#"
count: 4
path: src/Controller/Reporting/ReportUsersMonthController.php
-
message: "#^Cannot clone non\\-object variable \\$start of type DateTime\\|null\\.$#"
count: 3
path: src/Controller/Reporting/ReportUsersMonthController.php
-
message: "#^Method App\\\\Controller\\\\Reporting\\\\ReportUsersMonthController\\:\\:getData\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Controller/Reporting/ReportUsersMonthController.php
-
message: "#^Parameter \\#1 \\$begin of class App\\\\Model\\\\DailyStatistic constructor expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/ReportUsersMonthController.php
-
message: "#^Parameter \\#1 \\$begin of method App\\\\Timesheet\\\\TimesheetStatisticService\\:\\:getDailyStatistics\\(\\) expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/ReportUsersMonthController.php
-
message: "#^Parameter \\#2 \\$end of class App\\\\Model\\\\DailyStatistic constructor expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/ReportUsersMonthController.php
-
message: "#^Parameter \\#2 \\$end of method App\\\\Timesheet\\\\TimesheetStatisticService\\:\\:getDailyStatistics\\(\\) expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/ReportUsersMonthController.php
-
message: "#^Parameter \\#3 \\$users of method App\\\\Timesheet\\\\TimesheetStatisticService\\:\\:getDailyStatistics\\(\\) expects array\\<App\\\\Entity\\\\User\\>, iterable\\<App\\\\Entity\\\\User\\> given\\.$#"
count: 1
@@ -1385,51 +1355,11 @@ parameters:
count: 1
path: src/Controller/Reporting/ReportUsersYearController.php
-
message: "#^Cannot call method modify\\(\\) on DateTime\\|null\\.$#"
count: 4
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Cannot clone non\\-object variable \\$start of type DateTime\\|null\\.$#"
count: 3
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Method App\\\\Controller\\\\Reporting\\\\UserMonthController\\:\\:getData\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Parameter \\#1 \\$begin of class App\\\\Model\\\\DailyStatistic constructor expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Parameter \\#1 \\$begin of method App\\\\Controller\\\\Reporting\\\\AbstractUserReportController\\:\\:prepareReport\\(\\) expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Parameter \\#2 \\$end of class App\\\\Model\\\\DailyStatistic constructor expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Parameter \\#2 \\$end of method App\\\\Controller\\\\Reporting\\\\AbstractUserReportController\\:\\:prepareReport\\(\\) expects DateTime, DateTime\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Parameter \\#3 \\$user of class App\\\\Model\\\\DailyStatistic constructor expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Parameter \\#3 \\$user of method App\\\\Controller\\\\Reporting\\\\AbstractUserReportController\\:\\:prepareReport\\(\\) expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#"
count: 1
path: src/Controller/Reporting/UserMonthController.php
-
message: "#^Method App\\\\Controller\\\\Reporting\\\\UserWeekController\\:\\:getData\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
"app": {
"js": [
"/build/runtime.f0079159.js",
"/build/app.a6b63f34.js"
"/build/app.cd312ca8.js"
],
"css": [
"/build/app.b10b16e5.css"
@@ -63,7 +63,7 @@
},
"integrity": {
"/build/runtime.f0079159.js": "sha384-H22sAW1aTvyIPqvHOvGXWSWTxf0y6mptp+MsVmyXCfjx/WJjBbhX9gbUZ+qIuihV",
"/build/app.a6b63f34.js": "sha384-gYC58lsaQMGHSI811cimIwsqDcwhnKasGWsvFQODG0FrxVb2dDV9VvtFo9V5I8JL",
"/build/app.cd312ca8.js": "sha384-lS1VK+quldXkQU0DjsipiX8sXWvojX/NY0+kAUHFh69HlZZG36ubhkNbcV0crl35",
"/build/app.b10b16e5.css": "sha384-dwEgmHAQRm4PP7XTYkJfIoB+Iumd+0WuH5FyoC/+qC3WQ/e1gCTmlx9M2mIBxPuK",
"/build/export-pdf.587575e7.js": "sha384-J50GStmmfVwUTN4dIRQ02eg9hyzGFPSzpTtpPody92j0V6zCqw+s5l8+ZhVTugeW",
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",

View File

@@ -1,6 +1,6 @@
{
"build/app.css": "/build/app.b10b16e5.css",
"build/app.js": "/build/app.a6b63f34.js",
"build/app.js": "/build/app.cd312ca8.js",
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",
"build/export-pdf.js": "/build/export-pdf.587575e7.js",
"build/invoice.css": "/build/invoice.3c80ee80.css",

View File

@@ -17,11 +17,11 @@ class Constants
/**
* The current release version
*/
public const VERSION = '2.0.12';
public const VERSION = '2.0.13';
/**
* The current release: major * 10000 + minor * 100 + patch
*/
public const VERSION_ID = 20012;
public const VERSION_ID = 20013;
/**
* The software name
*/

View File

@@ -87,6 +87,7 @@ final class ReportUsersMonthController extends AbstractController
$values->setDate($dateTimeFactory->getStartOfMonth());
}
/** @var \DateTime $start */
$start = $values->getDate();
$start->modify('first day of 00:00:00');

View File

@@ -9,6 +9,7 @@
namespace App\Controller\Reporting;
use App\Entity\User;
use App\Model\DailyStatistic;
use App\Reporting\MonthByUser\MonthByUser;
use App\Reporting\MonthByUser\MonthByUserForm;
@@ -64,12 +65,14 @@ final class UserMonthController extends AbstractUserReportController
$values->setDate($dateTimeFactory->getStartOfMonth());
}
/** @var \DateTime $start */
$start = $values->getDate();
$start->modify('first day of 00:00:00');
$end = clone $start;
$end->modify('last day of 23:59:59');
/** @var User $selectedUser */
$selectedUser = $values->getUser();
$previousMonth = clone $start;

View File

@@ -55,12 +55,12 @@ final class TagFixtures extends Fixture
$tagName = $faker->text(rand(5, 10));
}
if (\in_array($tagName, $existing)) {
if (\in_array(mb_strtolower($tagName), $existing, true)) {
continue;
}
$existing[] = $tagName;
$tag->setName($tagName);
$existing[] = mb_strtolower($tagName);
$tag->setName(mb_substr($tagName, 0, 100));
$manager->persist($tag);

View File

@@ -10,7 +10,6 @@
namespace App\DataFixtures;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Tag;
use App\Entity\Timesheet;
@@ -58,12 +57,8 @@ final class TimesheetFixtures extends Fixture implements FixtureGroupInterface
public function load(ObjectManager $manager): void
{
$results = $this->findRandom($manager, Customer::class, 50);
$allUser = $this->getAllUsers($manager);
$faker = Factory::create();
$all = 0;
foreach ($allUser as $user) {

View File

@@ -47,6 +47,10 @@ final class UserSubscriber extends AbstractActionsSubscriber
$event->addActionToSubmenu('edit', $id, $action);
}
if ($this->isGranted('preferences', $user)) {
$event->addConfig($this->path('user_profile_preferences', ['username' => $user->getUserIdentifier()]));
}
if (($event->getUser()->getId() === $user->getId() && $this->isGranted('report:user')) || $this->isGranted('report:other')) {
$event->addActionToSubmenu('report', 'weekly', ['url' => $this->path('report_user_week', ['user' => $user->getId()]), 'translation_domain' => 'reporting', 'title' => 'report_user_week']);
$event->addActionToSubmenu('report', 'monthly', ['url' => $this->path('report_user_month', ['user' => $user->getId()]), 'translation_domain' => 'reporting', 'title' => 'report_user_month']);

View File

@@ -60,7 +60,7 @@ final class TagArrayToStringTransformer implements DataTransformerInterface
foreach ($newNames as $name) {
$tag = new Tag();
$tag->setName($name);
$tag->setName(mb_substr($name, 0, 100));
$tags[] = $tag;
// new tags persist automatically thanks to the cascade={"persist"}

View File

@@ -11,6 +11,7 @@ namespace App\Form\Type;
use App\Configuration\SystemConfiguration;
use App\Constants;
use App\Utils\Color;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
@@ -39,6 +40,13 @@ final class ColorChoiceType extends AbstractType implements DataTransformerInter
],
'label' => 'color',
'empty_data' => null,
'choice_attr' => function ($color, $name) {
if ($color === null) {
$color = (new Color())->getRandom($name);
}
return ['data-color' => $color];
},
];
$choices = [];

View File

@@ -80,7 +80,7 @@ final class TagsSelectType extends AbstractType
$newNames = array_diff($newNames, $foundTagNames);
foreach ($newNames as $name) {
$tag = new Tag();
$tag->setName($name);
$tag->setName(mb_substr($name, 0, 100));
$this->tagRepository->saveTag($tag);
$newData[] = $tag->getId();
}

View File

@@ -11,7 +11,6 @@ namespace App\Model;
use App\Entity\User;
use App\Model\Statistic\StatisticDate;
use DateTime;
use DateTimeInterface;
final class DailyStatistic implements DateStatisticInterface
@@ -19,16 +18,14 @@ final class DailyStatistic implements DateStatisticInterface
/**
* @var array<string, StatisticDate>
*/
private $days = [];
private $begin;
private $end;
private $user;
private array $days = [];
private DateTimeInterface $begin;
private DateTimeInterface $end;
public function __construct(DateTime $begin, DateTime $end, User $user)
public function __construct(DateTimeInterface $begin, DateTimeInterface $end, private User $user)
{
$this->begin = clone $begin;
$this->end = clone $end;
$this->user = $user;
}
public function getUser(): User
@@ -42,7 +39,7 @@ final class DailyStatistic implements DateStatisticInterface
return;
}
$tmp = clone $this->begin;
$tmp = \DateTime::createFromInterface($this->begin);
$tmp->setTime(0, 0, 0);
while ($tmp < $this->end) {
$id = $tmp->format('Y-m-d');

View File

@@ -116,7 +116,7 @@ final class LocaleFormatExtensions extends AbstractExtension implements LocaleAw
return $this->locale;
}
public function isWeekend(DateTime|string|null $dateTime): bool
public function isWeekend(\DateTimeInterface|string|null $dateTime): bool
{
if (!$dateTime instanceof \DateTime) {
return false;
@@ -141,7 +141,7 @@ final class LocaleFormatExtensions extends AbstractExtension implements LocaleAw
return ($day === 0 || $day === 6);
}
public function dateShort(DateTime|string|null $date): string
public function dateShort(\DateTimeInterface|string|null $date): string
{
return (string) $this->getFormatter()->dateShort($date);
}
@@ -158,17 +158,17 @@ final class LocaleFormatExtensions extends AbstractExtension implements LocaleAw
return new DateTime($date, new \DateTimeZone($timezone));
}
public function dateFormat(DateTime|string|null $date, string $format): string
public function dateFormat(\DateTimeInterface|string|null $date, string $format): string
{
return (string) $this->getFormatter()->dateFormat($date, $format);
}
public function dateWeekday(DateTime $date): string
public function dateWeekday(\DateTimeInterface $date): string
{
return $this->dayName($date, true) . ' ' . $this->getFormatter()->dateFormat($date, 'd');
}
public function time(DateTime|string|null $date): string
public function time(\DateTimeInterface|string|null $date): string
{
return (string) $this->getFormatter()->time($date);
}
@@ -192,12 +192,12 @@ final class LocaleFormatExtensions extends AbstractExtension implements LocaleAw
return $months;
}
public function monthName(\DateTime $dateTime, bool $withYear = false): string
public function monthName(\DateTimeInterface $dateTime, bool $withYear = false): string
{
return $this->getFormatter()->monthName($dateTime, $withYear);
}
public function dayName(\DateTime $dateTime, bool $short = false): string
public function dayName(\DateTimeInterface $dateTime, bool $short = false): string
{
return $this->getFormatter()->dayName($dateTime, $short);
}

View File

@@ -283,7 +283,7 @@ final class LocaleFormatter
/**
* @see https://unicode-org.github.io/icu/userguide/format_parse/datetime/
*/
private function formatIntl(\DateTime $dateTime, string $format): string
private function formatIntl(\DateTimeInterface $dateTime, string $format): string
{
$formatter = new IntlDateFormatter(
$this->locale,
@@ -303,12 +303,12 @@ final class LocaleFormatter
return (string) $formatted;
}
public function monthName(\DateTime $dateTime, bool $withYear = false): string
public function monthName(\DateTimeInterface $dateTime, bool $withYear = false): string
{
return $this->formatIntl($dateTime, ($withYear ? 'LLLL yyyy' : 'LLLL'));
}
public function dayName(\DateTime $dateTime, bool $short = false): string
public function dayName(\DateTimeInterface $dateTime, bool $short = false): string
{
return $this->formatIntl($dateTime, ($short ? 'EE' : 'EEEE'));
}