added configurable formats for duration (#287)
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -94,7 +94,7 @@ services:
|
||||
|
||||
App\Twig\Extensions:
|
||||
arguments:
|
||||
$locales: "%app_locales%"
|
||||
$languages: "%kimai.languages%"
|
||||
|
||||
App\Twig\DateExtensions:
|
||||
arguments:
|
||||
|
||||
@@ -156,6 +156,8 @@ class Configuration implements ConfigurationInterface
|
||||
->arrayPrototype()
|
||||
->children()
|
||||
->scalarNode('date_short')->end()
|
||||
->scalarNode('duration')->end()
|
||||
->scalarNode('duration_short')->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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)];
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user