added daterange-picker and localized date-inputs (#540)
This commit is contained in:
@@ -51,37 +51,88 @@ $(function() {
|
||||
);
|
||||
}
|
||||
|
||||
$('input[data-datepicker="on"]').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false,
|
||||
locale: {
|
||||
format: "YYYY-MM-DD",
|
||||
firstDay: 1
|
||||
}
|
||||
// ==== compound field in toolbar ====
|
||||
$('input[data-daterangepickerenable="on"]').each(function(index) {
|
||||
var localeFormat = $(this).data('format');
|
||||
var separator = $(this).data('separator');
|
||||
var transToday = $.kimai.settings['today'];
|
||||
var rangesList = {};
|
||||
rangesList[$.kimai.settings['today']] = [moment(), moment()];
|
||||
rangesList[$.kimai.settings['yesterday']] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')];
|
||||
rangesList[$.kimai.settings['thisWeek']] = [moment().startOf('week'), moment().endOf('week')];
|
||||
rangesList[$.kimai.settings['lastWeek']] = [moment().subtract(1, 'week').startOf('week'), moment().subtract(1, 'week').endOf('week')];
|
||||
rangesList[$.kimai.settings['thisMonth']] = [moment().startOf('month'), moment().endOf('month')];
|
||||
rangesList[$.kimai.settings['lastMonth']] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
|
||||
rangesList[$.kimai.settings['thisYear']] = [moment().startOf('year'), moment().endOf('year')];
|
||||
rangesList[$.kimai.settings['lastYear']] = [moment().subtract(1, 'year').startOf('year'), moment().subtract(1, 'year').endOf('year')];
|
||||
|
||||
$(this).daterangepicker({
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false,
|
||||
autoApply: false,
|
||||
linkedCalendars: false,
|
||||
locale: {
|
||||
separator: separator,
|
||||
format: localeFormat,
|
||||
firstDay: 1,
|
||||
applyLabel: $.kimai.settings['apply'],
|
||||
cancelLabel: $.kimai.settings['cancel'],
|
||||
customRangeLabel: $.kimai.settings['customRange']
|
||||
},
|
||||
ranges: rangesList,
|
||||
alwaysShowCalendars: true
|
||||
});
|
||||
|
||||
$(this).on('apply.daterangepicker', function(ev, picker) {
|
||||
$(this).val(picker.startDate.format(localeFormat) + ' - ' + picker.endDate.format(localeFormat));
|
||||
$(this).trigger("change");
|
||||
});
|
||||
});
|
||||
|
||||
$('input[data-datepicker="on"]').on('apply.daterangepicker', function(ev, picker) {
|
||||
$(this).val(picker.startDate.format('YYYY-MM-DD'));
|
||||
$(this).trigger("change");
|
||||
// ==== single select boxes in toolbars ====
|
||||
$('input[data-datepickerenable="on"]').each(function(index) {
|
||||
var localeFormat = $(this).data('format');
|
||||
$(this).daterangepicker({
|
||||
singleDatePicker: true,
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false,
|
||||
locale: {
|
||||
format: localeFormat,
|
||||
firstDay: 1,
|
||||
applyLabel: $.kimai.settings['apply'],
|
||||
cancelLabel: $.kimai.settings['cancel'],
|
||||
customRangeLabel: $.kimai.settings['customRange']
|
||||
}
|
||||
});
|
||||
|
||||
$(this).on('apply.daterangepicker', function(ev, picker) {
|
||||
$(this).val(picker.startDate.format(localeFormat));
|
||||
$(this).trigger("change");
|
||||
});
|
||||
});
|
||||
|
||||
$('input[data-datetimepicker="on"]').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
timePicker: true,
|
||||
timePicker24Hour: true,
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false,
|
||||
locale: {
|
||||
cancelLabel: 'Clear',
|
||||
format: "YYYY-MM-DD HH:mm",
|
||||
firstDay: 1
|
||||
}
|
||||
});
|
||||
// ==== edit timesheet - date with time ====
|
||||
$('input[data-datetimepicker="on"]').each(function(index) {
|
||||
var localeFormat = $(this).data('format');
|
||||
$(this).daterangepicker({
|
||||
singleDatePicker: true,
|
||||
timePicker: true,
|
||||
timePicker24Hour: true,
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false,
|
||||
locale: {
|
||||
format: localeFormat,
|
||||
firstDay: 1,
|
||||
applyLabel: $.kimai.settings['apply'],
|
||||
cancelLabel: $.kimai.settings['cancel'],
|
||||
customRangeLabel: $.kimai.settings['customRange']
|
||||
}
|
||||
});
|
||||
|
||||
$('input[data-datetimepicker="on"]').on('apply.daterangepicker', function(ev, picker) {
|
||||
$(this).val(picker.startDate.format('YYYY-MM-DD HH:mm'));
|
||||
$(this).trigger("change");
|
||||
$(this).on('apply.daterangepicker', function(ev, picker) {
|
||||
$(this).val(picker.startDate.format(localeFormat));
|
||||
$(this).trigger("change");
|
||||
});
|
||||
});
|
||||
|
||||
$('select[data-related-select]').change(function() {
|
||||
@@ -146,8 +197,19 @@ $(function() {
|
||||
// default values
|
||||
$.kimai.defaults = {
|
||||
locale: 'en',
|
||||
alertSuccessAutoHide: 5000,
|
||||
confirmDelete: 'Really delete?',
|
||||
alertSuccessAutoHide: 5000
|
||||
today: 'Today',
|
||||
yesterday: 'Yesterday',
|
||||
apply: 'Apply',
|
||||
cancel: 'Cancel',
|
||||
thisWeek: 'This week',
|
||||
lastWeek: 'Last week',
|
||||
thisMonth: 'This month',
|
||||
lastMonth: 'Last month',
|
||||
thisYear: 'This year',
|
||||
lastYear: 'Last year',
|
||||
customRange: 'Custom range'
|
||||
};
|
||||
|
||||
// once initialized, here are all values
|
||||
|
||||
@@ -97,41 +97,68 @@ kimai:
|
||||
# Language specific settings, like the date formats
|
||||
languages:
|
||||
de:
|
||||
date_short: 'd.m.Y'
|
||||
duration: '%%h:%%m Stunden'
|
||||
duration_short: '%%h:%%m h'
|
||||
date_time_type: 'dd.MM.yyyy HH:mm'
|
||||
date_time_picker: 'DD.MM.YYYY HH:mm'
|
||||
date_type: 'dd.MM.yyyy'
|
||||
date_picker: 'DD.MM.YYYY'
|
||||
date: 'd.m.Y'
|
||||
duration: '%%h:%%m h'
|
||||
en:
|
||||
date_short: 'Y-m-d'
|
||||
duration: '%%h:%%m hours'
|
||||
duration_short: '%%h:%%m h'
|
||||
date_time_type: 'yyyy-MM-dd HH:mm'
|
||||
date_time_picker: 'YYYY-MM-DD HH:mm'
|
||||
date_type: 'yyyy-MM-dd'
|
||||
date_picker: 'YYYY-MM-DD'
|
||||
date: 'Y-m-d'
|
||||
duration: '%%h:%%m h'
|
||||
pt_BR:
|
||||
date_short: 'd-m-Y'
|
||||
duration: '%%h:%%m horas'
|
||||
duration_short: '%%h:%%m h'
|
||||
date_time_type: 'dd-MM-yyyy HH:mm'
|
||||
date_time_picker: 'DD-MM-YYYY HH:mm'
|
||||
date_type: 'dd-MM-yyyy'
|
||||
date_picker: 'DD-MM-YYYY'
|
||||
date: 'd-m-Y'
|
||||
duration: '%%h:%%m h'
|
||||
it:
|
||||
date_short: 'd.m.Y'
|
||||
date_time_type: 'dd.MM.yyyy HH:mm'
|
||||
date_time_picker: 'DD.MM.YYYY HH:mm'
|
||||
date_type: 'dd.MM.yyyy'
|
||||
date_picker: 'DD.MM.YYYY'
|
||||
date: 'd.m.Y'
|
||||
duration: '%%h:%%m h'
|
||||
duration_short: '%%h:%%m h'
|
||||
fr:
|
||||
date_short: 'd/m/Y'
|
||||
duration: '%%h h %%m min'
|
||||
duration_short: '%%h h %%m'
|
||||
date_time_type: 'dd/MM/yyyy HH:mm'
|
||||
date_time_picker: 'DD/MM/YYYY HH:mm'
|
||||
date_type: 'dd/MM/yyyy'
|
||||
date_picker: 'DD/MM/YYYY'
|
||||
date: 'd/m/Y'
|
||||
duration: '%%h h %%m'
|
||||
es:
|
||||
date_short: 'd.m.Y'
|
||||
date_time_type: 'dd.MM.yyyy HH:mm'
|
||||
date_time_picker: 'DD.MM.YYYY HH:mm'
|
||||
date_type: 'dd.MM.yyyy'
|
||||
date_picker: 'DD.MM.YYYY'
|
||||
date: 'd.m.Y'
|
||||
duration: '%%h:%%m h'
|
||||
duration_short: '%%h:%%m h'
|
||||
ru:
|
||||
date_short: 'd.m.Y'
|
||||
duration: '%%h:%%m Часов'
|
||||
duration_short: '%%h:%%m h'
|
||||
date_time_type: 'dd.MM.yyyy HH:mm'
|
||||
date_time_picker: 'DD.MM.YYYY HH:mm'
|
||||
date_type: 'dd.MM.yyyy'
|
||||
date_picker: 'DD.MM.YYYY'
|
||||
date: 'd.m.Y'
|
||||
duration: '%%h:%%m h'
|
||||
ar:
|
||||
date_short: 'Y-m-d'
|
||||
duration: '%%h:%%m hours'
|
||||
duration_short: '%%h:%%m h'
|
||||
date_time_type: 'yyyy-MM-dd HH:mm'
|
||||
date_time_picker: 'YYYY-MM-DD HH:mm'
|
||||
date_type: 'yyyy-MM-dd'
|
||||
date_picker: 'YYYY-MM-DD'
|
||||
date: 'Y-m-d'
|
||||
duration: '%%h:%%m h'
|
||||
hu:
|
||||
date_short: 'Y.m.d.'
|
||||
duration: '%%h:%%m óra'
|
||||
duration_short: '%%h:%%m h'
|
||||
date_time_type: 'yyyy.MM.dd HH:mm'
|
||||
date_time_picker: 'YYYY.MM.DD HH:mm'
|
||||
date_type: 'yyyy.MM.dd'
|
||||
date_picker: 'YYYY.MM.DD'
|
||||
date: 'Y.m.d.'
|
||||
duration: '%%h:%%m h'
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
# timesheet calendar configuration
|
||||
|
||||
@@ -54,7 +54,11 @@ services:
|
||||
App\Controller\Admin\CustomerController:
|
||||
arguments:
|
||||
$defaults: "%kimai.defaults%"
|
||||
|
||||
|
||||
App\Utils\LocaleSettings:
|
||||
arguments:
|
||||
$languageSettings: "%kimai.languages%"
|
||||
|
||||
# ================================================================================
|
||||
# DATABASE
|
||||
# ================================================================================
|
||||
@@ -105,14 +109,6 @@ services:
|
||||
# THEME
|
||||
# ================================================================================
|
||||
|
||||
App\Twig\Extensions:
|
||||
arguments:
|
||||
$languages: "%kimai.languages%"
|
||||
|
||||
App\Twig\DateExtensions:
|
||||
arguments:
|
||||
$dateSettings: "%kimai.languages%"
|
||||
|
||||
KevinPapst\AdminLTEBundle\Helper\ContextHelper:
|
||||
alias: admin_lte_theme.context_helper
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"build/app.js": "/build/app.js?605a955e4ca22355e6ff",
|
||||
"build/app.js": "/build/app.js?bf44c766db95ef7d121c",
|
||||
"build/app.css": "/build/app.css?cc325c031344117b92b2eaada5cf2319",
|
||||
"build/images/blue@2x.png": "/build/images/blue@2x.png?2694acfd",
|
||||
"build/images/blue.png": "/build/images/blue.png?96f8a905",
|
||||
|
||||
@@ -168,6 +168,13 @@ class InvoiceController extends AbstractController
|
||||
}
|
||||
|
||||
$query->setResultType(TimesheetQuery::RESULT_TYPE_QUERYBUILDER);
|
||||
|
||||
if (null === $query->getBegin()) {
|
||||
$query->setBegin(new \DateTime('first day of this month'));
|
||||
}
|
||||
if (null === $query->getEnd()) {
|
||||
$query->setEnd(new \DateTime('last day of this month'));
|
||||
}
|
||||
$query->getBegin()->setTime(0, 0, 0);
|
||||
$query->getEnd()->setTime(23, 59, 59);
|
||||
|
||||
|
||||
@@ -172,9 +172,12 @@ class Configuration implements ConfigurationInterface
|
||||
$node
|
||||
->arrayPrototype()
|
||||
->children()
|
||||
->scalarNode('date_short')->end()
|
||||
->scalarNode('duration')->end()
|
||||
->scalarNode('duration_short')->end()
|
||||
->scalarNode('date_time_type')->defaultValue('yyyy-MM-dd HH:mm')->end() // for DateTimeType
|
||||
->scalarNode('date_time_picker')->defaultValue('YYYY-MM-DD HH:mm')->end() // for DateTimeType JS component
|
||||
->scalarNode('date_type')->defaultValue('yyyy-MM-dd')->end() // for DateType
|
||||
->scalarNode('date_picker')->defaultValue('YYYY-MM-DD')->end() // for DateType JS component
|
||||
->scalarNode('date')->defaultValue('Y-m-d')->end() // for display via twig
|
||||
->scalarNode('duration')->defaultValue('%%h:%%m h')->end() // for display via twig
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
62
src/Form/Model/DateRange.php
Normal file
62
src/Form/Model/DateRange.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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\Form\Model;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class DateRange
|
||||
{
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $begin;
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $end;
|
||||
|
||||
/**
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getBegin(): ?DateTime
|
||||
{
|
||||
return $this->begin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $begin
|
||||
* @return DateRange
|
||||
*/
|
||||
public function setBegin(DateTime $begin)
|
||||
{
|
||||
$this->begin = $begin;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getEnd(): ?DateTime
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $end
|
||||
* @return DateRange
|
||||
*/
|
||||
public function setEnd(DateTime $end)
|
||||
{
|
||||
$this->end = $end;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ namespace App\Form;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Form\Type\ActivityType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\DateTimePickerType;
|
||||
use App\Form\Type\DurationType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\UserType;
|
||||
@@ -19,7 +20,6 @@ use App\Repository\ActivityRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -90,13 +90,8 @@ class TimesheetEditForm extends AbstractType
|
||||
}
|
||||
|
||||
if (null === $end || !$options['duration_only']) {
|
||||
$builder->add('begin', DateTimeType::class, [
|
||||
$builder->add('begin', DateTimePickerType::class, [
|
||||
'label' => 'label.begin',
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => 'yyyy-MM-dd HH:mm',
|
||||
'with_seconds' => false,
|
||||
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on', 'placeholder' => 'yyyy-MM-dd HH:mm'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -105,14 +100,8 @@ class TimesheetEditForm extends AbstractType
|
||||
'required' => false,
|
||||
]);
|
||||
} else {
|
||||
$builder->add('end', DateTimeType::class, [
|
||||
$builder->add('end', DateTimePickerType::class, [
|
||||
'label' => 'label.end',
|
||||
'widget' => 'single_text',
|
||||
'required' => false,
|
||||
'html5' => false,
|
||||
'format' => 'yyyy-MM-dd HH:mm',
|
||||
'with_seconds' => false,
|
||||
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on', 'placeholder' => 'yyyy-MM-dd HH:mm'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Form\Type\ActivityType;
|
||||
use App\Form\Type\BeginDateType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\EndDateType;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Form\Type\PageSizeType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\UserRoleType;
|
||||
@@ -115,20 +114,11 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
*/
|
||||
protected function addStartDateChoice(FormBuilderInterface $builder)
|
||||
protected function addDateRangeChoice(FormBuilderInterface $builder, $allowEmpty = true)
|
||||
{
|
||||
$builder->add('begin', BeginDateType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
*/
|
||||
protected function addEndDateChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('end', EndDateType::class, [
|
||||
$builder->add('daterange', DateRangeType::class, [
|
||||
'required' => false,
|
||||
'allow_empty' => $allowEmpty,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ class InvoiceToolbarForm extends AbstractToolbarForm
|
||||
{
|
||||
$this->addTemplateChoice($builder);
|
||||
$this->addUserChoice($builder);
|
||||
$this->addStartDateChoice($builder);
|
||||
$this->addEndDateChoice($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
|
||||
@@ -24,8 +24,7 @@ class TimesheetAdminToolbarForm extends TimesheetToolbarForm
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addUserChoice($builder);
|
||||
$this->addStartDateChoice($builder);
|
||||
$this->addEndDateChoice($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
|
||||
@@ -26,8 +26,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
{
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addStartDateChoice($builder);
|
||||
$this->addEndDateChoice($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
|
||||
@@ -9,33 +9,50 @@
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Custom form field type to display the begin-date input field.
|
||||
* Custom form field type to display the date input fields.
|
||||
*/
|
||||
class BeginDateType extends AbstractType
|
||||
class DatePickerType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$pickerFormat = $this->localeSettings->getDatePickerFormat();
|
||||
$dateFormat = $this->localeSettings->getDateTypeFormat();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.begin',
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => DateType::HTML5_FORMAT,
|
||||
'format' => $dateFormat,
|
||||
'format_picker' => $pickerFormat,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
return [
|
||||
'autocomplete' => 'off',
|
||||
'data-datepicker' => 'on',
|
||||
'placeholder' => $options['format'],
|
||||
'data-format' => $options['format_picker'],
|
||||
];
|
||||
});
|
||||
}
|
||||
154
src/Form/Type/DateRangeType.php
Normal file
154
src/Form/Type/DateRangeType.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?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\Form\Type;
|
||||
|
||||
use App\Form\Model\DateRange;
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Custom form field type to select a date range
|
||||
*/
|
||||
class DateRangeType extends AbstractType
|
||||
{
|
||||
public const DATE_SPACER = ' - ';
|
||||
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$pickerFormat = $this->localeSettings->getDatePickerFormat();
|
||||
$dateFormat = $this->localeSettings->getDateFormat();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'model_timezone' => null,
|
||||
'view_timezone' => null,
|
||||
'label' => 'label.daterange',
|
||||
'format' => $dateFormat,
|
||||
'separator' => self::DATE_SPACER,
|
||||
'format_picker' => $pickerFormat,
|
||||
'allow_empty' => true,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
return [
|
||||
'autocomplete' => 'off',
|
||||
'placeholder' => $options['format_picker'] . $options['separator'] . $options['format_picker'],
|
||||
'data-format' => $options['format_picker'],
|
||||
'data-separator' => $options['separator'],
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
protected function formatToPattern(string $format, string $separator)
|
||||
{
|
||||
$pattern = str_replace('d', '[0-9]{2}', $format);
|
||||
$pattern = str_replace('m', '[0-9]{2}', $pattern);
|
||||
$pattern = str_replace('Y', '[0-9]{4}', $pattern);
|
||||
|
||||
return '/^' . $pattern . $separator . $pattern . '$/';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$formatDate = $options['format'];
|
||||
$separator = $options['separator'];
|
||||
$allowEmpty = $options['allow_empty'];
|
||||
$pattern = $this->formatToPattern($formatDate, $separator);
|
||||
|
||||
$builder->addModelTransformer(new CallbackTransformer(
|
||||
function (DateRange $range) use ($formatDate, $separator) {
|
||||
if (null === $range->getBegin()) {
|
||||
return '';
|
||||
}
|
||||
$display = $range->getBegin()->format($formatDate);
|
||||
if (null !== $range->getEnd()) {
|
||||
$display .= $separator . $range->getEnd()->format($formatDate);
|
||||
}
|
||||
|
||||
return $display;
|
||||
},
|
||||
function ($dates) use ($formatDate, $pattern, $separator, $allowEmpty) {
|
||||
$range = new DateRange();
|
||||
|
||||
if (empty($dates) && $allowEmpty) {
|
||||
return $range;
|
||||
}
|
||||
|
||||
if (preg_match($pattern, $dates) !== 1) {
|
||||
throw new TransformationFailedException('Invalid date range given');
|
||||
}
|
||||
|
||||
$values = explode($separator, $dates);
|
||||
|
||||
if (count($values) !== 2) {
|
||||
throw new TransformationFailedException('Invalid date range given');
|
||||
}
|
||||
|
||||
$begin = \DateTime::createFromFormat($formatDate, $values[0]);
|
||||
if ($begin === false) {
|
||||
throw new TransformationFailedException('Invalid begin date given');
|
||||
}
|
||||
$range->setBegin($begin);
|
||||
|
||||
$end = \DateTime::createFromFormat($formatDate, $values[1]);
|
||||
if ($end === false) {
|
||||
throw new TransformationFailedException('Invalid end date given');
|
||||
}
|
||||
$range->setEnd($end);
|
||||
|
||||
if ($begin->getTimestamp() > $end->getTimestamp()) {
|
||||
throw new TransformationFailedException('Begin date must be before end date');
|
||||
}
|
||||
|
||||
return $range;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'daterange';
|
||||
}
|
||||
}
|
||||
70
src/Form/Type/DateTimePickerType.php
Normal file
70
src/Form/Type/DateTimePickerType.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?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\Form\Type;
|
||||
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Custom form field type to display the date-time input fields.
|
||||
*/
|
||||
class DateTimePickerType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$dateTimePicker = $this->localeSettings->getDateTimePickerFormat();
|
||||
$dateTimeFormat = $this->localeSettings->getDateTimeTypeFormat();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.begin',
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => $dateTimeFormat,
|
||||
'format_picker' => $dateTimePicker,
|
||||
'with_seconds' => false,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
return [
|
||||
'data-datetimepicker' => 'on',
|
||||
'autocomplete' => 'off',
|
||||
'placeholder' => $options['format'],
|
||||
'data-format' => $options['format_picker'],
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return DateTimeType::class;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +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\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Custom form field type to display the end-date input field.
|
||||
*/
|
||||
class EndDateType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.end',
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => DateType::HTML5_FORMAT,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
return [
|
||||
'autocomplete' => 'off',
|
||||
'data-datepicker' => 'on',
|
||||
'placeholder' => $options['format'],
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return DateType::class;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ namespace App\Repository\Query;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\User;
|
||||
use App\Form\Model\DateRange;
|
||||
|
||||
/**
|
||||
* Can be used for advanced timesheet repository queries.
|
||||
@@ -44,13 +45,14 @@ class TimesheetQuery extends ActivityQuery
|
||||
*/
|
||||
protected $state = self::STATE_ALL;
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var DateRange
|
||||
*/
|
||||
protected $begin;
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $end;
|
||||
protected $dateRange;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->dateRange = new DateRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
@@ -123,7 +125,7 @@ class TimesheetQuery extends ActivityQuery
|
||||
*/
|
||||
public function getBegin()
|
||||
{
|
||||
return $this->begin;
|
||||
return $this->dateRange->getBegin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +134,7 @@ class TimesheetQuery extends ActivityQuery
|
||||
*/
|
||||
public function setBegin($begin)
|
||||
{
|
||||
$this->begin = $begin;
|
||||
$this->dateRange->setBegin($begin);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -142,7 +144,7 @@ class TimesheetQuery extends ActivityQuery
|
||||
*/
|
||||
public function getEnd()
|
||||
{
|
||||
return $this->end;
|
||||
return $this->dateRange->getEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +153,26 @@ class TimesheetQuery extends ActivityQuery
|
||||
*/
|
||||
public function setEnd($end)
|
||||
{
|
||||
$this->end = $end;
|
||||
$this->dateRange->setEnd($end);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateRange
|
||||
*/
|
||||
public function getDateRange(): DateRange
|
||||
{
|
||||
return $this->dateRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateRange $dateRange
|
||||
* @return TimesheetQuery
|
||||
*/
|
||||
public function setDateRange(DateRange $dateRange)
|
||||
{
|
||||
$this->dateRange = $dateRange;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Utils\LocaleSettings;
|
||||
use DateTime;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
/**
|
||||
@@ -18,27 +18,17 @@ use Twig\TwigFilter;
|
||||
*/
|
||||
class DateExtensions extends \Twig_Extension
|
||||
{
|
||||
private const FALLBACK_SHORT = 'Y-m-d';
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
protected $dateSettings;
|
||||
|
||||
/**
|
||||
* @var RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* DateExtensions constructor.
|
||||
* @param RequestStack $requestStack
|
||||
* @param array $dateSettings
|
||||
*/
|
||||
public function __construct(RequestStack $requestStack, array $dateSettings)
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->dateSettings = $dateSettings;
|
||||
$this->localeSettings = $localeSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,26 +43,12 @@ class DateExtensions extends \Twig_Extension
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getLocale()
|
||||
{
|
||||
return $this->requestStack->getCurrentRequest()->getLocale();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $context
|
||||
* @param DateTime $date
|
||||
* @return string
|
||||
*/
|
||||
public function dateShort(DateTime $date)
|
||||
{
|
||||
$locale = $this->getLocale();
|
||||
$format = self::FALLBACK_SHORT;
|
||||
|
||||
if (isset($this->dateSettings[$locale]['date_short'])) {
|
||||
$format = $this->dateSettings[$locale]['date_short'];
|
||||
}
|
||||
$format = $this->localeSettings->getDateFormat();
|
||||
|
||||
return date_format($date, $format);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace App\Twig;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Utils\Duration;
|
||||
use App\Utils\LocaleSettings;
|
||||
use NumberFormatter;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Intl\Intl;
|
||||
@@ -22,9 +23,9 @@ use Twig\TwigFilter;
|
||||
class Extensions extends \Twig_Extension
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $locales;
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@@ -89,12 +90,12 @@ class Extensions extends \Twig_Extension
|
||||
|
||||
/**
|
||||
* @param RequestStack $requestStack
|
||||
* @param array $languages
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
public function __construct(RequestStack $requestStack, array $languages)
|
||||
public function __construct(RequestStack $requestStack, LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->locales = $languages;
|
||||
$this->localeSettings = $localeSettings;
|
||||
$this->durationFormatter = new Duration();
|
||||
}
|
||||
|
||||
@@ -200,19 +201,8 @@ class Extensions extends \Twig_Extension
|
||||
return '?';
|
||||
}
|
||||
|
||||
$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';
|
||||
$format = $this->localeSettings->getDurationFormat();
|
||||
}
|
||||
|
||||
return $this->durationFormatter->format($seconds, $format);
|
||||
@@ -253,7 +243,7 @@ class Extensions extends \Twig_Extension
|
||||
*/
|
||||
public function money($amount, $currency = null)
|
||||
{
|
||||
$locale = $this->getLocale();
|
||||
$locale = $this->localeSettings->getLocale();
|
||||
|
||||
if ($this->locale !== $locale) {
|
||||
$this->locale = $locale;
|
||||
@@ -271,14 +261,6 @@ class Extensions extends \Twig_Extension
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getLocale()
|
||||
{
|
||||
return $this->requestStack->getCurrentRequest()->getLocale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the list of codes of the locales (languages) enabled in the
|
||||
* application and returns an array with the name of each locale written
|
||||
@@ -289,7 +271,7 @@ class Extensions extends \Twig_Extension
|
||||
public function getLocales()
|
||||
{
|
||||
$locales = [];
|
||||
foreach (array_keys($this->locales) as $locale) {
|
||||
foreach ($this->localeSettings->getAvailableLanguages() as $locale) {
|
||||
$locales[] = ['code' => $locale, 'name' => Intl::getLocaleBundle()->getLocaleName($locale, $locale)];
|
||||
}
|
||||
|
||||
|
||||
147
src/Utils/LocaleSettings.php
Normal file
147
src/Utils/LocaleSettings.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?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\Utils;
|
||||
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
class LocaleSettings
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $locale = 'en';
|
||||
|
||||
/**
|
||||
* @param RequestStack $requestStack
|
||||
* @param array $languageSettings
|
||||
*/
|
||||
public function __construct(RequestStack $requestStack, array $languageSettings)
|
||||
{
|
||||
// It can be null in a console command
|
||||
if (null !== $requestStack->getMasterRequest()) {
|
||||
$this->locale = $requestStack->getMasterRequest()->getLocale();
|
||||
}
|
||||
$this->settings = $languageSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all available locale/language codes.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAvailableLanguages(): array
|
||||
{
|
||||
return array_keys($this->settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current locale used by the user in this request.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale(): string
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format which is used by the form component to handle date values.
|
||||
*
|
||||
* @param null|string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getDateTypeFormat(?string $locale = null): string
|
||||
{
|
||||
return $this->getConfigByLocaleAndKey('date_type', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format which is used by the Javascript component to handle date values.
|
||||
*
|
||||
* @param null|string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getDatePickerFormat(?string $locale = null): string
|
||||
{
|
||||
return $this->getConfigByLocaleAndKey('date_picker', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format which is used by the form component to handle datetime values.
|
||||
*
|
||||
* @param null|string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getDateTimeTypeFormat(?string $locale = null): string
|
||||
{
|
||||
return $this->getConfigByLocaleAndKey('date_time_type', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format which is used by the Javascript component to handle datetime values.
|
||||
*
|
||||
* @param null|string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getDateTimePickerFormat(?string $locale = null): string
|
||||
{
|
||||
return $this->getConfigByLocaleAndKey('date_time_picker', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locale specific date format, which should be used in combination with the twig filter "|date".
|
||||
*
|
||||
* @param null|string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getDateFormat(?string $locale = null): string
|
||||
{
|
||||
return $this->getConfigByLocaleAndKey('date', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format used in the "|duration" twig filter to display a Timesheet duration.
|
||||
*
|
||||
* @param null|string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getDurationFormat(?string $locale = null): string
|
||||
{
|
||||
return $this->getConfigByLocaleAndKey('duration', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param null|string $locale
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigByLocaleAndKey(string $key, ?string $locale = null): string
|
||||
{
|
||||
if (null === $locale) {
|
||||
$locale = $this->getLocale();
|
||||
}
|
||||
|
||||
if (!isset($this->settings[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Unknown locale given: %s', $locale));
|
||||
}
|
||||
|
||||
if (!isset($this->settings[$locale][$key])) {
|
||||
throw new \InvalidArgumentException(sprintf('Unknown setting for locale %s: %s', $locale, $key));
|
||||
}
|
||||
|
||||
return $this->settings[$locale][$key];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -94,8 +94,22 @@
|
||||
{# we do not call parent() as we use a custom built for the frontend assets and don't want the default <script> #}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$.kimai.init({locale: '{{ app.request.locale }}', confirmDelete: '{{ 'confirm.delete'|trans }}'});
|
||||
//$.kimai.pauseRecord('li.messages-menu ul.menu li');
|
||||
$.kimai.init({
|
||||
locale: '{{ app.request.locale }}',
|
||||
confirmDelete: '{{ 'confirm.delete'|trans }}',
|
||||
apply: '{{ 'daterangepicker.apply'|trans({}, 'daterangepicker') }}',
|
||||
cancel: '{{ 'daterangepicker.cancel'|trans({}, 'daterangepicker') }}',
|
||||
today: '{{ 'daterangepicker.today'|trans({}, 'daterangepicker') }}',
|
||||
yesterday: '{{ 'daterangepicker.yesterday'|trans({}, 'daterangepicker') }}',
|
||||
lastWeek: '{{ 'daterangepicker.lastWeek'|trans({}, 'daterangepicker') }}',
|
||||
thisWeek: '{{ 'daterangepicker.thisWeek'|trans({}, 'daterangepicker') }}',
|
||||
lastMonth: '{{ 'daterangepicker.lastMonth'|trans({}, 'daterangepicker') }}',
|
||||
thisMonth: '{{ 'daterangepicker.thisMonth'|trans({}, 'daterangepicker') }}',
|
||||
lastYear: '{{ 'daterangepicker.lastYear'|trans({}, 'daterangepicker') }}',
|
||||
thisYear: '{{ 'daterangepicker.thisYear'|trans({}, 'daterangepicker') }}',
|
||||
customRange: '{{ 'daterangepicker.customRange'|trans({}, 'daterangepicker') }}'
|
||||
});
|
||||
{# $.kimai.pauseRecord('li.messages-menu ul.menu li'); #}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -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('full') %}
|
||||
{% set data = widget.data|duration %}
|
||||
{% 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('full') %}
|
||||
{% set data = widget.data|duration %}
|
||||
{% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %}
|
||||
{% set data = widget.data|money %}
|
||||
{% endif %}
|
||||
|
||||
@@ -7,3 +7,15 @@
|
||||
{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock form_label %}
|
||||
|
||||
{% block daterange_widget %}
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">
|
||||
<i class="far fa-calendar-alt"></i>
|
||||
</div>
|
||||
|
||||
{% set attr = attr|merge({'data-daterangepickerenable':'on'}) %}
|
||||
|
||||
{{ block('form_widget_simple') }}
|
||||
</div>
|
||||
{% endblock daterange_widget %}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace App\Tests\Controller\Admin;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Tests\Controller\ControllerBaseTest;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
|
||||
@@ -55,13 +56,14 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/team/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = (new \DateTime('-10 days'))->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime())->format('Y-m-d');
|
||||
|
||||
$form = $client->getCrawler()->filter('form.navbar-form')->form();
|
||||
$client->submit($form, [
|
||||
'state' => 1,
|
||||
'user' => 1,
|
||||
'pageSize' => 25,
|
||||
'begin' => (new \DateTime('-10 days'))->format('Y-m-d'),
|
||||
'end' => (new \DateTime())->format('Y-m-d'),
|
||||
'daterange' => $dateRange,
|
||||
'customer' => null,
|
||||
]);
|
||||
|
||||
@@ -90,13 +92,14 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/team/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = (new \DateTime('-10 days'))->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime())->format('Y-m-d');
|
||||
|
||||
$form = $client->getCrawler()->filter('form.navbar-form')->form();
|
||||
$form->getFormNode()->setAttribute('action', $this->createUrl('/team/timesheet/export'));
|
||||
$client->submit($form, [
|
||||
'state' => 1,
|
||||
'pageSize' => 25,
|
||||
'begin' => (new \DateTime('-10 days'))->format('Y-m-d'),
|
||||
'end' => (new \DateTime())->format('Y-m-d'),
|
||||
'daterange' => $dateRange,
|
||||
'customer' => null,
|
||||
]);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace App\Tests\Controller;
|
||||
|
||||
use App\Entity\InvoiceTemplate;
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Tests\DataFixtures\InvoiceFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
|
||||
@@ -137,12 +138,13 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/invoice/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = $begin->format('Y-m-d') . DateRangeType::DATE_SPACER . $end->format('Y-m-d');
|
||||
|
||||
$form = $client->getCrawler()->filter('#invoice-print-form')->form();
|
||||
$client->submit($form, [
|
||||
'template' => 1,
|
||||
'user' => '',
|
||||
'begin' => $begin->format('Y-m-d'),
|
||||
'end' => $end->format('Y-m-d'),
|
||||
'daterange' => $dateRange,
|
||||
'customer' => 1,
|
||||
]);
|
||||
|
||||
@@ -163,8 +165,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$client->submit($form, [
|
||||
'template' => 1,
|
||||
'user' => '',
|
||||
'begin' => $begin->format('Y-m-d'),
|
||||
'end' => $end->format('Y-m-d'),
|
||||
'daterange' => $dateRange,
|
||||
'customer' => 1,
|
||||
'project' => 1,
|
||||
]);
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace App\Tests\Controller;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
|
||||
/**
|
||||
@@ -54,12 +55,13 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = (new \DateTime('-10 days'))->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime())->format('Y-m-d');
|
||||
|
||||
$form = $client->getCrawler()->filter('form.navbar-form')->form();
|
||||
$client->submit($form, [
|
||||
'state' => 1,
|
||||
'pageSize' => 25,
|
||||
'begin' => (new \DateTime('-10 days'))->format('Y-m-d'),
|
||||
'end' => (new \DateTime())->format('Y-m-d'),
|
||||
'daterange' => $dateRange,
|
||||
'customer' => null,
|
||||
]);
|
||||
|
||||
@@ -83,13 +85,14 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = (new \DateTime('-10 days'))->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime())->format('Y-m-d');
|
||||
|
||||
$form = $client->getCrawler()->filter('form.navbar-form')->form();
|
||||
$form->getFormNode()->setAttribute('action', $this->createUrl('/timesheet/export'));
|
||||
$client->submit($form, [
|
||||
'state' => 1,
|
||||
'pageSize' => 25,
|
||||
'begin' => (new \DateTime('-10 days'))->format('Y-m-d'),
|
||||
'end' => (new \DateTime())->format('Y-m-d'),
|
||||
'daterange' => $dateRange,
|
||||
'customer' => null,
|
||||
]);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ use App\Model\InvoiceModel;
|
||||
use App\Repository\Query\InvoiceQuery;
|
||||
use App\Twig\DateExtensions;
|
||||
use App\Twig\Extensions;
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
@@ -56,16 +57,22 @@ abstract class AbstractRendererTest extends KernelTestCase
|
||||
protected function getAbstractRenderer(string $classname)
|
||||
{
|
||||
$requestStack = new RequestStack();
|
||||
$dateSettings = [];
|
||||
$languages = [];
|
||||
$languages = [
|
||||
'en' => [
|
||||
'date' => 'Y.m.d',
|
||||
'duration' => '%%h:%%m h'
|
||||
]
|
||||
];
|
||||
|
||||
$request = new Request();
|
||||
$request->setLocale('en');
|
||||
$requestStack->push($request);
|
||||
|
||||
$localeSettings = new LocaleSettings($requestStack, $languages);
|
||||
|
||||
$translator = $this->getMockBuilder(TranslatorInterface::class)->getMock();
|
||||
$dateExtension = new DateExtensions($requestStack, $dateSettings);
|
||||
$extensions = new Extensions($requestStack, $languages);
|
||||
$dateExtension = new DateExtensions($localeSettings);
|
||||
$extensions = new Extensions($requestStack, $localeSettings);
|
||||
|
||||
return new $classname($translator, $dateExtension, $extensions);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Tests\Twig;
|
||||
|
||||
use App\Twig\DateExtensions;
|
||||
use App\Utils\LocaleSettings;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
@@ -32,7 +33,9 @@ class DateExtensionsTest extends TestCase
|
||||
$requestStack = new RequestStack();
|
||||
$requestStack->push($request);
|
||||
|
||||
return new DateExtensions($requestStack, $dateSettings);
|
||||
$localeSettings = new LocaleSettings($requestStack, $dateSettings);
|
||||
|
||||
return new DateExtensions($localeSettings);
|
||||
}
|
||||
|
||||
public function testGetFilters()
|
||||
@@ -57,9 +60,9 @@ class DateExtensionsTest extends TestCase
|
||||
public function testDateShort($locale, \DateTime $date, $result)
|
||||
{
|
||||
$sut = $this->getSut($locale, [
|
||||
'de' => ['date_short' => 'd.m.Y'],
|
||||
'en' => ['date_short' => 'Y-m-d'],
|
||||
'ru' => ['date_short' => 'd.m.Y'],
|
||||
'de' => ['date' => 'd.m.Y'],
|
||||
'en' => ['date' => 'Y-m-d'],
|
||||
'ru' => ['date' => 'd.m.Y'],
|
||||
]);
|
||||
$this->assertEquals($result, $sut->dateShort($date));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace App\Tests\Twig;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Twig\Extensions;
|
||||
use App\Utils\LocaleSettings;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
@@ -21,10 +22,10 @@ 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']];
|
||||
private $localeEn = ['en' => ['date' => 'Y-m-d', 'duration' => '%h:%m h']];
|
||||
private $localeDe = ['de' => ['date' => 'd.m.Y', 'duration' => '%h:%m h']];
|
||||
private $localeRu = ['ru' => ['date' => 'd.m.Y', 'duration' => '%h:%m h']];
|
||||
private $localeFake = ['XX' => ['date' => 'd.m.Y', 'duration' => '%h - %m - %s Zeit']];
|
||||
|
||||
/**
|
||||
* @param array $locales
|
||||
@@ -38,7 +39,9 @@ class ExtensionsTest extends TestCase
|
||||
$requestStack = new RequestStack();
|
||||
$requestStack->push($request);
|
||||
|
||||
return new Extensions($requestStack, $locales);
|
||||
$localeSettings = new LocaleSettings($requestStack, $locales);
|
||||
|
||||
return new Extensions($requestStack, $localeSettings);
|
||||
}
|
||||
|
||||
public function testGetFilters()
|
||||
@@ -154,22 +157,17 @@ class ExtensionsTest extends TestCase
|
||||
|
||||
// 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()));
|
||||
$this->assertEquals('02 - 37 - 17 Zeit', $sut->duration($record->getDuration()));
|
||||
|
||||
// test negative duration
|
||||
$sut = $this->getSut($this->localeEn, 'XX');
|
||||
$sut = $this->getSut($this->localeEn, 'en');
|
||||
$this->assertEquals('?', $sut->duration('-1'));
|
||||
|
||||
// test zero duration
|
||||
$sut = $this->getSut($this->localeEn, 'XX');
|
||||
$sut = $this->getSut($this->localeEn, 'en');
|
||||
$this->assertEquals('00:00 h', $sut->duration('0'));
|
||||
|
||||
$sut = $this->getSut($this->localeEn, 'XX');
|
||||
$sut = $this->getSut($this->localeEn, 'en');
|
||||
$this->assertNull($sut->duration(null));
|
||||
}
|
||||
|
||||
|
||||
192
tests/Utils/LocaleSettingsTest.php
Normal file
192
tests/Utils/LocaleSettingsTest.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?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\Tests\Utils;
|
||||
|
||||
use App\Utils\LocaleSettings;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* @covers \App\Utils\LocaleSettings
|
||||
*/
|
||||
class LocaleSettingsTest extends TestCase
|
||||
{
|
||||
|
||||
protected function getRequestStack(string $locale)
|
||||
{
|
||||
$request = new Request();
|
||||
$request->setLocale($locale);
|
||||
$requestStack = new RequestStack();
|
||||
$requestStack->push($request);
|
||||
|
||||
return $requestStack;
|
||||
}
|
||||
|
||||
protected function getSut(string $locale, array $settings)
|
||||
{
|
||||
return new LocaleSettings($this->getRequestStack($locale), $settings);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'de' => [
|
||||
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
||||
'date_time_picker' => 'DD.MM.YYYY HH:mm',
|
||||
'date_type' => 'dd.MM.yyyy',
|
||||
'date_picker' => 'DD.MM.YYYY',
|
||||
'date' => 'd.m.Y',
|
||||
'duration' => '%h:%m h',
|
||||
],
|
||||
'en' => [
|
||||
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
||||
'date_time_picker' => 'YYYY-MM-DD HH:mm',
|
||||
'date_type' => 'yyyy-MM-dd',
|
||||
'date_picker' => 'YYYY-MM-DD',
|
||||
'date' => 'Y-m-d',
|
||||
'duration' => '%h:%m h',
|
||||
],
|
||||
'pt_BR' => [
|
||||
'date_time_type' => 'dd-MM-yyyy HH:mm',
|
||||
'date_time_picker' => 'DD-MM-YYYY HH:mm',
|
||||
'date_type' => 'dd-MM-yyyy',
|
||||
'date_picker' => 'DD-MM-YYYY',
|
||||
'date' => 'd-m-Y',
|
||||
'duration' => '%h:%m h',
|
||||
],
|
||||
'it' => [
|
||||
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
||||
'date_time_picker' => 'DD.MM.YYYY HH:mm',
|
||||
'date_type' => 'dd.MM.yyyy',
|
||||
'date_picker' => 'DD.MM.YYYY',
|
||||
'date' => 'd.m.Y',
|
||||
'duration' => '%h:%m h',
|
||||
],
|
||||
'fr' => [
|
||||
'date_time_type' => 'dd/MM/yyyy HH:mm',
|
||||
'date_time_picker' => 'DD/MM/YYYY HH:mm',
|
||||
'date_type' => 'dd/MM/yyyy',
|
||||
'date_picker' => 'DD/MM/YYYY',
|
||||
'date' => 'd/m/Y',
|
||||
'duration' => '%h h %m',
|
||||
],
|
||||
'es' => [
|
||||
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
||||
'date_time_picker' => 'DD.MM.YYYY HH:mm',
|
||||
'date_type' => 'dd.MM.yyyy',
|
||||
'date_picker' => 'DD.MM.YYYY',
|
||||
'date' => 'd.m.Y',
|
||||
'duration' => '%h:%m h',
|
||||
],
|
||||
'ru' => [
|
||||
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
||||
'date_time_picker' => 'DD.MM.YYYY HH:mm',
|
||||
'date_type' => 'dd.MM.yyyy',
|
||||
'date_picker' => 'DD.MM.YYYY',
|
||||
'date' => 'd.m.Y',
|
||||
'duration' => '%h:%m h',
|
||||
],
|
||||
'ar' => [
|
||||
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
||||
'date_time_picker' => 'YYYY-MM-DD HH:mm',
|
||||
'date_type' => 'yyyy-MM-dd',
|
||||
'date_picker' => 'YYYY-MM-DD',
|
||||
'date' => 'Y-m-d',
|
||||
'duration' => '%h:%m h',
|
||||
],
|
||||
'hu' => [
|
||||
'date_time_type' => 'yyyy.MM.dd HH:mm',
|
||||
'date_time_picker' => 'YYYY.MM.DD HH:mm',
|
||||
'date_type' => 'yyyy.MM.dd',
|
||||
'date_picker' => 'YYYY.MM.DD',
|
||||
'date' => 'Y.m.d.',
|
||||
'duration' => '%h:%m h',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetLocale()
|
||||
{
|
||||
$sut = $this->getSut('en', []);
|
||||
$this->assertEquals('en', $sut->getLocale());
|
||||
$sut = $this->getSut('ar', []);
|
||||
$this->assertEquals('ar', $sut->getLocale());
|
||||
}
|
||||
|
||||
public function testGetAvailableLanguages()
|
||||
{
|
||||
$sut = $this->getSut('en', []);
|
||||
$this->assertEquals([], $sut->getAvailableLanguages());
|
||||
|
||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||
$this->assertEquals(['de', 'en', 'pt_BR', 'it', 'fr', 'es', 'ru', 'ar', 'hu'], $sut->getAvailableLanguages());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidLocaleWithDefaultLocale()
|
||||
{
|
||||
$sut = $this->getSut('en', []);
|
||||
$sut->getDateFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidLocaleWithGivenLocale()
|
||||
{
|
||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||
$sut->getDateFormat('xx');
|
||||
}
|
||||
|
||||
public function testGetDurationFormat()
|
||||
{
|
||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||
$this->assertEquals('%h:%m h', $sut->getDurationFormat());
|
||||
$this->assertEquals('%h:%m h', $sut->getDurationFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetDateFormat()
|
||||
{
|
||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||
$this->assertEquals('Y-m-d', $sut->getDateFormat());
|
||||
$this->assertEquals('d.m.Y', $sut->getDateFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetDateTypeFormat()
|
||||
{
|
||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||
$this->assertEquals('yyyy-MM-dd', $sut->getDateTypeFormat());
|
||||
$this->assertEquals('dd.MM.yyyy', $sut->getDateTypeFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetDatePickerFormat()
|
||||
{
|
||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||
$this->assertEquals('YYYY-MM-DD', $sut->getDatePickerFormat());
|
||||
$this->assertEquals('DD.MM.YYYY', $sut->getDatePickerFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetDateTimeTypeFormat()
|
||||
{
|
||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||
$this->assertEquals('yyyy-MM-dd HH:mm', $sut->getDateTimeTypeFormat());
|
||||
$this->assertEquals('dd.MM.yyyy HH:mm', $sut->getDateTimeTypeFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetDateTimePickerFormat()
|
||||
{
|
||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||
$this->assertEquals('YYYY-MM-DD HH:mm', $sut->getDateTimePickerFormat());
|
||||
$this->assertEquals('DD.MM.YYYY HH:mm', $sut->getDateTimePickerFormat('de'));
|
||||
}
|
||||
}
|
||||
51
translations/daterangepicker.de.xliff
Normal file
51
translations/daterangepicker.de.xliff
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file date="2019-02-02T10:00:00Z" source-language="en" target-language="de" datatype="plaintext" original="daterangepicker.en.xliff">
|
||||
<body>
|
||||
<trans-unit id="daterangepicker.apply">
|
||||
<source>daterangepicker.apply</source>
|
||||
<target>Bestätigen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.cancel">
|
||||
<source>daterangepicker.cancel</source>
|
||||
<target>Abbrechen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.today">
|
||||
<source>daterangepicker.today</source>
|
||||
<target>Heute</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.yesterday">
|
||||
<source>daterangepicker.yesterday</source>
|
||||
<target>Gestern</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.lastWeek">
|
||||
<source>daterangepicker.lastWeek</source>
|
||||
<target>Letzte Woche</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.thisWeek">
|
||||
<source>daterangepicker.thisWeek</source>
|
||||
<target>Diese Woche</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.lastMonth">
|
||||
<source>daterangepicker.lastMonth</source>
|
||||
<target>Letzter Monat</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.thisMonth">
|
||||
<source>daterangepicker.thisMonth</source>
|
||||
<target>Dieser Monat</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.lastYear">
|
||||
<source>daterangepicker.lastYear</source>
|
||||
<target>Letztes Jahr</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.thisYear">
|
||||
<source>daterangepicker.thisYear</source>
|
||||
<target>Dieses Jahr</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.customRange">
|
||||
<source>daterangepicker.customRange</source>
|
||||
<target>Benutzerdefiniert</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
51
translations/daterangepicker.en.xliff
Normal file
51
translations/daterangepicker.en.xliff
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file date="2019-02-02T10:00:00Z" source-language="en" target-language="de" datatype="plaintext" original="daterangepicker.en.xliff">
|
||||
<body>
|
||||
<trans-unit id="daterangepicker.apply">
|
||||
<source>daterangepicker.apply</source>
|
||||
<target>Apply</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.cancel">
|
||||
<source>daterangepicker.cancel</source>
|
||||
<target>Cancel</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.today">
|
||||
<source>daterangepicker.today</source>
|
||||
<target>Today</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.yesterday">
|
||||
<source>daterangepicker.yesterday</source>
|
||||
<target>Yesterday</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.lastWeek">
|
||||
<source>daterangepicker.lastWeek</source>
|
||||
<target>Last week</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.thisWeek">
|
||||
<source>daterangepicker.thisWeek</source>
|
||||
<target>This week</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.lastMonth">
|
||||
<source>daterangepicker.lastMonth</source>
|
||||
<target>Last month</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.thisMonth">
|
||||
<source>daterangepicker.thisMonth</source>
|
||||
<target>This month</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.lastYear">
|
||||
<source>daterangepicker.lastYear</source>
|
||||
<target>Last year</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.thisYear">
|
||||
<source>daterangepicker.thisYear</source>
|
||||
<target>This year</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="daterangepicker.customRange">
|
||||
<source>daterangepicker.customRange</source>
|
||||
<target>Custom range</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -311,6 +311,10 @@
|
||||
<source>label.end</source>
|
||||
<target>Bis</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.daterange">
|
||||
<source>label.daterange</source>
|
||||
<target>Zeitraum</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="modal.columns.title">
|
||||
<source>modal.columns.title</source>
|
||||
<target>Ändere Spalten-Sichtbarkeit</target>
|
||||
|
||||
@@ -311,6 +311,10 @@
|
||||
<source>label.end</source>
|
||||
<target>To</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.daterange">
|
||||
<source>label.daterange</source>
|
||||
<target>Time range</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="modal.columns.title">
|
||||
<source>modal.columns.title</source>
|
||||
<target>Change column visibility</target>
|
||||
|
||||
@@ -33,7 +33,8 @@ Adjust the file `config/packages/kimai.yaml` and add the language settings below
|
||||
kimai:
|
||||
languages:
|
||||
xx:
|
||||
date_short: 'd.m.Y'
|
||||
date: 'd.m.Y'
|
||||
# there are more settings, copy them from 'en'
|
||||
```
|
||||
|
||||
Append the new locale in the file `config/services.yaml` at `parameters.app_locales` divided by a pipe:
|
||||
|
||||
Reference in New Issue
Block a user