added option to use only pre-defined tags (#1463)

This commit is contained in:
Kevin Papst
2020-02-12 15:35:42 +01:00
committed by GitHub
parent 847798eec1
commit 1b4f4bba22
16 changed files with 103 additions and 24 deletions

View File

@@ -32,7 +32,7 @@ use Symfony\Component\Console\Style\SymfonyStyle;
* @internal * @internal
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
class ImportProjectCommand extends Command class ImportCustomerCommand extends Command
{ {
protected static $defaultName = 'kimai:import:customer'; protected static $defaultName = 'kimai:import:customer';

View File

@@ -23,12 +23,19 @@ class ThemeConfiguration implements SystemBundleConfiguration, \ArrayAccess
return (bool) $this->find('auto_reload_datatable'); return (bool) $this->find('auto_reload_datatable');
} }
public function isAllowTagCreation(): bool
{
return (bool) $this->find('tags_create');
}
/** /**
* Currently unused, as JS selects are always activated. * Currently unused, as JS selects are always activated.
* @deprecated since 1.7 will be removed with 2.0 * @deprecated since 1.7 will be removed with 2.0
*/ */
public function getSelectPicker(): string public function getSelectPicker(): string
{ {
@trigger_error('getSelectPicker() is deprecated and will be removed with 2.0', E_USER_DEPRECATED);
return (string) $this->find('select_type'); return (string) $this->find('select_type');
} }

View File

@@ -301,6 +301,11 @@ class SystemConfigurationController extends AbstractController
->setLabel('theme.markdown_content') ->setLabel('theme.markdown_content')
->setType(CheckboxType::class) ->setType(CheckboxType::class)
->setTranslationDomain('system-configuration'), ->setTranslationDomain('system-configuration'),
(new Configuration())
->setName('theme.tags_create')
->setLabel('theme.tags_create')
->setType(CheckboxType::class)
->setTranslationDomain('system-configuration'),
// TODO should that be configurable per user? // TODO should that be configurable per user?
/* /*
(new Configuration()) (new Configuration())

View File

@@ -329,9 +329,12 @@ class Configuration implements ConfigurationInterface
->defaultValue('selectpicker') ->defaultValue('selectpicker')
->setDeprecated() ->setDeprecated()
->end() ->end()
->scalarNode('auto_reload_datatable') ->booleanNode('auto_reload_datatable')
->defaultFalse() ->defaultFalse()
->end() ->end()
->booleanNode('tags_create')
->defaultTrue()
->end()
->booleanNode('show_about') ->booleanNode('show_about')
->defaultTrue() ->defaultTrue()
->end() ->end()

View File

@@ -10,6 +10,7 @@
namespace App\Form\API; namespace App\Form\API;
use App\Form\TimesheetEditForm; use App\Form\TimesheetEditForm;
use App\Form\Type\TagsInputType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -29,6 +30,19 @@ class TimesheetApiEditForm extends TimesheetEditForm
} }
} }
/**
* Method added to prevent API BC breaks.
*
* @param FormBuilderInterface $builder
*/
protected function addTags(FormBuilderInterface $builder)
{
$builder
->add('tags', TagsInputType::class, [
'required' => false,
]);
}
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
parent::configureOptions($resolver); parent::configureOptions($resolver);

View File

@@ -15,7 +15,7 @@ use App\Entity\Project;
use App\Form\Type\ActivityType; use App\Form\Type\ActivityType;
use App\Form\Type\CustomerType; use App\Form\Type\CustomerType;
use App\Form\Type\ProjectType; use App\Form\Type\ProjectType;
use App\Form\Type\TagsInputType; use App\Form\Type\TagsType;
use App\Repository\ActivityRepository; use App\Repository\ActivityRepository;
use App\Repository\CustomerRepository; use App\Repository\CustomerRepository;
use App\Repository\ProjectRepository; use App\Repository\ProjectRepository;
@@ -29,6 +29,7 @@ use Symfony\Component\Form\FormEvents;
/** /**
* Defines the form used to manipulate Timesheet entries. * Defines the form used to manipulate Timesheet entries.
* @internal
*/ */
trait FormTrait trait FormTrait
{ {
@@ -148,12 +149,7 @@ trait FormTrait
protected function addTags(FormBuilderInterface $builder) protected function addTags(FormBuilderInterface $builder)
{ {
$builder $builder
->add('tags', TagsInputType::class, [ ->add('tags', TagsType::class, [
// documentation is for NelmioApiDocBundle
'documentation' => [
'type' => 'string',
'description' => 'Comma separated list of tags',
],
'required' => false, 'required' => false,
]); ]);
} }

View File

@@ -14,7 +14,7 @@ use App\Form\Type\CustomerType;
use App\Form\Type\FixedRateType; use App\Form\Type\FixedRateType;
use App\Form\Type\HourlyRateType; use App\Form\Type\HourlyRateType;
use App\Form\Type\ProjectType; use App\Form\Type\ProjectType;
use App\Form\Type\TagsInputType; use App\Form\Type\TagsType;
use App\Form\Type\UserType; use App\Form\Type\UserType;
use App\Form\Type\YesNoType; use App\Form\Type\YesNoType;
use App\Repository\ActivityRepository; use App\Repository\ActivityRepository;
@@ -180,7 +180,7 @@ class TimesheetMultiUpdate extends AbstractType
] ]
]); ]);
$builder->add('tags', TagsInputType::class, [ $builder->add('tags', TagsType::class, [
'required' => false, 'required' => false,
]); ]);

