mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 22:53:49 +00:00
Merge branch '111_exports_suite' into 641_issues_with_children
This commit is contained in:
@@ -14,7 +14,7 @@ namespace Chill\MainBundle\Controller;
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\CountryRepository;
|
||||
use Chill\MainBundle\Repository\PostalCodeRepository;
|
||||
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -30,11 +30,11 @@ final class PostalCodeAPIController extends ApiController
|
||||
|
||||
private PaginatorFactory $paginatorFactory;
|
||||
|
||||
private PostalCodeRepository $postalCodeRepository;
|
||||
private PostalCodeRepositoryInterface $postalCodeRepository;
|
||||
|
||||
public function __construct(
|
||||
CountryRepository $countryRepository,
|
||||
PostalCodeRepository $postalCodeRepository,
|
||||
PostalCodeRepositoryInterface $postalCodeRepository,
|
||||
PaginatorFactory $paginatorFactory
|
||||
) {
|
||||
$this->countryRepository = $countryRepository;
|
||||
|
@@ -22,6 +22,7 @@ use Chill\MainBundle\Controller\UserController;
|
||||
use Chill\MainBundle\Controller\UserJobApiController;
|
||||
use Chill\MainBundle\Controller\UserJobController;
|
||||
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
|
||||
use Chill\MainBundle\Doctrine\DQL\Age;
|
||||
use Chill\MainBundle\Doctrine\DQL\Extract;
|
||||
use Chill\MainBundle\Doctrine\DQL\GetJsonFieldByKey;
|
||||
use Chill\MainBundle\Doctrine\DQL\JsonAggregate;
|
||||
@@ -243,6 +244,7 @@ class ChillMainExtension extends Extension implements
|
||||
'datetime_functions' => [
|
||||
'EXTRACT' => Extract::class,
|
||||
'TO_CHAR' => ToChar::class,
|
||||
'AGE' => Age::class,
|
||||
],
|
||||
],
|
||||
'hydrators' => [
|
||||
|
54
src/Bundle/ChillMainBundle/Doctrine/DQL/Age.php
Normal file
54
src/Bundle/ChillMainBundle/Doctrine/DQL/Age.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Doctrine\DQL;
|
||||
|
||||
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
|
||||
class Age extends FunctionNode
|
||||
{
|
||||
private $value1;
|
||||
|
||||
private $value2;
|
||||
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
if (null !== $this->value2) {
|
||||
return sprintf(
|
||||
'AGE(%s, %s)',
|
||||
$this->value1->dispatch($sqlWalker),
|
||||
$this->value2->dispatch($sqlWalker)
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'AGE(%s)',
|
||||
$this->value1->dispatch($sqlWalker),
|
||||
);
|
||||
}
|
||||
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->value1 = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_COMMA);
|
||||
|
||||
$this->value2 = $parser->SimpleArithmeticExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
@@ -28,6 +28,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
||||
*/
|
||||
class Scope
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="boolean", nullable=false, options={"default": true})
|
||||
*/
|
||||
private bool $active = true;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
@@ -88,6 +93,18 @@ class Scope
|
||||
return $this->roleScopes;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): Scope
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
|
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Form\Type\DataTransformer;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use function gettype;
|
||||
use function is_int;
|
||||
|
||||
class PostalCodeToIdTransformer implements DataTransformerInterface
|
||||
{
|
||||
private PostalCodeRepositoryInterface $postalCodeRepository;
|
||||
|
||||
public function __construct(PostalCodeRepositoryInterface $postalCodeRepository)
|
||||
{
|
||||
$this->postalCodeRepository = $postalCodeRepository;
|
||||
}
|
||||
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value || trim('') === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_int((int) $value)) {
|
||||
throw new TransformationFailedException('Cannot transform ' . gettype($value));
|
||||
}
|
||||
|
||||
return $this->postalCodeRepository->find((int) $value);
|
||||
}
|
||||
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof PostalCode) {
|
||||
return $value->getId();
|
||||
}
|
||||
|
||||
throw new TransformationFailedException('Could not reverseTransform ' . gettype($value));
|
||||
}
|
||||
}
|
49
src/Bundle/ChillMainBundle/Form/Type/PickPostalCodeType.php
Normal file
49
src/Bundle/ChillMainBundle/Form/Type/PickPostalCodeType.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Form\Type\DataTransformer\PostalCodeToIdTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class PickPostalCodeType extends AbstractType
|
||||
{
|
||||
private PostalCodeToIdTransformer $postalCodeToIdTransformer;
|
||||
|
||||
public function __construct(PostalCodeToIdTransformer $postalCodeToIdTransformer)
|
||||
{
|
||||
$this->postalCodeToIdTransformer = $postalCodeToIdTransformer;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addViewTransformer($this->postalCodeToIdTransformer);
|
||||
}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['uniqid'] = $view->vars['attr']['data-input-postal-code'] = uniqid('input_pick_postal_code_');
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setDefault('class', PostalCode::class)
|
||||
->setDefault('multiple', false)
|
||||
->setAllowedTypes('multiple', ['bool'])
|
||||
->setDefault('compound', false);
|
||||
}
|
||||
}
|
@@ -15,9 +15,9 @@ use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\DataMapper\ScopePickerDataMapper;
|
||||
use Chill\MainBundle\Repository\ScopeRepository;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
@@ -26,11 +26,9 @@ use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
use function array_map;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
@@ -44,47 +42,37 @@ use function count;
|
||||
*/
|
||||
class ScopePickerType extends AbstractType
|
||||
{
|
||||
protected AuthorizationHelperInterface $authorizationHelper;
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
/**
|
||||
* @var ScopeRepository
|
||||
*/
|
||||
protected $scopeRepository;
|
||||
private Security $security;
|
||||
|
||||
protected Security $security;
|
||||
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
/**
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationHelperInterface $authorizationHelper,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
ScopeRepository $scopeRepository,
|
||||
Security $security,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
$this->security = $security;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$items = $this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
$options['role'] instanceof Role ? $options['role']->getRole() : $options['role'],
|
||||
$options['center']
|
||||
$items = array_filter(
|
||||
$this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
$options['role'] instanceof Role ? $options['role']->getRole() : $options['role'],
|
||||
$options['center']
|
||||
),
|
||||
static function (Scope $s) { return $s->isActive(); }
|
||||
);
|
||||
|
||||
if (0 === count($items)) {
|
||||
throw new RuntimeException('no scopes are reachable. This form should not be shown to user');
|
||||
}
|
||||
|
||||
if (1 !== count($items)) {
|
||||
$builder->add('scope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
@@ -123,35 +111,4 @@ class ScopePickerType extends AbstractType
|
||||
->setRequired('role')
|
||||
->setAllowedTypes('role', ['string', Role::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|Center|Center[] $center
|
||||
* @param string $role
|
||||
*
|
||||
* @return \Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
protected function buildAccessibleScopeQuery($center, $role)
|
||||
{
|
||||
$roles = $this->authorizationHelper->getParentRoles($role);
|
||||
$roles[] = $role;
|
||||
$centers = $center instanceof Center ? [$center] : $center;
|
||||
|
||||
$qb = $this->scopeRepository->createQueryBuilder('s');
|
||||
$qb
|
||||
// jointure to center
|
||||
->join('s.roleScopes', 'rs')
|
||||
->join('rs.permissionsGroups', 'pg')
|
||||
->join('pg.groupCenters', 'gc')
|
||||
// add center constraint
|
||||
->where($qb->expr()->in('IDENTITY(gc.center)', ':centers'))
|
||||
->setParameter('centers', array_map(static fn (Center $c) => $c->getId(), $centers))
|
||||
// role constraints
|
||||
->andWhere($qb->expr()->in('rs.role', ':roles'))
|
||||
->setParameter('roles', $roles)
|
||||
// user contraint
|
||||
->andWhere(':user MEMBER OF gc.users')
|
||||
->setParameter('user', $this->tokenStorage->getToken()->getUser());
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
||||
|
@@ -18,10 +18,9 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use RuntimeException;
|
||||
|
||||
final class PostalCodeRepository implements ObjectRepository
|
||||
final class PostalCodeRepository implements PostalCodeRepositoryInterface
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
@@ -29,7 +28,7 @@ final class PostalCodeRepository implements ObjectRepository
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->repository = $entityManager->getRepository(PostalCode::class);
|
||||
$this->repository = $entityManager->getRepository($this->getClassName());
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
@@ -51,20 +50,11 @@ final class PostalCodeRepository implements ObjectRepository
|
||||
return $this->repository->find($id, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PostalCode[]
|
||||
*/
|
||||
public function findAll(): array
|
||||
{
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return PostalCode[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
|
||||
{
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
@@ -95,7 +85,7 @@ final class PostalCodeRepository implements ObjectRepository
|
||||
return $this->repository->findOneBy($criteria, $orderBy);
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
public function getClassName(): string
|
||||
{
|
||||
return PostalCode::class;
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\Country;
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface PostalCodeRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function countByPattern(string $pattern, ?Country $country): int;
|
||||
|
||||
public function find($id, $lockMode = null, $lockVersion = null): ?PostalCode;
|
||||
|
||||
/**
|
||||
* @return PostalCode[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return PostalCode[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
|
||||
|
||||
public function findByPattern(string $pattern, ?Country $country, ?int $start = 0, ?int $limit = 50): array;
|
||||
|
||||
public function findOneBy(array $criteria, ?array $orderBy = null): ?PostalCode;
|
||||
|
||||
public function getClassName(): string;
|
||||
}
|
@@ -43,6 +43,15 @@ final class ScopeRepository implements ScopeRepositoryInterface
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function findAllActive(): array
|
||||
{
|
||||
$qb = $this->repository->createQueryBuilder('s');
|
||||
|
||||
$qb->where('s.active = \'TRUE\'');
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
|
@@ -22,10 +22,15 @@ interface ScopeRepositoryInterface extends ObjectRepository
|
||||
public function find($id, $lockMode = null, $lockVersion = null): ?Scope;
|
||||
|
||||
/**
|
||||
* @return Scope[]
|
||||
* @return array|Scope[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @return array|Scope[]
|
||||
*/
|
||||
public function findAllActive(): array;
|
||||
|
||||
/**
|
||||
* @param null|mixed $limit
|
||||
* @param null|mixed $offset
|
||||
|
@@ -14,9 +14,8 @@ namespace Chill\MainBundle\Repository;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
class UserJobRepository implements ObjectRepository
|
||||
class UserJobRepository implements UserJobRepositoryInterface
|
||||
{
|
||||
private EntityRepository $repository;
|
||||
|
||||
@@ -38,6 +37,11 @@ class UserJobRepository implements ObjectRepository
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function findAllActive(): array
|
||||
{
|
||||
return $this->repository->findBy(['active' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
@@ -49,12 +53,12 @@ class UserJobRepository implements ObjectRepository
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
public function findOneBy(array $criteria)
|
||||
public function findOneBy(array $criteria): ?UserJob
|
||||
{
|
||||
return $this->repository->findOneBy($criteria);
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
public function getClassName(): string
|
||||
{
|
||||
return UserJob::class;
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface UserJobRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function find($id): ?UserJob;
|
||||
|
||||
/**
|
||||
* @return array|UserJob[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @return array|UserJob[]
|
||||
*/
|
||||
public function findAllActive(): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return array|object[]|UserJob[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null);
|
||||
|
||||
public function findOneBy(array $criteria): ?UserJob;
|
||||
|
||||
public function getClassName(): string;
|
||||
}
|
@@ -16,11 +16,10 @@ use Chill\MainBundle\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
use function count;
|
||||
|
||||
final class UserRepository implements ObjectRepository
|
||||
final class UserRepository implements UserRepositoryInterface
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
@@ -171,7 +170,7 @@ final class UserRepository implements ObjectRepository
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
public function getClassName(): string
|
||||
{
|
||||
return User::class;
|
||||
}
|
||||
|
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
interface UserRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function countBy(array $criteria): int;
|
||||
|
||||
public function countByActive(): int;
|
||||
|
||||
public function countByUsernameOrEmail(string $pattern): int;
|
||||
|
||||
public function find($id, $lockMode = null, $lockVersion = null): ?User;
|
||||
|
||||
/**
|
||||
* @return User[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
*
|
||||
* @return User[]
|
||||
*/
|
||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
|
||||
|
||||
/**
|
||||
* @return array|User[]
|
||||
*/
|
||||
public function findByActive(?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
public function findByUsernameOrEmail(string $pattern, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
public function findOneBy(array $criteria, ?array $orderBy = null): ?User;
|
||||
|
||||
public function findOneByUsernameOrEmail(string $pattern);
|
||||
|
||||
/**
|
||||
* Get the users having a specific flags.
|
||||
*
|
||||
* If provided, only the users amongst "filtered users" are searched. This
|
||||
* allows to make a first search amongst users based on role and center
|
||||
* and, then filter those users having some flags.
|
||||
*
|
||||
* @param \Chill\MainBundle\Entity\User[] $amongstUsers
|
||||
* @param mixed $flag
|
||||
*/
|
||||
public function findUsersHavingFlags($flag, array $amongstUsers = []): array;
|
||||
|
||||
public function getClassName(): string;
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
import { createApp } from 'vue';
|
||||
import PickPostalCode from 'ChillMainAssets/vuejs/PickPostalCode/PickPostalCode';
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
|
||||
import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
|
||||
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
||||
|
||||
const i18n = _createI18n(appMessages);
|
||||
|
||||
|
||||
function loadOnePicker(el, input, uniqId, city) {
|
||||
const app = createApp({
|
||||
template: '<pick-postal-code @select-city="onCitySelected" @removeCity="onCityRemoved" :picked="city"></pick-postal-code>',
|
||||
components: {
|
||||
PickPostalCode,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
city: city,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCitySelected(city) {
|
||||
this.city = city;
|
||||
input.value = city.id;
|
||||
},
|
||||
onCityRemoved(city) {
|
||||
this.city = null;
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
})
|
||||
.use(i18n)
|
||||
.mount(el);
|
||||
}
|
||||
|
||||
function loadDynamicPickers(element) {
|
||||
|
||||
let apps = element.querySelectorAll('[data-module="pick-postal-code"]');
|
||||
|
||||
apps.forEach(function(el) {
|
||||
|
||||
const
|
||||
uniqId = el.dataset.uniqid,
|
||||
input = document.querySelector(`input[data-input-uniqid="${uniqId}"]`),
|
||||
cityIdValue = input.value === '' ? null : input.value
|
||||
;
|
||||
|
||||
if (cityIdValue !== null) {
|
||||
makeFetch('GET', `/api/1.0/main/postal-code/${cityIdValue}.json`).then(city => {
|
||||
loadOnePicker(el, input, uniqId, city);
|
||||
})
|
||||
} else {
|
||||
loadOnePicker(el, input, uniqId, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function(e) {
|
||||
loadDynamicPickers(document)
|
||||
})
|
@@ -0,0 +1,29 @@
|
||||
# Pickpostalcode
|
||||
|
||||
Allow to pick a postal code.
|
||||
|
||||
In use with module `mod_pick_postal_code`, associated with `PickPostalCodeType` in php.
|
||||
|
||||
## Usage
|
||||
|
||||
`<pick-postal-code @select-city="onCitySelected" @removeCity="onCityRemoved" :picked="city"></pick-postal-code>`
|
||||
|
||||
## Props
|
||||
|
||||
* `picked`: the city picked. A javascript object (a city). Null if empty.
|
||||
* `country`: country to restraint search on picked. May be null.
|
||||
|
||||
## Emits
|
||||
|
||||
### `selectCity`
|
||||
|
||||
When a city is onCitySelected.
|
||||
|
||||
Argument: a js object, representing a city
|
||||
|
||||
### `removeCity`
|
||||
|
||||
When a city is removed.
|
||||
|
||||
|
||||
Argument: a js object, representing a city
|
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="PickPostalCode">
|
||||
<vue-multiselect
|
||||
id="citySelector"
|
||||
@search-change="listenInputSearch"
|
||||
ref="citySelector"
|
||||
v-model="internalPicked"
|
||||
@select="selectCity"
|
||||
@remove="remove"
|
||||
name=""
|
||||
track-by="id"
|
||||
label="value"
|
||||
:custom-label="transName"
|
||||
:placeholder="$t('select_city')"
|
||||
:select-label="$t('multiselect.select_label')"
|
||||
:deselect-label="$t('multiselect.deselect_label')"
|
||||
:selected-label="$t('multiselect.selected_label')"
|
||||
:taggable="true"
|
||||
:multiple="false"
|
||||
:internal-search="false"
|
||||
:loading="isLoading"
|
||||
:options="cities"></vue-multiselect>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import {reactive, defineProps, onMounted} from "vue";
|
||||
import {fetchCities, searchCities} from "./api";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VueMultiselect,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cities: [],
|
||||
internalPicked: null,
|
||||
isLoading: false,
|
||||
abortControllers: [],
|
||||
}
|
||||
},
|
||||
emits: ['pickCity', 'removeCity'],
|
||||
props: {
|
||||
picked: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
country: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.picked !== null) {
|
||||
this.internalPicked = this.picked;
|
||||
this.cities.push(this.picked);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
transName(value) {
|
||||
return (value.code && value.name) ? `${value.name} (${value.code})` : '';
|
||||
},
|
||||
selectCity(city) {
|
||||
this.$emit('selectCity', city);
|
||||
},
|
||||
listenInputSearch(query) {
|
||||
if (query.length <= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let c = this.abortControllers.pop();
|
||||
|
||||
while (typeof c !== 'undefined') {
|
||||
c.abort();
|
||||
c = this.abortControllers.pop();
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
let controller = new AbortController();
|
||||
this.abortControllers.push(controller);
|
||||
|
||||
searchCities(query, this.country, controller).then(
|
||||
newCities => {
|
||||
this.cities = this.cities.filter(city => city.id === this.picked);
|
||||
newCities.forEach(item => {
|
||||
this.cities.push(item);
|
||||
})
|
||||
this.isLoading = false;
|
||||
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error); //TODO better error handling
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
remove(item) {
|
||||
this.$emit('removeCity', item);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
@@ -0,0 +1,3 @@
|
||||
.PickPostalCode {
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
import {makeFetch, fetchResults} from 'ChillMainAssets/lib/api/apiMethods';
|
||||
|
||||
/**
|
||||
* Endpoint chill_api_single_postal_code__index
|
||||
* method GET, get Cities Object
|
||||
* @params {object} a country object
|
||||
* @returns {Promise} a promise containing all Postal Code objects filtered with country
|
||||
*/
|
||||
const fetchCities = (country) => {
|
||||
// warning: do not use fetchResults (in apiMethods): we need only a **part** of the results in the db
|
||||
const params = new URLSearchParams({item_per_page: 100});
|
||||
|
||||
if (country !== null) {
|
||||
params.append('country', country.id);
|
||||
}
|
||||
|
||||
return makeFetch('GET', `/api/1.0/main/postal-code.json?${params.toString()}`).then(r => Promise.resolve(r.results));
|
||||
};
|
||||
|
||||
/**
|
||||
* Endpoint chill_main_postalcodeapi_search
|
||||
* method GET, get Cities Object
|
||||
* @params {string} search a search string
|
||||
* @params {object} country a country object
|
||||
* @params {AbortController} an abort controller
|
||||
* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string
|
||||
*/
|
||||
const searchCities = (search, country, controller) => {
|
||||
const url = '/api/1.0/main/postal-code/search.json?';
|
||||
const params = new URLSearchParams({q: search});
|
||||
|
||||
if (country !== null) {
|
||||
Object.assign('country', country.id);
|
||||
}
|
||||
|
||||
return makeFetch('GET', url + params, null, {signal: controller.signal})
|
||||
.then(result => Promise.resolve(result.results));
|
||||
};
|
||||
|
||||
export {
|
||||
fetchCities,
|
||||
searchCities,
|
||||
};
|
@@ -238,3 +238,9 @@
|
||||
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
|
||||
<div data-module="pick-dynamic" data-types="{{ form.vars['types']|json_encode }}" data-multiple="{{ form.vars['multiple'] }}" data-uniqid="{{ form.vars['uniqid'] }}"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block pick_postal_code_widget %}
|
||||
{{ form_help(form)}}
|
||||
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
|
||||
<div data-module="pick-postal-code" data-uniqid="{{ form.vars['uniqid'] }}"></div>
|
||||
{% endblock %}
|
||||
|
@@ -95,6 +95,15 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if entity_workflow.currentStep.destEmail|length > 0 %}
|
||||
<p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }} :</b></p>
|
||||
<ul>
|
||||
{% for e in entity_workflow.currentStep.destEmail -%}
|
||||
<li><a href="mailto:{{ e|escape('html_attr') }}">{{ e }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if entity_workflow.currentStep.destUserByAccessKey|length > 0 %}
|
||||
<p><b>{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }} :</b></p>
|
||||
<ul>
|
||||
@@ -103,6 +112,21 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if is_granted('CHILL_MAIN_WORKFLOW_LINK_SHOW', entity_workflow) %}
|
||||
<p><b>{{ 'workflow.This link grant any user to apply a transition'|trans }} :</b></p>
|
||||
|
||||
{% set link = absolute_url(path('chill_main_workflow_grant_access_by_key', {'id': entity_workflow.currentStep.id, 'accessKey': entity_workflow.currentStep.accessKey})) %}
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" readonly value="{{ link|e('html_attr') }}" class="form-control">
|
||||
<button class="btn btn-secondary" type="button" id="button-copy" onclick="navigator.clipboard.writeText('{{ link|e('html_attr') }}').then(() => { window.alert({{ ('"' ~ 'workflow.Access link copied'|trans ~ ' !"') |e('html_attr') }})});"><i class="fa fa-files-o"></i></button>
|
||||
<a class="btn btn-secondary" type="button" id="button-email"
|
||||
href="mailto:?body={{ ((('workflow.The workflow may be accssed through this link'|trans)~':')|e('url')) ~ '%0D%0A%0D%0A' ~ link|e('url') }}"><i class="fa fa-envelope"></i></a>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@@ -81,6 +81,15 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if entity_workflow.currentStep.destEmail|length > 0 %}
|
||||
<p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }} :</b></p>
|
||||
<ul>
|
||||
{% for e in entity_workflow.currentStep.destEmail -%}
|
||||
<li><a href="mailto:{{ e|escape('html_attr') }}">{{ e }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if step.destUserByAccessKey|length > 0 %}
|
||||
<p><b>{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }} :</b></p>
|
||||
<ul>
|
||||
|
@@ -27,6 +27,8 @@ class EntityWorkflowVoter extends Voter
|
||||
|
||||
public const SEE = 'CHILL_MAIN_WORKFLOW_SEE';
|
||||
|
||||
public const SHOW_ENTITY_LINK = 'CHILL_MAIN_WORKFLOW_LINK_SHOW';
|
||||
|
||||
private EntityWorkflowManager $manager;
|
||||
|
||||
private Security $security;
|
||||
@@ -80,6 +82,19 @@ class EntityWorkflowVoter extends Voter
|
||||
case self::DELETE:
|
||||
return $subject->getStep() === 'initial';
|
||||
|
||||
case self::SHOW_ENTITY_LINK:
|
||||
if ($subject->getStep() === 'initial') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentStep = $subject->getCurrentStepChained();
|
||||
|
||||
if ($currentStep->isFinal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $currentStep->getPrevious()->getTransitionBy() === $this->security->getUser();
|
||||
|
||||
default:
|
||||
throw new UnexpectedValueException("attribute {$attribute} not supported");
|
||||
}
|
||||
@@ -91,6 +106,7 @@ class EntityWorkflowVoter extends Voter
|
||||
self::SEE,
|
||||
self::CREATE,
|
||||
self::DELETE,
|
||||
self::SHOW_ENTITY_LINK,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -108,12 +108,9 @@ abstract class AbstractAggregatorTest extends KernelTestCase
|
||||
abstract public function getQueryBuilders();
|
||||
|
||||
/**
|
||||
* Compare aliases array before and after that aggregator alter query
|
||||
* Compare aliases array before and after that aggregator alter query.
|
||||
*
|
||||
* @dataProvider dataProviderAliasDidNotDisappears
|
||||
*
|
||||
* @param QueryBuilder $qb
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function testAliasDidNotDisappears(QueryBuilder $qb, array $data)
|
||||
|
@@ -100,12 +100,9 @@ abstract class AbstractFilterTest extends KernelTestCase
|
||||
abstract public function getQueryBuilders();
|
||||
|
||||
/**
|
||||
* Compare aliases array before and after that filter alter query
|
||||
* Compare aliases array before and after that filter alter query.
|
||||
*
|
||||
* @dataProvider dataProviderAliasDidNotDisappears
|
||||
*
|
||||
* @param QueryBuilder $qb
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function testAliasDidNotDisappears(QueryBuilder $qb, array $data)
|
||||
|
@@ -17,6 +17,7 @@ namespace Chill\MainBundle\Test;
|
||||
* **Usage : ** You must set up trait with `setUpTrait` before use
|
||||
* and use tearDownTrait after usage.
|
||||
*
|
||||
* @deprecated use @see{\Prophecy\PhpUnit\ProphecyTrait} instead
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @deprecated use @class{Prophecy\PhpUnit\ProphecyTrait} instead
|
||||
|
79
src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/AgeTest.php
Normal file
79
src/Bundle/ChillMainBundle/Tests/Doctrine/DQL/AgeTest.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Tests\Doctrine\DQL;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class AgeTest extends KernelTestCase
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||
}
|
||||
|
||||
public function generateQueries(): iterable
|
||||
{
|
||||
yield [
|
||||
'SELECT AGE(a.validFrom, a.validTo) FROM ' . Address::class . ' a',
|
||||
[],
|
||||
];
|
||||
|
||||
yield [
|
||||
'SELECT AGE(:date0, :date1) FROM ' . Address::class . ' a',
|
||||
[
|
||||
'date0' => new DateTimeImmutable('now'),
|
||||
'date1' => new DateTimeImmutable('2020-01-01'),
|
||||
],
|
||||
];
|
||||
|
||||
yield [
|
||||
'SELECT AGE(a.validFrom, :date1) FROM ' . Address::class . ' a',
|
||||
[
|
||||
'date1' => new DateTimeImmutable('now'),
|
||||
],
|
||||
];
|
||||
|
||||
yield [
|
||||
'SELECT AGE(:date0, a.validFrom) FROM ' . Address::class . ' a',
|
||||
[
|
||||
'date0' => new DateTimeImmutable('now'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider generateQueries
|
||||
*/
|
||||
public function testWorking(string $dql, array $args)
|
||||
{
|
||||
$dql = $this->entityManager->createQuery($dql)->setMaxResults(3);
|
||||
|
||||
foreach ($args as $key => $value) {
|
||||
$dql->setParameter($key, $value);
|
||||
}
|
||||
|
||||
$results = $dql->getResult();
|
||||
|
||||
$this->assertIsArray($results);
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\PostalCode;
|
||||
use Chill\MainBundle\Form\Type\DataTransformer\PostalCodeToIdTransformer;
|
||||
use Chill\MainBundle\Form\Type\PickPostalCodeType;
|
||||
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use ReflectionClass;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\PreloadedExtension;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class PickPostalCodeTypeTest extends TypeTestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testSubmitValidData(): void
|
||||
{
|
||||
$builder = $this->factory->createBuilder(FormType::class, ['postal_code' => null]);
|
||||
$builder->add('postal_code', PickPostalCodeType::class);
|
||||
$form = $builder->getForm();
|
||||
|
||||
$form->submit(['postal_code' => '1']);
|
||||
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
|
||||
$this->assertEquals(1, $form['postal_code']->getData()->getId());
|
||||
}
|
||||
|
||||
protected function getExtensions()
|
||||
{
|
||||
$postalCodeRepository = $this->prophesize(PostalCodeRepositoryInterface::class);
|
||||
$postalCodeRepository->find(Argument::any())
|
||||
->will(static function ($args) {
|
||||
$postalCode = new PostalCode();
|
||||
$reflectionClass = new ReflectionClass($postalCode);
|
||||
$id = $reflectionClass->getProperty('id');
|
||||
$id->setAccessible(true);
|
||||
$id->setValue($postalCode, (int) $args[0]);
|
||||
|
||||
return $postalCode;
|
||||
});
|
||||
|
||||
$type = new PickPostalCodeType(
|
||||
new PostalCodeToIdTransformer(
|
||||
$postalCodeRepository->reveal()
|
||||
)
|
||||
);
|
||||
|
||||
return [
|
||||
new PreloadedExtension([$type], []),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\PreloadedExtension;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class ScopePickerTypeTest extends TypeTestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testBuildOneScopeIsSuccessful()
|
||||
{
|
||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||
'center' => new Center(),
|
||||
'role' => 'ONE_SCOPE',
|
||||
]);
|
||||
|
||||
$view = $form->createView();
|
||||
|
||||
$this->assertContains('hidden', $view['scope']->vars['block_prefixes']);
|
||||
}
|
||||
|
||||
public function testBuildThreeScopesIsSuccessful()
|
||||
{
|
||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||
'center' => new Center(),
|
||||
'role' => 'THREE_SCOPE',
|
||||
]);
|
||||
|
||||
$view = $form->createView();
|
||||
|
||||
$this->assertContains('entity', $view['scope']->vars['block_prefixes']);
|
||||
}
|
||||
|
||||
public function testBuildTwoScopesIsSuccessful()
|
||||
{
|
||||
$form = $this->factory->create(ScopePickerType::class, null, [
|
||||
'center' => new Center(),
|
||||
'role' => 'TWO_SCOPE',
|
||||
]);
|
||||
|
||||
$view = $form->createView();
|
||||
|
||||
$this->assertContains('entity', $view['scope']->vars['block_prefixes']);
|
||||
}
|
||||
|
||||
protected function getExtensions()
|
||||
{
|
||||
$user = new User();
|
||||
$role1Scope = 'ONE_SCOPE';
|
||||
$role2Scope = 'TWO_SCOPE';
|
||||
$role3Scope = 'THREE_SCOPE';
|
||||
$scopeA = (new Scope())->setName(['fr' => 'scope a']);
|
||||
$scopeB = (new Scope())->setName(['fr' => 'scope b']);
|
||||
$scopeC = (new Scope())->setName(['fr' => 'scope b'])->setActive(false);
|
||||
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
|
||||
$authorizationHelper->getReachableScopes($user, $role1Scope, Argument::any())
|
||||
->willReturn([$scopeA]);
|
||||
$authorizationHelper->getReachableScopes($user, $role2Scope, Argument::any())
|
||||
->willReturn([$scopeA, $scopeB]);
|
||||
$authorizationHelper->getReachableScopes($user, $role3Scope, Argument::any())
|
||||
->willReturn([$scopeA, $scopeB, $scopeC]);
|
||||
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->getUser()->willReturn($user);
|
||||
|
||||
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(
|
||||
static function ($args) { return $args[0]['fr']; }
|
||||
);
|
||||
|
||||
$type = new ScopePickerType(
|
||||
$authorizationHelper->reveal(),
|
||||
$security->reveal(),
|
||||
$translatableStringHelper->reveal()
|
||||
);
|
||||
|
||||
// add the mocks for creating EntityType
|
||||
$entityManager = DoctrineTestHelper::createTestEntityManager();
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->getClassMetadata(Scope::class)->willReturn($entityManager->getClassMetadata(Scope::class));
|
||||
$em->contains(Argument::type(Scope::class))->willReturn(true);
|
||||
$em->initializeObject(Argument::type(Scope::class))->will(static fn ($o) => $o);
|
||||
$emRevealed = $em->reveal();
|
||||
$managerRegistry = $this->prophesize(ManagerRegistry::class);
|
||||
$managerRegistry->getManager(Argument::any())->willReturn($emRevealed);
|
||||
$managerRegistry->getManagerForClass(Scope::class)->willReturn($emRevealed);
|
||||
|
||||
$entityType = $this->prophesize(EntityType::class);
|
||||
$entityType->getParent()->willReturn(ChoiceType::class);
|
||||
|
||||
return [
|
||||
new PreloadedExtension([$type], []),
|
||||
new DoctrineOrmExtension($managerRegistry->reveal()),
|
||||
];
|
||||
}
|
||||
}
|
@@ -70,6 +70,7 @@ module.exports = function(encore, entries)
|
||||
encore.addEntry('mod_entity_workflow_subscribe', __dirname + '/Resources/public/module/entity-workflow-subscribe/index.js');
|
||||
encore.addEntry('mod_entity_workflow_pick', __dirname + '/Resources/public/module/entity-workflow-pick/index.js');
|
||||
encore.addEntry('mod_wopi_link', __dirname + '/Resources/public/module/wopi-link/index.js');
|
||||
encore.addEntry('mod_pick_postal_code', __dirname + '/Resources/public/module/pick-postal-code/index.js');
|
||||
|
||||
// Vue entrypoints
|
||||
encore.addEntry('vue_address', __dirname + '/Resources/public/vuejs/Address/index.js');
|
||||
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20221010142417 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE scopes DROP active');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Allow a scope to be desactivated';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE scopes ADD active BOOLEAN DEFAULT true NOT NULL');
|
||||
}
|
||||
}
|
@@ -455,7 +455,6 @@ workflow:
|
||||
Delete workflow: Supprimer le workflow
|
||||
Steps is not waiting for transition. Maybe someone apply the transition before you ?: L'étape que vous cherchez a déjà été modifiée par un autre utilisateur. Peut-être quelqu'un a-t-il modifié cette étape avant vous ?
|
||||
You get access to this step: Vous avez acquis les droits pour appliquer une transition sur ce workflow.
|
||||
Those users are also granted to apply a transition by using an access key: Ces utilisateurs peuvent également valider cette étape, grâce à un lien d'accès
|
||||
dest by email: Liens d'autorisation par email
|
||||
dest by email help: Les adresses email mentionnées ici recevront un lien d'accès. Ce lien d'accès permettra à l'utilisateur de valider cette étape.
|
||||
Add an email: Ajouter une adresse email
|
||||
@@ -467,6 +466,11 @@ workflow:
|
||||
Previous workflow transitionned help: Workflows où vous avez exécuté une action.
|
||||
For: Pour
|
||||
You must select a next step, pick another decision if no next steps are available: Il faut une prochaine étape. Choissisez une autre décision si nécessaire.
|
||||
An access key was also sent to those addresses: Un lien d'accès a été envoyé à ces addresses
|
||||
Those users are also granted to apply a transition by using an access key: Ces utilisateurs ont obtennu l'accès grâce au lien reçu par email
|
||||
Access link copied: Lien d'accès copié
|
||||
This link grant any user to apply a transition: Le lien d'accès suivant permet d'appliquer une transition
|
||||
The workflow may be accssed through this link: Une transition peut être appliquée sur ce workflow grâce au lien d'accès suivant
|
||||
|
||||
|
||||
Subscribe final: Recevoir une notification à l'étape finale
|
||||
|
Reference in New Issue
Block a user