Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -9,454 +9,25 @@
|
||||
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Form\Type\ActivityType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Form\Type\PageSizeType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\SearchTermType;
|
||||
use App\Form\Type\TagsType;
|
||||
use App\Form\Type\TeamType;
|
||||
use App\Form\Type\UserRoleType;
|
||||
use App\Form\Type\UserType;
|
||||
use App\Form\Type\VisibilityType;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\ActivityFormTypeQuery;
|
||||
use App\Repository\Query\BaseQuery;
|
||||
use App\Repository\Query\CustomerFormTypeQuery;
|
||||
use App\Repository\Query\ProjectFormTypeQuery;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Validator\Constraints\Choice;
|
||||
|
||||
/**
|
||||
* Defines the base form used for all toolbars.
|
||||
*
|
||||
* Extend this class and stack the elements defined here, they are coupled to each other and with the toolbar.js.
|
||||
* Extend this class and stack the elements defined here, they are coupled to each other and connected with javascript.
|
||||
*
|
||||
* @deprecated since 2.0, will be removed with 2.1 - use ToolbarFormTrait instead
|
||||
*/
|
||||
abstract class AbstractToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* Dirty hack to enable easy handling of GET form in controller and javascript.
|
||||
* Cleans up the name of all form elements (and unfortunately of the form itself).
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
@trigger_error('The "AbstractToolbarForm" is deprecated and will be removed with 2.1, use the "ToolbarFormTrait" instead', E_USER_DEPRECATED);
|
||||
|
||||
// Dirty hack to enable easy handling of GET form in controller and javascript.
|
||||
// Cleans up the name of all form elements (and unfortunately of the form itself).
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function addUserChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('user', UserType::class, [
|
||||
'label' => 'label.user',
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addUsersChoice(FormBuilderInterface $builder, string $field = 'users', array $options = [])
|
||||
{
|
||||
$builder->add($field, UserType::class, array_merge([
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'User ID'],
|
||||
'description' => 'Array of user IDs',
|
||||
],
|
||||
'label' => 'label.user',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
], $options));
|
||||
}
|
||||
|
||||
protected function addTeamChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('team', TeamType::class, [
|
||||
'label' => 'label.team',
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addTeamsChoice(FormBuilderInterface $builder, string $field = 'teams', array $options = [])
|
||||
{
|
||||
$builder->add($field, TeamType::class, array_merge([
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'Team ID'],
|
||||
'description' => 'Array of team IDs',
|
||||
],
|
||||
'label' => 'label.team',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
], $options));
|
||||
}
|
||||
|
||||
protected function addCustomerChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false)
|
||||
{
|
||||
$this->addCustomerSelect($builder, $options, false, $multiProject);
|
||||
}
|
||||
|
||||
protected function addCustomerMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false)
|
||||
{
|
||||
$this->addCustomerSelect($builder, $options, true, $multiProject);
|
||||
}
|
||||
|
||||
private function addCustomerSelect(FormBuilderInterface $builder, array $options, bool $multiCustomer, bool $multiProject)
|
||||
{
|
||||
$name = 'customer';
|
||||
if ($multiCustomer) {
|
||||
$name = 'customers';
|
||||
}
|
||||
|
||||
// just a fake field for having this field at the right position in the frontend
|
||||
$builder->add($name, CustomerType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'Customer ID'],
|
||||
'description' => 'Array of customer IDs',
|
||||
],
|
||||
'choices' => [],
|
||||
'multiple' => $multiCustomer,
|
||||
]);
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($builder, $options, $name, $multiCustomer, $multiProject) {
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, CustomerType::class, array_merge([
|
||||
'multiple' => $multiCustomer,
|
||||
'required' => false,
|
||||
'project_enabled' => $multiCustomer ? 'customers' : 'customer',
|
||||
'project_select' => $multiProject ? 'projects' : 'project',
|
||||
'end_date_param' => '%daterange%',
|
||||
'start_date_param' => '%daterange%',
|
||||
'query_builder' => function (CustomerRepository $repo) use ($builder, $data, $name, $multiCustomer) {
|
||||
$query = new CustomerFormTypeQuery();
|
||||
$query->setUser($builder->getOption('user'));
|
||||
|
||||
if (isset($data[$name]) && !empty($data[$name])) {
|
||||
if ($multiCustomer) {
|
||||
$query->setCustomers($data[$name]);
|
||||
} else {
|
||||
$query->addCustomer($data[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
], $options));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function addVisibilityChoice(FormBuilderInterface $builder, string $label = 'label.visible')
|
||||
{
|
||||
$builder->add('visibility', VisibilityType::class, [
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'label' => $label,
|
||||
'search' => false
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addPageSizeChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('pageSize', PageSizeType::class, [
|
||||
'required' => false,
|
||||
'search' => false
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addUserRoleChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('role', UserRoleType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addDateRange(FormBuilderInterface $builder, array $options, $allowEmpty = true, $required = false)
|
||||
{
|
||||
$params = [
|
||||
'required' => $required,
|
||||
'allow_empty' => $allowEmpty,
|
||||
];
|
||||
|
||||
if (\array_key_exists('timezone', $options)) {
|
||||
$params['timezone'] = $options['timezone'];
|
||||
}
|
||||
|
||||
$builder->add('daterange', DateRangeType::class, $params);
|
||||
}
|
||||
|
||||
protected function addDateRangeChoice(FormBuilderInterface $builder, $allowEmpty = true, $required = false)
|
||||
{
|
||||
$this->addDateRange($builder, [], $allowEmpty, $required);
|
||||
}
|
||||
|
||||
protected function addProjectChoice(FormBuilderInterface $builder, array $options = [], bool $multiCustomer = false, bool $multiActivity = false)
|
||||
{
|
||||
$this->addProjectSelect($builder, $options, false, $multiCustomer, $multiActivity);
|
||||
}
|
||||
|
||||
protected function addProjectMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiCustomer = false, bool $multiActivity = false)
|
||||
{
|
||||
$this->addProjectSelect($builder, $options, true, $multiCustomer, $multiActivity);
|
||||
}
|
||||
|
||||
private function addProjectSelect(FormBuilderInterface $builder, array $options, bool $multiProject, bool $multiCustomer, bool $multiActivity)
|
||||
{
|
||||
$name = 'project';
|
||||
if ($multiProject) {
|
||||
$name = 'projects';
|
||||
}
|
||||
// just a fake field for having this field at the right position in the frontend
|
||||
$builder->add($name, ProjectType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'Project ID'],
|
||||
'description' => 'Array of project IDs',
|
||||
],
|
||||
'choices' => [],
|
||||
'multiple' => $multiProject,
|
||||
]);
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($builder, $options, $name, $multiCustomer, $multiProject, $multiActivity) {
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, ProjectType::class, array_merge([
|
||||
'multiple' => $multiProject,
|
||||
'required' => false,
|
||||
'activity_enabled' => $multiProject ? 'projects' : 'project',
|
||||
'activity_select' => $multiActivity ? 'activities' : 'activity',
|
||||
'query_builder' => function (ProjectRepository $repo) use ($builder, $data, $options, $multiCustomer, $multiProject) {
|
||||
$query = new ProjectFormTypeQuery();
|
||||
$query->setUser($builder->getOption('user'));
|
||||
|
||||
$name = $multiCustomer ? 'customers' : 'customer';
|
||||
if (isset($data[$name]) && !empty($data[$name])) {
|
||||
if (\is_array($data[$name])) {
|
||||
$query->setCustomers($data[$name]);
|
||||
} else {
|
||||
$query->addCustomer($data[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$name = $multiProject ? 'projects' : 'project';
|
||||
if (isset($data[$name]) && !empty($data[$name])) {
|
||||
if (\is_array($data[$name])) {
|
||||
$query->setProjects($data[$name]);
|
||||
} else {
|
||||
$query->addProject($data[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['ignore_date']) && true === $options['ignore_date']) {
|
||||
$query->setIgnoreDate(true);
|
||||
}
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
], $options));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function addActivityChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false)
|
||||
{
|
||||
$this->addActivitySelect($builder, $options, false, $multiProject);
|
||||
}
|
||||
|
||||
protected function addActivityMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false)
|
||||
{
|
||||
$this->addActivitySelect($builder, $options, true, $multiProject);
|
||||
}
|
||||
|
||||
private function addActivitySelect(FormBuilderInterface $builder, array $options = [], bool $multiActivity = false, bool $multiProject = false)
|
||||
{
|
||||
$name = 'activity';
|
||||
if ($multiActivity) {
|
||||
$name = 'activities';
|
||||
}
|
||||
|
||||
// just a fake field for having this field at the right position in the frontend
|
||||
$builder->add($name, ActivityType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'Activity ID'],
|
||||
'description' => 'Array of activity IDs',
|
||||
],
|
||||
'choices' => [],
|
||||
'multiple' => $multiActivity,
|
||||
]);
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($name, $multiActivity, $multiProject) {
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, ActivityType::class, [
|
||||
'multiple' => $multiActivity,
|
||||
'required' => false,
|
||||
'query_builder' => function (ActivityRepository $repo) use ($data, $multiActivity, $multiProject) {
|
||||
$query = new ActivityFormTypeQuery();
|
||||
|
||||
$name = $multiActivity ? 'activities' : 'activity';
|
||||
if (isset($data[$name]) && !empty($data[$name])) {
|
||||
// we need to pre-fetch the activities to see if they are global, see ActivityFormTypeQuery::isGlobalsOnly()
|
||||
$activities = $data[$name];
|
||||
if (!\is_array($activities)) {
|
||||
$activities = [$activities];
|
||||
}
|
||||
foreach ($activities as $activity) {
|
||||
if ($activity instanceof Activity) {
|
||||
$query->addActivity($activity);
|
||||
} elseif ($activity !== null) {
|
||||
$tmp = $repo->find($activity);
|
||||
if (null !== $tmp) {
|
||||
$query->addActivity($tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$name = $multiProject ? 'projects' : 'project';
|
||||
if (isset($data[$name]) && !empty($data[$name])) {
|
||||
if ($multiProject) {
|
||||
$query->setProjects($data[$name]);
|
||||
} else {
|
||||
$query->addProject($data[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function addHiddenPagination(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('page', HiddenType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Page number. Default: 1',
|
||||
],
|
||||
'empty_data' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addHiddenOrder(FormBuilderInterface $builder)
|
||||
{
|
||||
@trigger_error('addHiddenOrder() is deprecated and will be removed with 2.0, use the new search modal instead', E_USER_DEPRECATED);
|
||||
|
||||
$builder->add('order', HiddenType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'string',
|
||||
'description' => 'The order for returned items',
|
||||
],
|
||||
'constraints' => [
|
||||
new Choice(['choices' => [BaseQuery::ORDER_ASC, BaseQuery::ORDER_DESC]])
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addOrder(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('order', ChoiceType::class, [
|
||||
'documentation' => [
|
||||
'description' => 'The order for returned items',
|
||||
],
|
||||
'label' => 'label.order',
|
||||
'choices' => [
|
||||
'label.asc' => BaseQuery::ORDER_ASC,
|
||||
'label.desc' => BaseQuery::ORDER_DESC
|
||||
],
|
||||
'search' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addHiddenOrderBy(FormBuilderInterface $builder, array $allowedColumns)
|
||||
{
|
||||
@trigger_error('addHiddenOrderBy() is deprecated and will be removed with 2.0, use the new search modal instead', E_USER_DEPRECATED);
|
||||
|
||||
$builder->add('orderBy', HiddenType::class, [
|
||||
'constraints' => [
|
||||
new Choice(['choices' => $allowedColumns])
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addOrderBy(FormBuilderInterface $builder, array $allowedColumns)
|
||||
{
|
||||
$all = [];
|
||||
foreach ($allowedColumns as $id => $name) {
|
||||
$label = \is_int($id) ? 'label.' . $name : $id;
|
||||
$all[$label] = $name;
|
||||
}
|
||||
$builder->add('orderBy', ChoiceType::class, [
|
||||
'label' => 'label.orderBy',
|
||||
'choices' => $all,
|
||||
'search' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addTagInputField(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('tags', TagsType::class, [
|
||||
'required' => false
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addSearchTermInputField(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('searchTerm', SearchTermType::class);
|
||||
}
|
||||
|
||||
protected function addTimesheetStateChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('state', ChoiceType::class, [
|
||||
'label' => 'label.entryState',
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'search' => false,
|
||||
'choices' => [
|
||||
'entryState.all' => TimesheetQuery::STATE_ALL,
|
||||
'entryState.running' => TimesheetQuery::STATE_RUNNING,
|
||||
'entryState.stopped' => TimesheetQuery::STATE_STOPPED
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addExportStateChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('exported', ChoiceType::class, [
|
||||
'label' => 'label.exported',
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'search' => false,
|
||||
'choices' => [
|
||||
'entryState.all' => TimesheetQuery::STATE_ALL,
|
||||
'entryState.exported' => TimesheetQuery::STATE_EXPORTED,
|
||||
'entryState.not_exported' => TimesheetQuery::STATE_NOT_EXPORTED
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addBillableChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('billable', BillableType::class, [
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'search' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Repository\Query\ActivityQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@@ -17,12 +18,11 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
/**
|
||||
* Defines the form used for filtering the activities.
|
||||
*/
|
||||
class ActivityToolbarForm extends AbstractToolbarForm
|
||||
final class ActivityToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$newOptions = [];
|
||||
if ($options['ignore_date'] === true) {
|
||||
@@ -40,7 +40,7 @@ class ActivityToolbarForm extends AbstractToolbarForm
|
||||
'search' => false,
|
||||
'placeholder' => null,
|
||||
'required' => false,
|
||||
'label' => 'label.globalsOnly',
|
||||
'label' => 'globalsOnly',
|
||||
]);
|
||||
$this->addVisibilityChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
@@ -49,10 +49,7 @@ class ActivityToolbarForm extends AbstractToolbarForm
|
||||
$this->addOrderBy($builder, ActivityQuery::ACTIVITY_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => ActivityQuery::class,
|
||||
|
||||
@@ -1,40 +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\Toolbar;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class BillableType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.billable',
|
||||
'choices' => [
|
||||
'entryState.all' => null,
|
||||
'yes' => true,
|
||||
'no' => false,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
}
|
||||
@@ -10,18 +10,18 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Repository\Query\CustomerQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering the customer.
|
||||
*/
|
||||
class CustomerToolbarForm extends AbstractToolbarForm
|
||||
final class CustomerToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addVisibilityChoice($builder);
|
||||
@@ -31,10 +31,7 @@ class CustomerToolbarForm extends AbstractToolbarForm
|
||||
$this->addOrderBy($builder, CustomerQuery::CUSTOMER_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => CustomerQuery::class,
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Form\Type\MarkAsExportedType;
|
||||
use App\Repository\Query\ExportQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@@ -18,44 +18,35 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
/**
|
||||
* Defines the form used for filtering timesheet entries for exports.
|
||||
*/
|
||||
class ExportToolbarForm extends AbstractToolbarForm
|
||||
final class ExportToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addBillableChoice($builder);
|
||||
$this->addExportStateChoice($builder);
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
if ($options['include_user']) {
|
||||
$this->addUsersChoice($builder);
|
||||
$this->addTeamsChoice($builder);
|
||||
}
|
||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||
$this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true);
|
||||
$this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true);
|
||||
$this->addActivityMultiChoice($builder, [], true);
|
||||
$this->addExportRenderer($builder);
|
||||
$this->addTagInputField($builder);
|
||||
if ($options['include_user']) {
|
||||
$this->addUsersChoice($builder);
|
||||
$this->addTeamsChoice($builder);
|
||||
}
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
$this->addBillableChoice($builder);
|
||||
$this->addExportStateChoice($builder);
|
||||
$builder->add('renderer', HiddenType::class, []);
|
||||
if ($options['include_export']) {
|
||||
$builder->add('markAsExported', MarkAsExportedType::class);
|
||||
$builder->add('markAsExported', HiddenType::class, [
|
||||
'label' => 'mark_as_exported',
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
*/
|
||||
protected function addExportRenderer(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('renderer', HiddenType::class, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => ExportQuery::class,
|
||||
|
||||
@@ -11,32 +11,30 @@ namespace App\Form\Toolbar;
|
||||
|
||||
use App\Form\Type\InvoiceStatusType;
|
||||
use App\Repository\Query\InvoiceArchiveQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering timesheet entries for invoices.
|
||||
*/
|
||||
class InvoiceArchiveForm extends AbstractToolbarForm
|
||||
final class InvoiceArchiveForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||
$this->addCustomerMultiChoice($builder, ['required' => false, 'start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true, 'placeholder' => ''], true);
|
||||
$builder->add('status', InvoiceStatusType::class, ['required' => false]);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
$this->addOrder($builder);
|
||||
$this->addOrderBy($builder, InvoiceArchiveQuery::INVOICE_ARCHIVE_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => InvoiceArchiveQuery::class,
|
||||
|
||||
@@ -9,20 +9,26 @@
|
||||
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Form\Type\DatePickerType;
|
||||
use App\Form\Type\InvoiceTemplateType;
|
||||
use App\Repository\Query\InvoiceQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering timesheet entries for invoices.
|
||||
*/
|
||||
class InvoiceToolbarForm extends InvoiceToolbarSimpleForm
|
||||
final class InvoiceToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
parent::buildForm($builder, $options);
|
||||
$this->addTemplateChoice($builder);
|
||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||
$this->addCustomerMultiChoice($builder, ['required' => false, 'start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true, 'placeholder' => ''], true);
|
||||
$this->addProjectMultiChoice($builder, ['ignore_date' => true], false, true);
|
||||
$this->addSearchTermInputField($builder);
|
||||
if ($options['include_user']) {
|
||||
$this->addUsersChoice($builder);
|
||||
@@ -31,14 +37,26 @@ class InvoiceToolbarForm extends InvoiceToolbarSimpleForm
|
||||
$this->addActivityMultiChoice($builder, $options, true);
|
||||
$this->addTagInputField($builder);
|
||||
$this->addExportStateChoice($builder);
|
||||
$builder->add('invoiceDate', DatePickerType::class, [
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
protected function addTemplateChoice(FormBuilderInterface $builder): void
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
$resolver->setDefault('include_user', true);
|
||||
$builder->add('template', InvoiceTemplateType::class, [
|
||||
'required' => true,
|
||||
'placeholder' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => InvoiceQuery::class,
|
||||
'csrf_protection' => false,
|
||||
'include_user' => true,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +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\Toolbar;
|
||||
|
||||
use App\Form\Type\InvoiceTemplateType;
|
||||
use App\Form\Type\MarkAsExportedType;
|
||||
use App\Repository\Query\InvoiceQuery;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering timesheet entries for invoices.
|
||||
*/
|
||||
class InvoiceToolbarSimpleForm extends AbstractToolbarForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addTemplateChoice($builder);
|
||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||
$this->addCustomerMultiChoice($builder, ['required' => false, 'start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true, 'placeholder' => ''], true);
|
||||
$this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true);
|
||||
if ($options['include_export']) {
|
||||
$builder->add('markAsExported', MarkAsExportedType::class);
|
||||
}
|
||||
}
|
||||
|
||||
protected function addTemplateChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('template', InvoiceTemplateType::class, [
|
||||
'required' => true,
|
||||
'placeholder' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => InvoiceQuery::class,
|
||||
'csrf_protection' => false,
|
||||
'include_user' => true,
|
||||
'include_export' => true,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -10,18 +10,18 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Repository\Query\ProjectQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering the projects.
|
||||
*/
|
||||
class ProjectToolbarForm extends AbstractToolbarForm
|
||||
final class ProjectToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addCustomerMultiChoice($builder, [], false);
|
||||
@@ -32,10 +32,7 @@ class ProjectToolbarForm extends AbstractToolbarForm
|
||||
$this->addOrderBy($builder, ProjectQuery::PROJECT_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => ProjectQuery::class,
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Repository\Query\TagQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TagToolbarForm extends AbstractToolbarForm
|
||||
final class TagToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
@@ -27,10 +27,7 @@ class TagToolbarForm extends AbstractToolbarForm
|
||||
$this->addOrderBy($builder, TagQuery::TAG_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => TagQuery::class,
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Repository\Query\TeamQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TeamToolbarForm extends AbstractToolbarForm
|
||||
final class TeamToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$this->addUsersChoice($builder);
|
||||
$this->addSearchTermInputField($builder);
|
||||
@@ -28,10 +28,7 @@ class TeamToolbarForm extends AbstractToolbarForm
|
||||
$this->addOrderBy($builder, TeamQuery::TEAM_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => TeamQuery::class,
|
||||
|
||||
@@ -10,23 +10,18 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering the timesheet.
|
||||
*/
|
||||
class TimesheetExportToolbarForm extends AbstractToolbarForm
|
||||
final class TimesheetExportToolbarForm extends AbstractType
|
||||
{
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'export';
|
||||
}
|
||||
use ToolbarFormTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$newOptions = [];
|
||||
if ($options['ignore_date'] === true) {
|
||||
@@ -49,10 +44,7 @@ class TimesheetExportToolbarForm extends AbstractToolbarForm
|
||||
$this->addOrderBy($builder, TimesheetQuery::TIMESHEET_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => TimesheetQuery::class,
|
||||
|
||||
@@ -10,18 +10,18 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering the timesheet.
|
||||
*/
|
||||
class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
final class TimesheetToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$newOptions = [];
|
||||
if ($options['ignore_date'] === true) {
|
||||
@@ -47,10 +47,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
$this->addOrderBy($builder, TimesheetQuery::TIMESHEET_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => TimesheetQuery::class,
|
||||
|
||||
436
src/Form/Toolbar/ToolbarFormTrait.php
Normal file
436
src/Form/Toolbar/ToolbarFormTrait.php
Normal file
@@ -0,0 +1,436 @@
|
||||
<?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\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Form\Type\ActivityType;
|
||||
use App\Form\Type\BillableSearchType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Form\Type\PageSizeType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\SearchTermType;
|
||||
use App\Form\Type\TagsType;
|
||||
use App\Form\Type\TeamType;
|
||||
use App\Form\Type\UserRoleType;
|
||||
use App\Form\Type\UserType;
|
||||
use App\Form\Type\VisibilityType;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\ActivityFormTypeQuery;
|
||||
use App\Repository\Query\BaseQuery;
|
||||
use App\Repository\Query\CustomerFormTypeQuery;
|
||||
use App\Repository\Query\ProjectFormTypeQuery;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
/**
|
||||
* Defines the base form used for all toolbars.
|
||||
*
|
||||
* Extend this class and stack the elements defined here, they are coupled to each other and with the toolbar.js.
|
||||
*/
|
||||
trait ToolbarFormTrait
|
||||
{
|
||||
protected function addUserChoice(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('user', UserType::class, [
|
||||
'label' => 'user',
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addUsersChoice(FormBuilderInterface $builder, string $field = 'users', array $options = []): void
|
||||
{
|
||||
$builder->add($field, UserType::class, array_merge([
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'User ID'],
|
||||
'description' => 'Array of user IDs',
|
||||
],
|
||||
'label' => 'user',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
], $options));
|
||||
}
|
||||
|
||||
protected function addTeamChoice(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('team', TeamType::class, [
|
||||
'label' => 'team',
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addTeamsChoice(FormBuilderInterface $builder, string $field = 'teams', array $options = []): void
|
||||
{
|
||||
$builder->add($field, TeamType::class, array_merge([
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'Team ID'],
|
||||
'description' => 'Array of team IDs',
|
||||
],
|
||||
'label' => 'team',
|
||||
'multiple' => true,
|
||||
'required' => false,
|
||||
], $options));
|
||||
}
|
||||
|
||||
protected function addCustomerChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false): void
|
||||
{
|
||||
$this->addCustomerSelect($builder, $options, false, $multiProject);
|
||||
}
|
||||
|
||||
protected function addCustomerMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false): void
|
||||
{
|
||||
$this->addCustomerSelect($builder, $options, true, $multiProject);
|
||||
}
|
||||
|
||||
private function addCustomerSelect(FormBuilderInterface $builder, array $options, bool $multiCustomer, bool $multiProject): void
|
||||
{
|
||||
$name = 'customer';
|
||||
if ($multiCustomer) {
|
||||
$name = 'customers';
|
||||
}
|
||||
|
||||
// just a fake field for having this field at the right position in the frontend
|
||||
$builder->add($name, CustomerType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'Customer ID'],
|
||||
'description' => 'Array of customer IDs',
|
||||
],
|
||||
'choices' => [],
|
||||
'multiple' => $multiCustomer,
|
||||
]);
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($builder, $options, $name, $multiCustomer, $multiProject) {
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, CustomerType::class, array_merge([
|
||||
'multiple' => $multiCustomer,
|
||||
'required' => false,
|
||||
'project_enabled' => $multiCustomer ? 'customers[]' : 'customer',
|
||||
'project_select' => $multiProject ? 'projects' : 'project',
|
||||
'end_date_param' => '%daterange%',
|
||||
'start_date_param' => '%daterange%',
|
||||
'query_builder' => function (CustomerRepository $repo) use ($builder, $data, $name) {
|
||||
$query = new CustomerFormTypeQuery();
|
||||
$query->setUser($builder->getOption('user'));
|
||||
|
||||
if (\array_key_exists($name, $data) && $data[$name] !== null && $data[$name] !== '') {
|
||||
$customers = \is_array($data[$name]) ? $data[$name] : [$data[$name]];
|
||||
foreach ($customers as $customer) {
|
||||
$customer = \is_string($customer) ? (int) $customer : $customer;
|
||||
if (!\is_int($customer) && !($customer instanceof Customer)) {
|
||||
throw new \Exception('Need a customer object or an ID for customer select');
|
||||
}
|
||||
$query->addCustomer($customer);
|
||||
}
|
||||
}
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
], $options));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function addVisibilityChoice(FormBuilderInterface $builder, string $label = 'visible'): void
|
||||
{
|
||||
$builder->add('visibility', VisibilityType::class, [
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'label' => $label,
|
||||
'search' => false
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addPageSizeChoice(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('pageSize', PageSizeType::class, [
|
||||
'required' => false,
|
||||
'search' => false
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addUserRoleChoice(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('role', UserRoleType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addDateRange(FormBuilderInterface $builder, array $options, $allowEmpty = true, $required = false): void
|
||||
{
|
||||
$params = [
|
||||
'required' => $required,
|
||||
'allow_empty' => $allowEmpty,
|
||||
];
|
||||
|
||||
if (\array_key_exists('timezone', $options)) {
|
||||
$params['timezone'] = $options['timezone'];
|
||||
}
|
||||
|
||||
$builder->add('daterange', DateRangeType::class, $params);
|
||||
}
|
||||
|
||||
protected function addDateRangeChoice(FormBuilderInterface $builder, $allowEmpty = true, $required = false): void
|
||||
{
|
||||
$this->addDateRange($builder, [], $allowEmpty, $required);
|
||||
}
|
||||
|
||||
protected function addProjectChoice(FormBuilderInterface $builder, array $options = [], bool $multiCustomer = false, bool $multiActivity = false): void
|
||||
{
|
||||
$this->addProjectSelect($builder, $options, false, $multiCustomer, $multiActivity);
|
||||
}
|
||||
|
||||
protected function addProjectMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiCustomer = false, bool $multiActivity = false): void
|
||||
{
|
||||
$this->addProjectSelect($builder, $options, true, $multiCustomer, $multiActivity);
|
||||
}
|
||||
|
||||
private function addProjectSelect(FormBuilderInterface $builder, array $options, bool $multiProject, bool $multiCustomer, bool $multiActivity): void
|
||||
{
|
||||
$name = 'project';
|
||||
if ($multiProject) {
|
||||
$name = 'projects';
|
||||
}
|
||||
// just a fake field for having this field at the right position in the frontend
|
||||
$builder->add($name, ProjectType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'Project ID'],
|
||||
'description' => 'Array of project IDs',
|
||||
],
|
||||
'choices' => [],
|
||||
'multiple' => $multiProject,
|
||||
]);
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($builder, $options, $name, $multiCustomer, $multiProject, $multiActivity) {
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, ProjectType::class, array_merge([
|
||||
'multiple' => $multiProject,
|
||||
'required' => false,
|
||||
'activity_enabled' => $multiProject ? 'projects[]' : 'project',
|
||||
'activity_select' => $multiActivity ? 'activities' : 'activity',
|
||||
'query_builder' => function (ProjectRepository $repo) use ($builder, $data, $options, $multiCustomer, $multiProject) {
|
||||
$query = new ProjectFormTypeQuery();
|
||||
$query->setUser($builder->getOption('user'));
|
||||
|
||||
$name = $multiCustomer ? 'customers' : 'customer';
|
||||
if (\array_key_exists($name, $data) && $data[$name] !== null && $data[$name] !== '') {
|
||||
$customers = \is_array($data[$name]) ? $data[$name] : [$data[$name]];
|
||||
foreach ($customers as $customer) {
|
||||
$customer = \is_string($customer) ? (int) $customer : $customer;
|
||||
if (!\is_int($customer) && !($customer instanceof Customer)) {
|
||||
throw new \Exception('Need a customer object or an ID for project select');
|
||||
}
|
||||
$query->addCustomer($customer);
|
||||
}
|
||||
}
|
||||
|
||||
$name = $multiProject ? 'projects' : 'project';
|
||||
if (\array_key_exists($name, $data) && $data[$name] !== null && $data[$name] !== '') {
|
||||
$projects = \is_array($data[$name]) ? $data[$name] : [$data[$name]];
|
||||
foreach ($projects as $project) {
|
||||
$project = \is_string($project) ? (int) $project : $project;
|
||||
if (!\is_int($project) && !($project instanceof Project)) {
|
||||
throw new \Exception('Need a project object or an ID for project select');
|
||||
}
|
||||
$query->addProject($project);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['ignore_date']) && true === $options['ignore_date']) {
|
||||
$query->setIgnoreDate(true);
|
||||
}
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
], $options));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function addActivityChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false): void
|
||||
{
|
||||
$this->addActivitySelect($builder, $options, false, $multiProject);
|
||||
}
|
||||
|
||||
protected function addActivityMultiChoice(FormBuilderInterface $builder, array $options = [], bool $multiProject = false): void
|
||||
{
|
||||
$this->addActivitySelect($builder, $options, true, $multiProject);
|
||||
}
|
||||
|
||||
private function addActivitySelect(FormBuilderInterface $builder, array $options = [], bool $multiActivity = false, bool $multiProject = false): void
|
||||
{
|
||||
$name = 'activity';
|
||||
if ($multiActivity) {
|
||||
$name = 'activities';
|
||||
}
|
||||
|
||||
// just a fake field for having this field at the right position in the frontend
|
||||
$builder->add($name, ActivityType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'integer', 'description' => 'Activity ID'],
|
||||
'description' => 'Array of activity IDs',
|
||||
],
|
||||
'choices' => [],
|
||||
'multiple' => $multiActivity,
|
||||
]);
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::PRE_SUBMIT,
|
||||
function (FormEvent $event) use ($name, $multiActivity, $multiProject) {
|
||||
/** @var array<string, mixed> $data */
|
||||
$data = $event->getData();
|
||||
$event->getForm()->add($name, ActivityType::class, [
|
||||
'multiple' => $multiActivity,
|
||||
'required' => false,
|
||||
'query_builder' => function (ActivityRepository $repo) use ($data, $multiActivity, $multiProject) {
|
||||
$query = new ActivityFormTypeQuery();
|
||||
|
||||
$name = $multiActivity ? 'activities' : 'activity';
|
||||
if (\array_key_exists($name, $data) && $data[$name] !== null && $data[$name] !== '') {
|
||||
// we need to pre-fetch the activities to see if they are global, see ActivityFormTypeQuery::isGlobalsOnly()
|
||||
$activities = \is_array($data[$name]) ? $data[$name] : [$data[$name]];
|
||||
foreach ($activities as $activity) {
|
||||
$activity = \is_string($activity) ? (int) $activity : $activity;
|
||||
if (!\is_int($activity) && !($activity instanceof Activity)) {
|
||||
throw new \Exception('Need an activity object or an ID for activity select');
|
||||
}
|
||||
$query->addActivity($activity);
|
||||
}
|
||||
}
|
||||
|
||||
$name = $multiProject ? 'projects' : 'project';
|
||||
if (\array_key_exists($name, $data) && $data[$name] !== null && $data[$name] !== '') {
|
||||
$projects = \is_array($data[$name]) ? $data[$name] : [$data[$name]];
|
||||
foreach ($projects as $project) {
|
||||
$project = \is_string($project) ? (int) $project : $project;
|
||||
if (!\is_int($project) && !($project instanceof Project)) {
|
||||
throw new \Exception('Need a project object or an ID for activity select');
|
||||
}
|
||||
$query->addProject($project);
|
||||
}
|
||||
}
|
||||
|
||||
return $repo->getQueryBuilderForFormType($query);
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function addHiddenPagination(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('page', HiddenType::class, [
|
||||
'documentation' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Page number. Default: 1',
|
||||
],
|
||||
'empty_data' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addOrder(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('order', ChoiceType::class, [
|
||||
'documentation' => [
|
||||
'description' => 'The order for returned items',
|
||||
],
|
||||
'label' => 'order',
|
||||
'choices' => [
|
||||
'asc' => BaseQuery::ORDER_ASC,
|
||||
'desc' => BaseQuery::ORDER_DESC
|
||||
],
|
||||
'search' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addOrderBy(FormBuilderInterface $builder, array $allowedColumns): void
|
||||
{
|
||||
$all = [];
|
||||
foreach ($allowedColumns as $id => $name) {
|
||||
$label = \is_int($id) ? $name : $id;
|
||||
$all[$label] = $name;
|
||||
}
|
||||
$builder->add('orderBy', ChoiceType::class, [
|
||||
'label' => 'orderBy',
|
||||
'choices' => $all,
|
||||
'search' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addTagInputField(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('tags', TagsType::class, [
|
||||
'required' => false,
|
||||
'allow_create' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addSearchTermInputField(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('searchTerm', SearchTermType::class);
|
||||
}
|
||||
|
||||
protected function addTimesheetStateChoice(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('state', ChoiceType::class, [
|
||||
'label' => 'entryState',
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'search' => false,
|
||||
'choices' => [
|
||||
'all' => TimesheetQuery::STATE_ALL,
|
||||
'entryState.running' => TimesheetQuery::STATE_RUNNING,
|
||||
'entryState.stopped' => TimesheetQuery::STATE_STOPPED
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addExportStateChoice(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('exported', ChoiceType::class, [
|
||||
'label' => 'exported',
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'search' => false,
|
||||
'choices' => [
|
||||
'all' => TimesheetQuery::STATE_ALL,
|
||||
'yes' => TimesheetQuery::STATE_EXPORTED,
|
||||
'no' => TimesheetQuery::STATE_NOT_EXPORTED
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addBillableChoice(FormBuilderInterface $builder): void
|
||||
{
|
||||
$builder->add('billable', BillableSearchType::class, [
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'search' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -10,33 +10,30 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Repository\Query\UserQuery;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering the user.
|
||||
*/
|
||||
class UserToolbarForm extends AbstractToolbarForm
|
||||
final class UserToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
use ToolbarFormTrait;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addUserRoleChoice($builder);
|
||||
$this->addTeamsChoice($builder, 'searchTeams');
|
||||
$this->addVisibilityChoice($builder, 'label.active');
|
||||
$this->addVisibilityChoice($builder, 'active');
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addHiddenPagination($builder);
|
||||
$this->addOrder($builder);
|
||||
$this->addOrderBy($builder, UserQuery::USER_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => UserQuery::class,
|
||||
|
||||
Reference in New Issue
Block a user