View File

@@ -15,8 +15,7 @@ use App\Form\Type\DateRangeType;
use App\Form\Type\PageSizeType; use App\Form\Type\PageSizeType;
use App\Form\Type\ProjectType; use App\Form\Type\ProjectType;
use App\Form\Type\SearchTermType; use App\Form\Type\SearchTermType;
use App\Form\Type\TagsInputType; use App\Form\Type\TagsType;
use App\Form\Type\TagsSelectType;
use App\Form\Type\UserRoleType; use App\Form\Type\UserRoleType;
use App\Form\Type\UserType; use App\Form\Type\UserType;
use App\Form\Type\VisibilityType; use App\Form\Type\VisibilityType;
@@ -225,20 +224,11 @@ abstract class AbstractToolbarForm extends AbstractType
protected function addTagInputField(FormBuilderInterface $builder) protected function addTagInputField(FormBuilderInterface $builder)
{ {
$builder->add('tags', TagsInputType::class, [ $builder->add('tags', TagsType::class, [
'required' => false 'required' => false
]); ]);
} }
// TODO add a system setting to control if tags can be created on the fly
protected function addTagSelectField(FormBuilderInterface $builder)
{
$builder->add('tags', TagsSelectType::class, [
'required' => false,
'multiple' => true,
]);
}
protected function addSearchTermInputField(FormBuilderInterface $builder) protected function addSearchTermInputField(FormBuilderInterface $builder)
{ {
$builder->add('searchTerm', SearchTermType::class); $builder->add('searchTerm', SearchTermType::class);

View File

@@ -58,6 +58,11 @@ class TagsInputType extends AbstractType
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults([ $resolver->setDefaults([
// documentation is for NelmioApiDocBundle
'documentation' => [
'type' => 'string',
'description' => 'Comma separated list of tags',
],
'label' => 'label.tag', 'label' => 'label.tag',
'attr' => [ 'attr' => [
'data-autocomplete-url' => $this->router->generate('get_tags'), 'data-autocomplete-url' => $this->router->generate('get_tags'),

View File

@@ -28,6 +28,7 @@ class TagsSelectType extends AbstractType
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'multiple' => true,
'class' => Tag::class, 'class' => Tag::class,
'label' => 'label.tag', 'label' => 'label.tag',
'choice_label' => function (Tag $tag) { 'choice_label' => function (Tag $tag) {

View File

@@ -0,0 +1,38 @@
<?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\Configuration\ThemeConfiguration;
use Symfony\Component\Form\AbstractType;
class TagsType extends AbstractType
{
/**
* @var ThemeConfiguration
*/
private $configuration;
public function __construct(ThemeConfiguration $configuration)
{
$this->configuration = $configuration;
}
/**
* {@inheritdoc}
*/
public function getParent()
{
if ($this->configuration->isAllowTagCreation()) {
return TagsInputType::class;
}
return TagsSelectType::class;
}
}

View File

@@ -48,6 +48,7 @@ class ThemeConfigurationTest extends TestCase
'title' => null, 'title' => null,
], ],
'auto_reload_datatable' => false, 'auto_reload_datatable' => false,
'tags_create' => true,
]; ];
} }
@@ -61,7 +62,16 @@ class ThemeConfigurationTest extends TestCase
{ {
$sut = $this->getSut($this->getDefaultSettings(), []); $sut = $this->getSut($this->getDefaultSettings(), []);
$this->assertFalse($sut->isAutoReloadDatatable()); $this->assertFalse($sut->isAutoReloadDatatable());
$this->assertEquals('', $sut->getSelectPicker()); $this->assertTrue($sut->isAllowTagCreation());
$this->assertNull($sut->getTitle()); $this->assertNull($sut->getTitle());
} }
/**
* @group legacy
*/
public function testDeprecations()
{
$sut = $this->getSut($this->getDefaultSettings(), []);
$this->assertEquals('', $sut->getSelectPicker());
}
} }

View File

@@ -171,6 +171,7 @@ class AppExtensionTest extends TestCase
], ],
'auto_reload_datatable' => false, 'auto_reload_datatable' => false,
'autocomplete_chars' => 3, 'autocomplete_chars' => 3,
'tags_create' => true,
], ],
'kimai.theme.select_type' => 'selectpicker', 'kimai.theme.select_type' => 'selectpicker',
'kimai.theme.show_about' => true, 'kimai.theme.show_about' => true,

