added tags for timesheets (#604)

This commit is contained in:
Mathias
2019-05-12 01:40:04 +02:00
committed by Kevin Papst
parent f31118292c
commit e29e183e84
84 changed files with 2132 additions and 158 deletions

View File

@@ -0,0 +1,82 @@
<?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\DataTransformer;
use App\Entity\Tag;
use App\Repository\TagRepository;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
class TagArrayToStringTransformer implements DataTransformerInterface
{
/**
* @var TagRepository
*/
private $tagRepository;
/**
* @param TagRepository $tagRepository
*/
public function __construct(TagRepository $tagRepository)
{
$this->tagRepository = $tagRepository;
}
/**
* Transforms an array of tags to a string.
*
* @param Tag[]|null $tags
*
* @return string
*/
public function transform($tags): string
{
if (empty($tags)) {
return '';
}
return implode(', ', $tags);
}
/**
* Transforms a string to an array of tags.
*
* @param string $stringOfTags
*
* @return Tag[]
* @throws TransformationFailedException if object (issue) is not found
*/
public function reverseTransform($stringOfTags): array
{
// check for empty tag list
if (empty($stringOfTags)) {
return [];
}
$names = array_filter(array_unique(array_map('trim', explode(',', $stringOfTags))));
// Get the current tags and find the new ones that should be created
$tags = $this->tagRepository->findBy(['name' => $names]);
$newNames = array_diff($names, $tags);
foreach ($newNames as $name) {
$tag = new Tag();
$tag->setName($name);
$tags[] = $tag;
// There's no need to persist these new tags because Doctrine does that automatically
// thanks to the cascade={"persist"} option in the App\Entity\Timesheet::$tags property.
}
// Return an array of tags to transform them back into a Doctrine Collection.
// See Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer::reverseTransform()
return $tags;
}
}

View File

@@ -92,6 +92,5 @@ class SelectWithApiDataExtension extends AbstractTypeExtension
{
$resolver->setDefined(['api_data']);
$resolver->setAllowedTypes('api_data', 'array');
//$resolver->setDefault('api_data', []);
}
}

View File

@@ -18,6 +18,7 @@ use App\Form\Type\DurationType;
use App\Form\Type\FixedRateType;
use App\Form\Type\HourlyRateType;
use App\Form\Type\ProjectType;
use App\Form\Type\TagsInputType;
use App\Form\Type\UserType;
use App\Form\Type\YesNoType;
use App\Repository\ActivityRepository;
@@ -250,8 +251,17 @@ class TimesheetEditForm extends AbstractType
->add('description', TextareaType::class, [
'label' => 'label.description',
'required' => false,
])
;
]);
$builder
->add('tags', TagsInputType::class, [
// documentation is for NelmioApiDocBundle
'documentation' => [
'type' => 'text',
'description' => 'Tags for timesheet entry',
],
'required' => false,
]);
if ($options['include_rate']) {
$builder

View File

@@ -14,6 +14,7 @@ use App\Form\Type\CustomerType;
use App\Form\Type\DateRangeType;
use App\Form\Type\PageSizeType;
use App\Form\Type\ProjectType;
use App\Form\Type\TagsInputType;
use App\Form\Type\UserRoleType;
use App\Form\Type\UserType;
use App\Form\Type\VisibilityType;
@@ -208,4 +209,14 @@ abstract class AbstractToolbarForm extends AbstractType
'empty_data' => 1
]);
}
/**
* @param FormBuilderInterface $builder
*/
protected function addTagInputField(FormBuilderInterface $builder)
{
$builder->add('tags', TagsInputType::class, [
'required' => false
]);
}
}

View File

@@ -33,6 +33,7 @@ class ExportToolbarForm extends AbstractToolbarForm
$this->addProjectChoice($builder);
$this->addActivityChoice($builder);
$this->addExportType($builder);
$this->addTagInputField($builder);
}
/**

View File

@@ -30,6 +30,7 @@ class InvoiceToolbarForm extends AbstractToolbarForm
$this->addCustomerChoice($builder);
$this->addProjectChoice($builder);
$this->addActivityChoice($builder);
$this->addTagInputField($builder);
}
protected function addTemplateChoice(FormBuilderInterface $builder)

View File

@@ -0,0 +1,37 @@
<?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\Toolbar;
use App\Repository\Query\TagQuery;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TagToolbarForm extends AbstractToolbarForm
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => TagQuery::class,
'csrf_protection' => false,
]);
}
}

View File

@@ -33,6 +33,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
$this->addCustomerChoice($builder);
$this->addProjectChoice($builder);
$this->addActivityChoice($builder);
$this->addTagInputField($builder);
$this->addHiddenPagination($builder);
}

View File

@@ -0,0 +1,77 @@
<?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\DataTransformer\TagArrayToStringTransformer;
use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Custom form field type to enter tags or use one of autocompleted field
*/
class TagsInputType extends AbstractType
{
/**
* @var TagArrayToStringTransformer
*/
private $transformer;
/**
* @var UrlGeneratorInterface
*/
private $router;
/**
* @param TagArrayToStringTransformer $transformer
* @param UrlGeneratorInterface $router
*/
public function __construct(TagArrayToStringTransformer $transformer, UrlGeneratorInterface $router)
{
$this->transformer = $transformer;
$this->router = $router;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->addModelTransformer(new CollectionToArrayTransformer(), true)
->addModelTransformer($this->transformer, true);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'label' => 'label.tag',
'attr' => [
'data-autocomplete-url' => $this->router->generate('get_tags'),
'class' => 'js-autocomplete',
'autocomplete' => 'off',
]
]);
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return TextType::class;
}
}