added team permissions (#996)

This commit is contained in:
Kevin Papst
2019-08-16 01:22:33 +02:00
committed by GitHub
parent 9611646bc6
commit 561ec3b1e9
124 changed files with 4122 additions and 362 deletions

View File

@@ -67,8 +67,11 @@ class ActivityEditForm extends AbstractType
if ($options['customer']) {
$builder
->add('customer', CustomerType::class, [
'query_builder' => function (CustomerRepository $repo) use ($customer) {
return $repo->getQueryBuilderForFormType(new CustomerFormTypeQuery($customer));
'query_builder' => function (CustomerRepository $repo) use ($builder, $customer) {
$query = new CustomerFormTypeQuery($customer);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType($query);
},
'data' => $customer ? $customer : null,
'required' => false,
@@ -80,15 +83,18 @@ class ActivityEditForm extends AbstractType
$builder
->add('project', ProjectType::class, [
'required' => false,
'query_builder' => function (ProjectRepository $repo) use ($project, $customer) {
return $repo->getQueryBuilderForFormType(new ProjectFormTypeQuery($project, $customer));
'query_builder' => function (ProjectRepository $repo) use ($builder, $project, $customer) {
$query = new ProjectFormTypeQuery($project, $customer);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType($query);
},
]);
// replaces the project select after submission, to make sure only projects for the selected customer are displayed
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($project) {
function (FormEvent $event) use ($builder, $project) {
$data = $event->getData();
if (!isset($data['customer']) || empty($data['customer'])) {
return;
@@ -96,8 +102,11 @@ class ActivityEditForm extends AbstractType
$event->getForm()->add('project', ProjectType::class, [
'group_by' => null,
'query_builder' => function (ProjectRepository $repo) use ($data, $project) {
return $repo->getQueryBuilderForFormType(new ProjectFormTypeQuery($project, $data['customer']));
'query_builder' => function (ProjectRepository $repo) use ($builder, $data, $project) {
$query = new ProjectFormTypeQuery($project, $data['customer']);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType($query);
},
]);
}

View File

@@ -0,0 +1,51 @@
<?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\Entity\Customer;
use App\Form\Type\TeamType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CustomerTeamPermissionForm extends AbstractType
{
use EntityFormTrait;
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('teams', TeamType::class, [
'multiple' => true,
'expanded' => true,
'by_reference' => false,
])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Customer::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'admin_customer_teams_edit',
'attr' => [
'data-form-event' => 'kimai.customerTeamUpdate'
],
]);
}
}

View File

@@ -0,0 +1,45 @@
<?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\Extension;
use App\Entity\User;
use App\Security\CurrentUser;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class UserExtension extends AbstractTypeExtension
{
/**
* @var CurrentUser
*/
private $user;
public function __construct(CurrentUser $user)
{
$this->user = $user;
}
public static function getExtendedTypes(): iterable
{
return [FormType::class];
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefined(['user']);
// null needs to be allowed, as there is no user for anonymoud forms (like "forgot password" and "registration")
$resolver->setAllowedTypes('user', [User::class, 'null']);
$resolver->setDefault('user', $this->user->getUser());
}
}

View File

@@ -59,8 +59,11 @@ class ProjectEditForm extends AbstractType
'required' => false,
])
->add('customer', CustomerType::class, [
'query_builder' => function (CustomerRepository $repo) use ($customer) {
return $repo->getQueryBuilderForFormType(new CustomerFormTypeQuery($customer));
'query_builder' => function (CustomerRepository $repo) use ($builder, $customer) {
$query = new CustomerFormTypeQuery($customer);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType($query);
},
]);

View File

@@ -0,0 +1,51 @@
<?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\Entity\Project;
use App\Form\Type\TeamType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProjectTeamPermissionForm extends AbstractType
{
use EntityFormTrait;
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('teams', TeamType::class, [
'multiple' => true,
'expanded' => true,
'by_reference' => false,
])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Project::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'admin_project_teams_edit',
'attr' => [
'data-form-event' => 'kimai.projectTeamUpdate'
],
]);
}
}

View File

@@ -0,0 +1,51 @@
<?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\Entity\Team;
use App\Form\Type\CustomerType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TeamCustomerForm extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('customers', CustomerType::class, [
'multiple' => true,
'expanded' => true,
'by_reference' => false,
'query_builder_for_user' => false,
'required' => false,
])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Team::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'admin_team_customer',
'attr' => [
'data-form-event' => 'kimai.teamUpdate'
],
]);
}
}