View File

@@ -337,6 +337,7 @@ class ConfigurationTest extends TestCase
'translation' => null, 'translation' => null,
], ],
'autocomplete_chars' => 3, 'autocomplete_chars' => 3,
'tags_create' => true,
], ],
'industry' => [ 'industry' => [
'translation' => null, 'translation' => null,

View File

@@ -34,6 +34,10 @@
<source>label.theme.markdown_content</source> <source>label.theme.markdown_content</source>
<target>Erlaube Markdown-Formatierungen in Beschreibungen und Kommentaren</target> <target>Erlaube Markdown-Formatierungen in Beschreibungen und Kommentaren</target>
</trans-unit> </trans-unit>
<trans-unit id="label.theme.tags_create">
<source>label.theme.tags_create</source>
<target>Schlagworte: Suche per Auto-Complete und Erstellung von Schlagworten erlauben</target>
</trans-unit>
<trans-unit id="label.timesheet.mode"> <trans-unit id="label.timesheet.mode">
<source>label.timesheet.mode</source> <source>label.timesheet.mode</source>
<target>Zeiterfassungs Modus</target> <target>Zeiterfassungs Modus</target>

View File

@@ -34,6 +34,10 @@
<source>label.theme.markdown_content</source> <source>label.theme.markdown_content</source>
<target>Allow markdown-formattings in descriptions and comments</target> <target>Allow markdown-formattings in descriptions and comments</target>
</trans-unit> </trans-unit>
<trans-unit id="label.theme.tags_create">
<source>label.theme.tags_create</source>
<target>Tags: use auto-complete search and allow tag creation</target>
</trans-unit>
<trans-unit id="label.timesheet.mode"> <trans-unit id="label.timesheet.mode">
<source>label.timesheet.mode</source> <source>label.timesheet.mode</source>
<target>Timetracking mode</target> <target>Timetracking mode</target>