From ebe007eb7812c6fa5927785f35ba8c514d96db61 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sat, 1 Sep 2018 17:54:17 +0200 Subject: [PATCH] added configurable formats for duration (#287) --- config/packages/kimai.yaml | 12 +++++++ config/services.yaml | 2 +- src/DependencyInjection/Configuration.php | 2 ++ src/Form/Type/DurationType.php | 2 +- src/Twig/Extensions.php | 32 ++++++++++++----- src/Utils/Duration.php | 16 +++++---- templates/dashboard/section-chart.html.twig | 2 +- templates/dashboard/section-simple.html.twig | 2 +- tests/Twig/ExtensionsTest.php | 38 ++++++++++++++------ tests/Utils/DurationTest.php | 2 +- 10 files changed, 78 insertions(+), 32 deletions(-) diff --git a/config/packages/kimai.yaml b/config/packages/kimai.yaml index ef735ce2..890fc323 100644 --- a/config/packages/kimai.yaml +++ b/config/packages/kimai.yaml @@ -45,16 +45,28 @@ kimai: languages: de: date_short: 'd.m.Y' + duration: '%%h:%%m Stunden' + duration_short: '%%h:%%m h' en: date_short: 'Y-m-d' + duration: '%%h:%%m hours' + duration_short: '%%h:%%m h' it: date_short: 'd.m.Y' + duration: '%%h:%%m h' + duration_short: '%%h:%%m h' fr: date_short: 'd/m/Y' + duration: '%%h:%%m h' + duration_short: '%%h:%%m h' es: date_short: 'd.m.Y' + duration: '%%h:%%m h' + duration_short: '%%h:%%m h' ru: date_short: 'd.m.Y' + duration: '%%h:%%m h' + duration_short: '%%h:%%m h' # timesheet calendar configuration calendar: diff --git a/config/services.yaml b/config/services.yaml index e8406c25..769a476f 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -94,7 +94,7 @@ services: App\Twig\Extensions: arguments: - $locales: "%app_locales%" + $languages: "%kimai.languages%" App\Twig\DateExtensions: arguments: diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 4320ee3f..9afe72a2 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -156,6 +156,8 @@ class Configuration implements ConfigurationInterface ->arrayPrototype() ->children() ->scalarNode('date_short')->end() + ->scalarNode('duration')->end() + ->scalarNode('duration_short')->end() ->end() ->end() ; diff --git a/src/Form/Type/DurationType.php b/src/Form/Type/DurationType.php index cf7a60b7..a6eb340b 100644 --- a/src/Form/Type/DurationType.php +++ b/src/Form/Type/DurationType.php @@ -67,7 +67,7 @@ class DurationType extends AbstractType $builder->addModelTransformer(new CallbackTransformer( function ($intToFormat) use ($formatter) { try { - return $formatter->format($intToFormat, true); + return $formatter->format($intToFormat); } catch (\Exception $e) { throw new TransformationFailedException($e->getMessage()); } diff --git a/src/Twig/Extensions.php b/src/Twig/Extensions.php index 551c0ddf..7ce835c1 100644 --- a/src/Twig/Extensions.php +++ b/src/Twig/Extensions.php @@ -85,14 +85,13 @@ class Extensions extends \Twig_Extension ]; /** - * Extensions constructor. - * @param string $locales - * @param string $locale + * @param RequestStack $requestStack + * @param array $languages */ - public function __construct(RequestStack $requestStack, $locales) + public function __construct(RequestStack $requestStack, array $languages) { $this->requestStack = $requestStack; - $this->locales = explode('|', $locales); + $this->locales = $languages; $this->durationFormatter = new Duration(); } @@ -157,10 +156,10 @@ class Extensions extends \Twig_Extension * Transforms seconds into a duration string. * * @param int|Timesheet $duration - * @param bool $includeSeconds + * @param string $format * @return string */ - public function duration($duration, $includeSeconds = false) + public function duration($duration, $format = null) { $seconds = $duration; if ($duration instanceof Timesheet) { @@ -170,7 +169,22 @@ class Extensions extends \Twig_Extension } } - return $this->durationFormatter->format($seconds, $includeSeconds) . ' h'; + $locale = $this->getLocale(); + switch ($format) { + case 'full': + $format = isset($this->locales[$locale]) ? $this->locales[$locale]['duration'] : null; + break; + case null: + case 'short': + $format = isset($this->locales[$locale]) ? $this->locales[$locale]['duration_short'] : null; + break; + } + + if (null === $format) { + $format = '%h:%m h'; + } + + return $this->durationFormatter->format($seconds, $format); } /** @@ -244,7 +258,7 @@ class Extensions extends \Twig_Extension public function getLocales() { $locales = []; - foreach ($this->locales as $locale) { + foreach (array_keys($this->locales) as $locale) { $locales[] = ['code' => $locale, 'name' => Intl::getLocaleBundle()->getLocaleName($locale, $locale)]; } diff --git a/src/Utils/Duration.php b/src/Utils/Duration.php index 23d25eb7..19866aa1 100644 --- a/src/Utils/Duration.php +++ b/src/Utils/Duration.php @@ -18,14 +18,17 @@ class Duration public const FORMAT_NATURAL = 'natural'; public const FORMAT_SECONDS = 'seconds'; + public const FORMAT_WITH_SECONDS = '%h:%m:%s'; + public const FORMAT_NO_SECONDS = '%h:%m'; + /** * Transforms seconds into a duration string. * * @param $seconds - * @param bool $includeSeconds + * @param string $format * @return string */ - public function format($seconds, $includeSeconds = false) + public function format($seconds, $format = self::FORMAT_NO_SECONDS) { $hour = floor($seconds / 3600); $minute = floor(($seconds / 60) % 60); @@ -33,14 +36,13 @@ class Duration $hour = $hour > 9 ? $hour : '0' . $hour; $minute = $minute > 9 ? $minute : '0' . $minute; - if (!$includeSeconds) { - return $hour . ':' . $minute; - } - $second = $seconds % 60; $second = $second > 9 ? $second : '0' . $second; - return $hour . ':' . $minute . ':' . $second; + $formatted = str_replace('%h', $hour, $format); + $formatted = str_replace('%m', $minute, $formatted); + + return str_replace('%s', $second, $formatted); } /** diff --git a/templates/dashboard/section-chart.html.twig b/templates/dashboard/section-chart.html.twig index f003dbc9..d7ece03b 100644 --- a/templates/dashboard/section-chart.html.twig +++ b/templates/dashboard/section-chart.html.twig @@ -94,7 +94,7 @@ {% for widget in section.widgets|slice(1, width) %} {% set data = widget.data %} {% if widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_DURATION') %} - {% set data = widget.data|duration %} + {% set data = widget.data|duration('full') %} {% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %} {% set data = widget.data|money %} {% endif %} diff --git a/templates/dashboard/section-simple.html.twig b/templates/dashboard/section-simple.html.twig index ff4afef7..f50607b9 100644 --- a/templates/dashboard/section-simple.html.twig +++ b/templates/dashboard/section-simple.html.twig @@ -17,7 +17,7 @@ {% set data = widget.data %} {% if widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_DURATION') %} - {% set data = widget.data|duration %} + {% set data = widget.data|duration('full') %} {% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %} {% set data = widget.data|money %} {% endif %} diff --git a/tests/Twig/ExtensionsTest.php b/tests/Twig/ExtensionsTest.php index 0eef9ef6..8076f468 100644 --- a/tests/Twig/ExtensionsTest.php +++ b/tests/Twig/ExtensionsTest.php @@ -21,8 +21,13 @@ use Twig\TwigFilter; */ class ExtensionsTest extends TestCase { + private $localeEn = ['en' => ['date_short' => 'Y-m-d', 'duration' => '%h:%m:%s h', 'duration_short' => '%h:%m h']]; + private $localeDe = ['de' => ['date_short' => 'd.m.Y', 'duration' => '%h:%m:%s Stunden', 'duration_short' => '%h:%m h']]; + private $localeRu = ['ru' => ['date_short' => 'd.m.Y', 'duration' => '%h:%m:%s h', 'duration_short' => '%h:%m h']]; + private $localeFake = ['XX' => ['date_short' => 'd.m.Y', 'duration' => '%h Stunden, %m Minuten und %s Sekunden', 'duration_short' => '%h - %m - %s Zeit']]; + /** - * @param string $locales + * @param array $locales * @param string $locale * @return Extensions */ @@ -39,7 +44,7 @@ class ExtensionsTest extends TestCase public function testGetFilters() { $filters = ['duration', 'money', 'currency', 'country', 'icon']; - $sut = $this->getSut('de'); + $sut = $this->getSut($this->localeDe); $twigFilters = $sut->getFilters(); $this->assertCount(count($filters), $twigFilters); $i = 0; @@ -52,7 +57,7 @@ class ExtensionsTest extends TestCase public function testGetFunctions() { $functions = ['locales', 'is_visible_column']; - $sut = $this->getSut('de'); + $sut = $this->getSut($this->localeDe); $twigFunctions = $sut->getFunctions(); $this->assertCount(count($functions), $twigFunctions); $i = 0; @@ -70,7 +75,8 @@ class ExtensionsTest extends TestCase ['code' => 'ru', 'name' => 'русский'], ]; - $sut = $this->getSut('en|de|ru'); + $appLocales = array_merge($this->localeEn, $this->localeDe, $this->localeRu); + $sut = $this->getSut($appLocales); $this->assertEquals($locales, $sut->getLocales()); } @@ -82,7 +88,7 @@ class ExtensionsTest extends TestCase 'RUB' => 'RUB', ]; - $sut = $this->getSut('en'); + $sut = $this->getSut($this->localeEn); foreach ($symbols as $name => $symbol) { $this->assertEquals($symbol, $sut->currency($name)); } @@ -96,7 +102,7 @@ class ExtensionsTest extends TestCase 'ES' => 'Spain', ]; - $sut = $this->getSut('en'); + $sut = $this->getSut($this->localeEn); foreach ($countries as $locale => $name) { $this->assertEquals($name, $sut->country($locale)); } @@ -111,7 +117,7 @@ class ExtensionsTest extends TestCase */ public function testMoney($result, $amount, $currency, $locale) { - $sut = $this->getSut('en', $locale); + $sut = $this->getSut($this->localeEn, $locale); $this->assertEquals($result, $sut->money($amount, $currency)); } @@ -137,12 +143,22 @@ class ExtensionsTest extends TestCase { $record = $this->getTimesheet(9437); - $sut = $this->getSut('en'); + $sut = $this->getSut($this->localeEn); $this->assertEquals('02:37 h', $sut->duration($record->getDuration())); - $this->assertEquals('02:37:17 h', $sut->duration($record->getDuration(), true)); + $this->assertEquals('02:37:17 h', $sut->duration($record->getDuration(), '%h:%m:%s h')); + // test Timesheet object $this->assertEquals('02:37 h', $sut->duration($record)); - $this->assertEquals('02:37:17 h', $sut->duration($record, true)); + $this->assertEquals('02:37:17', $sut->duration($record, '%h:%m:%s')); + + // test extended format + $sut = $this->getSut($this->localeFake, 'XX'); + $this->assertEquals('02 - 37 - 17 Zeit', $sut->duration($record->getDuration(), 'short')); + $this->assertEquals('02 Stunden, 37 Minuten und 17 Sekunden', $sut->duration($record->getDuration(), 'full')); + + // test fallback format + $sut = $this->getSut($this->localeEn, 'XX'); + $this->assertEquals('02:37 h', $sut->duration($record->getDuration())); } protected function getTimesheet($seconds) @@ -167,7 +183,7 @@ class ExtensionsTest extends TestCase ]; // test pre-defined icons - $sut = $this->getSut('en'); + $sut = $this->getSut($this->localeEn); foreach ($icons as $icon) { $result = $sut->icon($icon); $this->assertNotEmpty($result, 'Problem with icon definition: ' . $icon); diff --git a/tests/Utils/DurationTest.php b/tests/Utils/DurationTest.php index adb22be8..3750a0f7 100644 --- a/tests/Utils/DurationTest.php +++ b/tests/Utils/DurationTest.php @@ -21,7 +21,7 @@ class DurationTest extends TestCase { $sut = new Duration(); $this->assertEquals('02:38', $sut->format(9494)); - $this->assertEquals('02:38:14', $sut->format(9494, true)); + $this->assertEquals('02:38:14', $sut->format(9494, Duration::FORMAT_WITH_SECONDS)); } /**