Duration only mode #131 (#134)

This commit is contained in:
Kevin Papst
2018-02-10 22:19:49 +01:00
committed by GitHub
parent 93e1cd5095
commit 5a4bb0d081
19 changed files with 671 additions and 135 deletions

View File

@@ -1,35 +0,0 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Form;
use App\Form\Type\UserType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Defines the form used to administrate Timesheet entries.
*/
class TimesheetAdminForm extends TimesheetEditForm
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
// User
->add('user', UserType::class, [
'label' => 'label.user',
])
;
}
}

View File

@@ -9,13 +9,13 @@
namespace App\Form;
use App\Form\Type\DurationType;
use App\Form\Type\UserType;
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;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Entity\Customer;
use App\Entity\Timesheet;
use App\Form\Type\ActivityGroupedWithCustomerNameType;
use App\Repository\ActivityRepository;
@@ -39,36 +39,39 @@ class TimesheetEditForm extends AbstractType
$activity = $entry->getActivity();
}
$builder
// datetime
->add('begin', DateTimeType::class, [
if ($entry->getEnd() === null || !$options['duration_only']) {
$builder->add('begin', DateTimeType::class, [
'label' => 'label.begin',
'date_widget' => 'single_text',
])
// datetime
->add('end', DateTimeType::class, [
]);
}
if ($options['duration_only']) {
$builder->add('duration', DurationType::class);
} else {
$builder->add('end', DateTimeType::class, [
'label' => 'label.end',
'date_widget' => 'single_text',
'required' => false,
])
// Activity
]);
}
$builder
->add('activity', ActivityGroupedWithCustomerNameType::class, [
'label' => 'label.activity',
'query_builder' => function (ActivityRepository $repo) use ($activity) {
return $repo->builderForEntityType($activity);
},
])
// customer
->add('description', TextareaType::class, [
'label' => 'label.description',
'required' => false,
])
// string
->add('rate', MoneyType::class, [
'label' => 'label.rate',
'currency' => $builder->getOption('currency'),
])
;
if ($options['include_user']) {
$builder->add('user', UserType::class);
}
}
/**
@@ -81,7 +84,8 @@ class TimesheetEditForm extends AbstractType
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'timesheet_edit',
'currency' => Customer::DEFAULT_CURRENCY,
'duration_only' => false,
'include_user' => false,
]);
}
}

View File

@@ -0,0 +1,99 @@
<?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\Duration;
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\OptionsResolver;
use Symfony\Component\Validator\Constraints\Regex;
/**
* Custom form field type to handle a timesheet duration.
*/
class DurationType extends AbstractType
{
/**
* @var string
*/
protected $pattern;
/**
* DurationType constructor.
*/
public function __construct()
{
$patterns = [
'[0-9]{1,}',
'[0-9]{1,}:[0-9]{1,2}:[0-9]{1,2}',
'[0-9]{1,2}:[0-9]{1,2}',
'[0-9]{1,}[hmsHMS]{1}',
'[0-9]{1,}[hmsHMS]{1}[0-9]{1,}[hmsHMS]{1}',
'[0-9]{1,}[hmsHMS]{1}[0-9]{1,}[hmsHMS]{1}[0-9]{1,}[hmsHMS]{1}',
];
$this->pattern = '/^' . implode('$|^', $patterns) . '$/';
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'label' => 'label.duration',
'constraints' => [new Regex(['pattern' => $this->pattern])],
]);
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$formatter = new Duration();
$pattern = $this->pattern;
$builder->addModelTransformer(new CallbackTransformer(
function ($intToFormat) use ($formatter) {
try {
return $formatter->format($intToFormat, true);
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage());
}
},
function ($formatToInt) use ($formatter, $pattern) {
if (empty($formatToInt)) {
return 0;
}
if (!preg_match($pattern, $formatToInt)) {
throw new TransformationFailedException('Invalid duration format given');
}
try {
return $formatter->parseDurationString($formatToInt);
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage());
}
}
));
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return TextType::class;
}
}

View File

@@ -27,6 +27,7 @@ class UserType extends AbstractType
{
$resolver->setDefaults([
'class' => User::class,
'label' => 'label.user',
'choice_label' => function (User $user) {
if (!empty($user->getAlias())) {
return $user->getAlias() . ' (' . $user->getUsername() . ')';