61
src/Form/TeamEditForm.php Normal file
View File

@@ -0,0 +1,61 @@
<?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\Entity\Team;
use App\Form\Type\UserType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TeamEditForm extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, [
'label' => 'label.name',
'attr' => [
'autofocus' => 'autofocus'
],
])
->add('teamlead', UserType::class, [
'label' => 'label.teamlead',
'multiple' => false,
'expanded' => false,
])
->add('users', UserType::class, [
'multiple' => true,
'expanded' => true,
'by_reference' => false,
])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Team::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'admin_team_edit',
'attr' => [
'data-form-event' => 'kimai.teamUpdate'
],
]);
}
}

View File

@@ -0,0 +1,52 @@
<?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\Entity\Team;
use App\Form\Type\ProjectType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TeamProjectForm extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('projects', ProjectType::class, [
'multiple' => true,
'expanded' => false,
'by_reference' => false,
'attr' => ['size' => '20'],
'query_builder_for_user' => false,
'required' => false,
])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Team::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'admin_team_project',
'attr' => [
'data-form-event' => 'kimai.teamUpdate'
],
]);
}
}

View File

@@ -163,8 +163,11 @@ class TimesheetEditForm extends AbstractType
{
$builder
->add('customer', CustomerType::class, [
'query_builder' => function (CustomerRepository $repo) use ($customer) {
return $repo->getQueryBuilderForFormType(new CustomerFormTypeQuery($customer));
'query_builder' => function (CustomerRepository $repo) use ($builder, $customer) {
$query = new CustomerFormTypeQuery($customer);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType($query);
},
'data' => $customer ? $customer : '',
'required' => false,
@@ -189,8 +192,11 @@ class TimesheetEditForm extends AbstractType
array_merge($projectOptions, [
'placeholder' => '',
'activity_enabled' => true,
'query_builder' => function (ProjectRepository $repo) use ($project, $customer) {
return $repo->getQueryBuilderForFormType(new ProjectFormTypeQuery($project, $customer));
'query_builder' => function (ProjectRepository $repo) use ($builder, $project, $customer) {
$query = new ProjectFormTypeQuery($project, $customer);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType($query);
},
])
);
@@ -198,7 +204,7 @@ class TimesheetEditForm extends AbstractType
// replaces the project select after submission, to make sure only projects for the selected customer are displayed
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($project, $customer, $isNew) {
function (FormEvent $event) use ($builder, $project, $customer, $isNew) {
$data = $event->getData();
$customer = isset($data['customer']) && !empty($data['customer']) ? $data['customer'] : null;
$project = isset($data['project']) && !empty($data['project']) ? $data['project'] : $project;
@@ -207,7 +213,7 @@ class TimesheetEditForm extends AbstractType
'placeholder' => '',
'activity_enabled' => true,
'group_by' => null,
'query_builder' => function (ProjectRepository $repo) use ($project, $customer, $isNew) {
'query_builder' => function (ProjectRepository $repo) use ($builder, $project, $customer, $isNew) {
// is there a better wa to prevent starting a record with a hidden project ?
if ($isNew && !is_object($project)) {
/** @var Project $project */
@@ -221,8 +227,10 @@ class TimesheetEditForm extends AbstractType
}
}
}
$query = new ProjectFormTypeQuery($project, $customer);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType(new ProjectFormTypeQuery($project, $customer));
return $repo->getQueryBuilderForFormType($query);
},
]);
}

View File

@@ -66,13 +66,14 @@ abstract class AbstractToolbarForm extends AbstractType
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) {
function (FormEvent $event) use ($builder) {
$data = $event->getData();
$event->getForm()->add('customer', CustomerType::class, [
'required' => false,
'project_enabled' => true,
'query_builder' => function (CustomerRepository $repo) use ($data) {
'query_builder' => function (CustomerRepository $repo) use ($builder, $data) {
$query = new CustomerFormTypeQuery();
$query->setUser($builder->getOption('user'));
if (isset($data['customer']) && !empty($data['customer'])) {
$query->setCustomer($data['customer']);
}
@@ -138,13 +139,14 @@ abstract class AbstractToolbarForm extends AbstractType
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) {
function (FormEvent $event) use ($builder) {
$data = $event->getData();
$event->getForm()->add('project', ProjectType::class, [
'required' => false,
'activity_enabled' => true,
'query_builder' => function (ProjectRepository $repo) use ($data) {
'query_builder' => function (ProjectRepository $repo) use ($builder, $data) {
$query = new ProjectFormTypeQuery();
$query->setUser($builder->getOption('user'));
if (isset($data['customer']) && !empty($data['customer'])) {
$query->setCustomer($data['customer']);

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\TeamQuery;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TeamToolbarForm 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' => TeamQuery::class,
'csrf_protection' => false,
]);
}
}

View File

@@ -37,13 +37,22 @@ class CustomerType extends AbstractType
'label' => 'label.customer',
'class' => Customer::class,
'choice_label' => 'name',
'query_builder' => function (CustomerRepository $repo) {
return $repo->getQueryBuilderForFormType(new CustomerFormTypeQuery());
},
'query_builder_for_user' => true,
'project_enabled' => false,
'project_visibility' => ProjectQuery::SHOW_VISIBLE,
]);
$resolver->setDefault('query_builder', function (Options $options) {
return function (CustomerRepository $repo) use ($options) {
$query = new CustomerFormTypeQuery();
if (true === $options['query_builder_for_user']) {
$query->setUser($options['user']);
}
return $repo->getQueryBuilderForFormType($query);
};
});
$resolver->setDefault('api_data', function (Options $options) {
if (true === $options['project_enabled']) {
return [

View File

@@ -62,13 +62,22 @@ class ProjectType extends AbstractType
'group_by' => function (Project $project, $key, $index) {
return $project->getCustomer()->getName();
},
'query_builder' => function (ProjectRepository $repo) {
return $repo->getQueryBuilderForFormType(new ProjectFormTypeQuery());
},
'query_builder_for_user' => true,
'activity_enabled' => false,
'activity_visibility' => ActivityQuery::SHOW_VISIBLE,
]);
$resolver->setDefault('query_builder', function (Options $options) {
return function (ProjectRepository $repo) use ($options) {
$query = new ProjectFormTypeQuery();
if (true === $options['query_builder_for_user']) {
$query->setUser($options['user']);
}
return $repo->getQueryBuilderForFormType($query);
};
});
$resolver->setDefault('api_data', function (Options $options) {
if (true === $options['activity_enabled']) {
return [

View File

@@ -0,0 +1,44 @@
<?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\Entity\Team;
use App\Repository\TeamRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TeamType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => Team::class,
'label' => 'label.team',
'query_builder' => function (TeamRepository $repo) {
return $repo->createQueryBuilder('t')->orderBy('t.name', 'ASC');
},
'choice_label' => function (Team $team) {
return $team->getName();
},
]);
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return EntityType::class;
}
}

View File

@@ -17,12 +17,23 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Custom form field type to edit a user preference.
*/
class UserPreferenceType extends AbstractType
{
/**
* @var TranslatorInterface
*/
private $translate;
public function __construct(TranslatorInterface $translator)
{
$this->translate = $translator;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
@@ -54,12 +65,22 @@ class UserPreferenceType extends AbstractType
$type = HiddenType::class;
}
$event->getForm()->add('value', $type, [
'label' => 'label.' . $preference->getName(),
'constraints' => $preference->getConstraints(),
'required' => $required,
'disabled' => !$preference->isEnabled(),
]);
$transId = 'label.' . $preference->getName();
if ($this->translate->trans($transId) === $transId) {
$transId = $preference->getName();
}
$options = array_merge(
[
'label' => $transId,
'constraints' => $preference->getConstraints(),
'required' => $required,
'disabled' => !$preference->isEnabled(),
],
$preference->getOptions()
);
$event->getForm()->add('value', $type, $options);
}
);
$builder->add('name', HiddenType::class);

View File

@@ -10,8 +10,11 @@
namespace App\Form\Type;
use App\Entity\User;
use App\Repository\Query\UserFormTypeQuery;
use App\Repository\UserRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
@@ -35,6 +38,15 @@ class UserType extends AbstractType
return $user->getUsername();
},
]);
$resolver->setDefault('query_builder', function (Options $options) {
return function (UserRepository $repo) use ($options) {
$query = new UserFormTypeQuery();
$query->setUser($options['user']);
return $repo->getQueryBuilderForFormType($query);
};
});
}
/**

View File

@@ -0,0 +1,49 @@
<?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\Entity\User;
use App\Form\Type\TeamType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Defines the form used to assign a User to teams.
*/
class UserTeamsType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('teams', TeamType::class, [
'label' => 'label.team',
'multiple' => true,
'expanded' => true,
])
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'edit_user_teams',
]);
}
}