mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 14:43:49 +00:00
Merge remote-tracking branch 'origin/master' into track-address-reference-update
This commit is contained in:
@@ -34,6 +34,29 @@ abstract class AbstractCRUDController extends AbstractController
|
||||
*/
|
||||
protected array $crudConfig = [];
|
||||
|
||||
/**
|
||||
* get the role given from the config.
|
||||
*
|
||||
* @param mixed $entity
|
||||
* @param mixed $_format
|
||||
*/
|
||||
protected function getRoleFor(string $action, Request $request, $entity, $_format): string
|
||||
{
|
||||
$actionConfig = $this->getActionConfig($action);
|
||||
|
||||
if (null !== $actionConfig['roles'][$request->getMethod()]) {
|
||||
return $actionConfig['roles'][$request->getMethod()];
|
||||
}
|
||||
|
||||
if ($this->crudConfig['base_role']) {
|
||||
return $this->crudConfig['base_role'];
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf('the config does not have any role for the ' .
|
||||
'method %s nor a global role for the whole action. Add those to your ' .
|
||||
'configuration or override the required method', $request->getMethod()));
|
||||
}
|
||||
|
||||
public static function getSubscribedServices(): array
|
||||
{
|
||||
return array_merge(
|
||||
|
@@ -280,11 +280,13 @@ class ApiController extends AbstractCRUDController
|
||||
switch ($request->getMethod()) {
|
||||
case Request::METHOD_DELETE:
|
||||
// oups... how to use property accessor to remove element ?
|
||||
/* @phpstan-ignore-next-line as we do not find a simpler way to do this */
|
||||
$entity->{'remove' . ucfirst($property)}($postedData);
|
||||
|
||||
break;
|
||||
|
||||
case Request::METHOD_POST:
|
||||
/* @phpstan-ignore-next-line as we do not find a simpler way to do this */
|
||||
$entity->{'add' . ucfirst($property)}($postedData);
|
||||
|
||||
break;
|
||||
@@ -499,28 +501,6 @@ class ApiController extends AbstractCRUDController
|
||||
return ['groups' => ['read']];
|
||||
}
|
||||
|
||||
/**
|
||||
* get the role given from the config.
|
||||
*
|
||||
* @param mixed $entity
|
||||
* @param mixed $_format
|
||||
*/
|
||||
protected function getRoleFor(string $action, Request $request, $entity, $_format): string
|
||||
{
|
||||
$actionConfig = $this->getActionConfig($action);
|
||||
|
||||
if (null !== $actionConfig['roles'][$request->getMethod()]) {
|
||||
return $actionConfig['roles'][$request->getMethod()];
|
||||
}
|
||||
|
||||
if ($this->crudConfig['base_role']) {
|
||||
return $this->crudConfig['base_role'];
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('the config does not have any role for the ' .
|
||||
'method %s nor a global role for the whole action. Add those to your ' .
|
||||
'configuration or override the required method', $request->getMethod()));
|
||||
}
|
||||
|
||||
protected function getSerializer(): SerializerInterface
|
||||
{
|
||||
|
@@ -101,7 +101,7 @@ class CRUDRoutesLoader extends Loader
|
||||
$singleCollection = $action['single_collection'] ?? '_entity' === $name ? 'single' : null;
|
||||
|
||||
if ('collection' === $singleCollection) {
|
||||
// continue;
|
||||
// continue;
|
||||
}
|
||||
|
||||
// compute default action
|
||||
|
@@ -1,5 +1,14 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
|
@@ -1,5 +1,14 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
|
@@ -30,6 +30,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
use Symfony\Component\Workflow\TransitionBlocker;
|
||||
@@ -48,11 +49,13 @@ class WorkflowController extends AbstractController
|
||||
|
||||
private Registry $registry;
|
||||
|
||||
private Security $security;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
private ValidatorInterface $validator;
|
||||
|
||||
public function __construct(EntityWorkflowManager $entityWorkflowManager, EntityWorkflowRepository $entityWorkflowRepository, ValidatorInterface $validator, PaginatorFactory $paginatorFactory, Registry $registry, EntityManagerInterface $entityManager, TranslatorInterface $translator)
|
||||
public function __construct(EntityWorkflowManager $entityWorkflowManager, EntityWorkflowRepository $entityWorkflowRepository, ValidatorInterface $validator, PaginatorFactory $paginatorFactory, Registry $registry, EntityManagerInterface $entityManager, TranslatorInterface $translator, Security $security)
|
||||
{
|
||||
$this->entityWorkflowManager = $entityWorkflowManager;
|
||||
$this->entityWorkflowRepository = $entityWorkflowRepository;
|
||||
@@ -61,6 +64,7 @@ class WorkflowController extends AbstractController
|
||||
$this->registry = $registry;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->translator = $translator;
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,10 +295,18 @@ class WorkflowController extends AbstractController
|
||||
|
||||
if (count($workflow->getEnabledTransitions($entityWorkflow)) > 0) {
|
||||
// possible transition
|
||||
|
||||
$usersInvolved = $entityWorkflow->getUsersInvolved();
|
||||
$currentUserFound = array_search($this->security->getUser(), $usersInvolved, true);
|
||||
|
||||
if (false !== $currentUserFound) {
|
||||
unset($usersInvolved[$currentUserFound]);
|
||||
}
|
||||
|
||||
$transitionForm = $this->createForm(
|
||||
WorkflowStepType::class,
|
||||
$entityWorkflow->getCurrentStep(),
|
||||
['transition' => true, 'entity_workflow' => $entityWorkflow]
|
||||
['transition' => true, 'entity_workflow' => $entityWorkflow, 'suggested_users' => $usersInvolved]
|
||||
);
|
||||
|
||||
$transitionForm->handleRequest($request);
|
||||
|
@@ -50,8 +50,8 @@ class LoadLanguages extends AbstractFixture implements ContainerAwareInterface,
|
||||
|
||||
foreach (Intl::getLanguageBundle()->getLanguageNames() as $code => $language) {
|
||||
if (
|
||||
!in_array($code, $this->regionalVersionToInclude, true)
|
||||
&& !in_array($code, $this->ancientToExclude, true)
|
||||
!in_array($code, $this->regionalVersionToInclude, true)
|
||||
&& !in_array($code, $this->ancientToExclude, true)
|
||||
) {
|
||||
$lang = (new Language())
|
||||
->setId($code)
|
||||
|
@@ -274,7 +274,7 @@ class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->end() // end of root/children
|
||||
->end() // end of root
|
||||
;
|
||||
;
|
||||
|
||||
$rootNode->children()
|
||||
->arrayNode('add_address')->addDefaultsIfNotSet()->children()
|
||||
|
@@ -211,7 +211,7 @@ trait AddWidgetConfigurationTrait
|
||||
*
|
||||
* @throws InvalidConfigurationException if a service's tag does not have the "alias" key
|
||||
*
|
||||
* @return type
|
||||
* @return array
|
||||
*/
|
||||
protected function getWidgetAliasesbyPlace($place, ContainerBuilder $containerBuilder)
|
||||
{
|
||||
|
@@ -523,12 +523,12 @@ class Address implements TrackCreationInterface, TrackUpdateInterface
|
||||
/**
|
||||
* Update the ref status
|
||||
*
|
||||
<<<<<<< HEAD
|
||||
* <<<<<<< HEAD
|
||||
* @param Address::ADDR_REFERENCE_STATUS_* $refStatus
|
||||
* @param bool|null $updateLastUpdate Also update the "refStatusLastUpdate"
|
||||
=======
|
||||
* =======
|
||||
* The refstatuslast update is also updated
|
||||
>>>>>>> 31152616d (Feature: Provide api endpoint for reviewing addresses)
|
||||
* >>>>>>> 31152616d (Feature: Provide api endpoint for reviewing addresses)
|
||||
*/
|
||||
public function setRefStatus(string $refStatus, ?bool $updateLastUpdate = true): self
|
||||
{
|
||||
|
@@ -43,36 +43,20 @@ class Country
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var array<string, string>
|
||||
*
|
||||
* @ORM\Column(type="json")
|
||||
* @groups({"read", "docgen:read"})
|
||||
* @Context({"is-translatable": true}, groups={"docgen:read"})
|
||||
*/
|
||||
private $name;
|
||||
private array $name = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the string
|
||||
*/
|
||||
public function getCountryCode()
|
||||
public function getCountryCode(): string
|
||||
{
|
||||
return $this->countryCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
@@ -80,31 +64,23 @@ class Country
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): array
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $countryCode
|
||||
*/
|
||||
public function setCountryCode($countryCode)
|
||||
public function setCountryCode(?string $countryCode): self
|
||||
{
|
||||
$this->countryCode = $countryCode;
|
||||
$this->countryCode = (string) $countryCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Country
|
||||
* @param array<string, string> $name
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(array $name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
|
@@ -51,10 +51,7 @@ class CommentEmbeddable
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return interger $userId
|
||||
*/
|
||||
public function getUserId()
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||
use Doctrine\ORM\Event\PreFlushEventArgs;
|
||||
use Doctrine\ORM\Event\PrePersistEventArgs;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
@@ -133,7 +134,7 @@ class NotificationComment implements TrackCreationInterface, TrackUpdateInterfac
|
||||
/**
|
||||
* @ORM\PrePersist
|
||||
*/
|
||||
public function onPrePersist(LifecycleEventArgs $eventArgs): void
|
||||
public function onPrePersist(PrePersistEventArgs $eventArgs): void
|
||||
{
|
||||
$this->recentlyPersisted = true;
|
||||
}
|
||||
|
@@ -348,6 +348,23 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
|
||||
return $this->transitionningStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User[]
|
||||
*/
|
||||
public function getUsersInvolved(): array
|
||||
{
|
||||
$usersInvolved = [];
|
||||
$usersInvolved[spl_object_hash($this->getCreatedBy())] = $this->getCreatedBy();
|
||||
|
||||
foreach ($this->steps as $step) {
|
||||
foreach ($step->getDestUser() as $u) {
|
||||
$usersInvolved[spl_object_hash($u)] = $u;
|
||||
}
|
||||
}
|
||||
|
||||
return $usersInvolved;
|
||||
}
|
||||
|
||||
public function getWorkflowName(): string
|
||||
{
|
||||
return $this->workflowName;
|
||||
|
@@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Export;
|
||||
|
||||
use Doctrine\ORM\NativeQuery;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
||||
/**
|
||||
@@ -23,6 +24,7 @@ use Doctrine\ORM\QueryBuilder;
|
||||
* aggregation, use `ListInterface`.
|
||||
*
|
||||
* @example Chill\PersonBundle\Export\CountPerson an example of implementation
|
||||
* @template Q of QueryBuilder|NativeQuery
|
||||
*/
|
||||
interface ExportInterface extends ExportElementInterface
|
||||
{
|
||||
@@ -84,7 +86,7 @@ interface ExportInterface extends ExportElementInterface
|
||||
* @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR')
|
||||
* @param mixed $data The data from the export's form (as defined in `buildForm`)
|
||||
*
|
||||
* @return pure-callable(null|string|int|float|'_header' $value):string|int|\DateTimeInterface where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }`
|
||||
* @return callable(null|string|int|float|'_header' $value): string|int|\DateTimeInterface where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }`
|
||||
*/
|
||||
public function getLabels($key, array $values, $data);
|
||||
|
||||
@@ -102,7 +104,7 @@ interface ExportInterface extends ExportElementInterface
|
||||
/**
|
||||
* Return the results of the query builder.
|
||||
*
|
||||
* @param \Doctrine\ORM\NativeQuery|QueryBuilder $query
|
||||
* @param Q $query
|
||||
* @param mixed[] $data the data from the export's fomr (added by self::buildForm)
|
||||
*
|
||||
* @return mixed[] an array of results
|
||||
@@ -132,7 +134,7 @@ interface ExportInterface extends ExportElementInterface
|
||||
* @param array $acl an array where each row has a `center` key containing the Chill\MainBundle\Entity\Center, and `circles` keys containing the reachable circles. Example: `array( array('center' => $centerA, 'circles' => array($circleA, $circleB) ) )`
|
||||
* @param array $data the data from the form, if any
|
||||
*
|
||||
* @return \Doctrine\ORM\NativeQuery|QueryBuilder the query to execute.
|
||||
* @return Q the query to execute.
|
||||
*/
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []);
|
||||
|
||||
|
@@ -43,11 +43,13 @@ class AddressDataMapper implements DataMapperInterface
|
||||
/** @var FormInterface $form */
|
||||
switch ($key) {
|
||||
case 'streetAddress1':
|
||||
/** @phpstan-ignore-next-line */
|
||||
$form->setData($address->getStreetAddress1());
|
||||
|
||||
break;
|
||||
|
||||
case 'streetAddress2':
|
||||
/** @phpstan-ignore-next-line */
|
||||
$form->setData($address->getStreetAddress2());
|
||||
|
||||
break;
|
||||
@@ -110,11 +112,13 @@ class AddressDataMapper implements DataMapperInterface
|
||||
|
||||
return;
|
||||
}
|
||||
/** @phpstan-ignore-next-line */
|
||||
$address->setStreetAddress1($form->getData());
|
||||
|
||||
break;
|
||||
|
||||
case 'streetAddress2':
|
||||
/** @phpstan-ignore-next-line */
|
||||
$address->setStreetAddress2($form->getData());
|
||||
|
||||
break;
|
||||
|
@@ -24,15 +24,7 @@ class ExportPickCenterDataMapper implements DataMapperInterface
|
||||
{
|
||||
protected RegroupmentRepository $regroupmentRepository;
|
||||
|
||||
/**
|
||||
* @param array|Center[] $data
|
||||
* @param $forms
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function mapDataToForms($data, $forms)
|
||||
public function mapDataToForms($data, $forms): void
|
||||
{
|
||||
if (null === $data) {
|
||||
return;
|
||||
@@ -44,7 +36,9 @@ class ExportPickCenterDataMapper implements DataMapperInterface
|
||||
$pickedRegroupment = [];
|
||||
|
||||
foreach ($this->regroupmentRepository->findAll() as $regroupment) {
|
||||
[$contained, $notContained] = $regroupment->getCenters()->partition(static function (Center $center) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
[$contained, $notContained] = $regroupment->getCenters()->partition(static function (Center $center): bool {
|
||||
return false;
|
||||
});
|
||||
|
||||
if (0 === count($notContained)) {
|
||||
@@ -56,13 +50,7 @@ class ExportPickCenterDataMapper implements DataMapperInterface
|
||||
$form['centers']->setData($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iterable $forms
|
||||
* @param array $data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function mapFormsToData($forms, &$data)
|
||||
public function mapFormsToData($forms, &$data): void
|
||||
{
|
||||
/** @var array<string, FormInterface> $forms */
|
||||
$forms = iterator_to_array($forms);
|
||||
@@ -74,8 +62,8 @@ class ExportPickCenterDataMapper implements DataMapperInterface
|
||||
}
|
||||
|
||||
if (array_key_exists('regroupment', $forms)) {
|
||||
/** @var Regroupment $regroupment */
|
||||
foreach ($forms['regroupment']->getData() as $regroupment) {
|
||||
/** @var Regroupment $regroupment */
|
||||
foreach ($regroupment->getCenters() as $center) {
|
||||
$centers[spl_object_hash($center)] = $center;
|
||||
}
|
||||
|
@@ -42,7 +42,9 @@ class IdToEntityDataTransformer implements DataTransformerInterface
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->multiple = $multiple;
|
||||
$this->getId = $getId ?? static function (object $o) { return $o->getId(); };
|
||||
$this->getId = $getId ?? static function (object $o) {
|
||||
return $o->getId();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -56,12 +56,7 @@ class CommentType extends AbstractType
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars = array_replace(
|
||||
$view->vars,
|
||||
[
|
||||
'fullWidth' => true,
|
||||
]
|
||||
);
|
||||
$view->vars['fullWidth'] = true;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
|
@@ -31,8 +31,6 @@ class MultipleObjectsToIdTransformer implements DataTransformerInterface
|
||||
* Transforms a string (id) to an object (item).
|
||||
*
|
||||
* @param mixed $array
|
||||
*
|
||||
* @return ArrayCollection
|
||||
*/
|
||||
public function reverseTransform($array)
|
||||
{
|
||||
@@ -53,10 +51,8 @@ class MultipleObjectsToIdTransformer implements DataTransformerInterface
|
||||
* Transforms an object (use) to a string (id).
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return ArrayCollection
|
||||
*/
|
||||
public function transform($array)
|
||||
public function transform($array): array
|
||||
{
|
||||
$ret = [];
|
||||
|
||||
|
@@ -19,6 +19,7 @@ use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
@@ -28,12 +29,15 @@ class PickUserDynamicType extends AbstractType
|
||||
{
|
||||
private DenormalizerInterface $denormalizer;
|
||||
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
private SerializerInterface $serializer;
|
||||
|
||||
public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer)
|
||||
public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer, NormalizerInterface $normalizer)
|
||||
{
|
||||
$this->denormalizer = $denormalizer;
|
||||
$this->serializer = $serializer;
|
||||
$this->normalizer = $normalizer;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
@@ -46,6 +50,11 @@ class PickUserDynamicType extends AbstractType
|
||||
$view->vars['multiple'] = $options['multiple'];
|
||||
$view->vars['types'] = ['user'];
|
||||
$view->vars['uniqid'] = uniqid('pick_user_dyn');
|
||||
$view->vars['suggested'] = [];
|
||||
|
||||
foreach ($options['suggested'] as $user) {
|
||||
$view->vars['suggested'][] = $this->normalizer->normalize($user, 'json', ['groups' => 'read']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
@@ -53,7 +62,8 @@ class PickUserDynamicType extends AbstractType
|
||||
$resolver
|
||||
->setDefault('multiple', false)
|
||||
->setAllowedTypes('multiple', ['bool'])
|
||||
->setDefault('compound', false);
|
||||
->setDefault('compound', false)
|
||||
->setDefault('suggested', []);
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
|
@@ -39,7 +39,7 @@ class PrivateCommentType extends AbstractType
|
||||
$builder
|
||||
->add('comments', ChillTextareaType::class, [
|
||||
'disable_editor' => $options['disable_editor'],
|
||||
'label' => false,
|
||||
'label' => $options['label'],
|
||||
])
|
||||
->setDataMapper($this->dataMapper);
|
||||
}
|
||||
|
@@ -95,12 +95,7 @@ class ScopePickerType extends AbstractType
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars = array_replace(
|
||||
$view->vars,
|
||||
[
|
||||
'fullWidth' => true,
|
||||
]
|
||||
);
|
||||
$view->vars['fullWidth'] = true;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
|
@@ -137,8 +137,8 @@ class WorkflowStepType extends AbstractType
|
||||
$meta = $workflow->getMetadataStore()->getPlaceMetadata($to);
|
||||
|
||||
if (
|
||||
!array_key_exists('isFinal', $meta) || false === $meta['isFinal']
|
||||
) {
|
||||
!array_key_exists('isFinal', $meta) || false === $meta['isFinal']
|
||||
) {
|
||||
$toFinal = false;
|
||||
}
|
||||
}
|
||||
@@ -154,6 +154,7 @@ class WorkflowStepType extends AbstractType
|
||||
'label' => 'workflow.dest for next steps',
|
||||
'multiple' => true,
|
||||
'mapped' => false,
|
||||
'suggested' => $options['suggested_users'],
|
||||
])
|
||||
->add('future_dest_emails', ChillCollectionType::class, [
|
||||
'label' => 'workflow.dest by email',
|
||||
@@ -200,6 +201,7 @@ class WorkflowStepType extends AbstractType
|
||||
->setAllowedTypes('transition', 'bool')
|
||||
->setRequired('entity_workflow')
|
||||
->setAllowedTypes('entity_workflow', EntityWorkflow::class)
|
||||
->setDefault('suggested_users', [])
|
||||
->setDefault('constraints', [
|
||||
new Callback(
|
||||
function ($step, ExecutionContextInterface $context, $payload) {
|
||||
|
@@ -17,6 +17,8 @@ use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Repository\NotificationRepository;
|
||||
use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
|
||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||
use Doctrine\ORM\Event\PostPersistEventArgs;
|
||||
use Doctrine\ORM\Event\PostUpdateEventArgs;
|
||||
use Doctrine\ORM\Event\PreFlushEventArgs;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
@@ -68,7 +70,7 @@ final class NotificationByUserCounter implements NotificationCounterInterface
|
||||
return 'chill_main_notif_unread_by_' . $user->getId();
|
||||
}
|
||||
|
||||
public function onEditNotificationComment(NotificationComment $notificationComment, LifecycleEventArgs $eventArgs): void
|
||||
public function onEditNotificationComment(NotificationComment $notificationComment, PostPersistEventArgs $eventArgs): void
|
||||
{
|
||||
$this->resetCacheForNotification($notificationComment->getNotification());
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@ namespace Chill\MainBundle\Notification\Email;
|
||||
use Chill\MainBundle\Entity\Notification;
|
||||
use Chill\MainBundle\Entity\NotificationComment;
|
||||
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||
use Doctrine\ORM\Event\PostPersistEventArgs;
|
||||
use Doctrine\ORM\Event\PostUpdateEventArgs;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
@@ -36,7 +38,7 @@ class NotificationMailer
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function postPersistComment(NotificationComment $comment, LifecycleEventArgs $eventArgs): void
|
||||
public function postPersistComment(NotificationComment $comment, PostPersistEventArgs $eventArgs): void
|
||||
{
|
||||
foreach (
|
||||
array_merge(
|
||||
@@ -72,13 +74,13 @@ class NotificationMailer
|
||||
/**
|
||||
* Send a email after a notification is persisted.
|
||||
*/
|
||||
public function postPersistNotification(Notification $notification, LifecycleEventArgs $eventArgs): void
|
||||
public function postPersistNotification(Notification $notification, PostPersistEventArgs $eventArgs): void
|
||||
{
|
||||
$this->sendNotificationEmailsToAddresses($notification);
|
||||
$this->sendNotificationEmailsToAddressesEmails($notification);
|
||||
}
|
||||
|
||||
public function postUpdateNotification(Notification $notification, LifecycleEventArgs $eventArgs): void
|
||||
public function postUpdateNotification(Notification $notification, PostUpdateEventArgs $eventArgs): void
|
||||
{
|
||||
$this->sendNotificationEmailsToAddressesEmails($notification);
|
||||
}
|
||||
|
@@ -21,37 +21,36 @@ class Page implements PageInterface
|
||||
/**
|
||||
* the number of item per page.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $itemPerPage;
|
||||
protected int $itemPerPage;
|
||||
|
||||
/**
|
||||
* the number of the current page.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $number;
|
||||
protected int $number;
|
||||
|
||||
/**
|
||||
* The route for the current page.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $route;
|
||||
protected string $route;
|
||||
|
||||
/**
|
||||
* Parameters for the route to the current page.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routeParameters;
|
||||
protected array $routeParameters;
|
||||
|
||||
/**
|
||||
* The number of items in the whole iteration.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $totalItems;
|
||||
protected int $totalItems;
|
||||
|
||||
/**
|
||||
* @var UrlGeneratorInterface
|
||||
@@ -59,12 +58,12 @@ class Page implements PageInterface
|
||||
protected $urlGenerator;
|
||||
|
||||
public function __construct(
|
||||
$number,
|
||||
$itemPerPage,
|
||||
int $number,
|
||||
int $itemPerPage,
|
||||
UrlGeneratorInterface $urlGenerator,
|
||||
$route,
|
||||
string $route,
|
||||
array $routeParameters,
|
||||
$totalItems
|
||||
int $totalItems
|
||||
) {
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->number = $number;
|
||||
@@ -74,24 +73,24 @@ class Page implements PageInterface
|
||||
$this->totalItems = $totalItems;
|
||||
}
|
||||
|
||||
public function generateUrl()
|
||||
public function generateUrl(): string
|
||||
{
|
||||
return $this->urlGenerator->generate($this->route, $this->routeParameters);
|
||||
}
|
||||
|
||||
public function getFirstItemNumber()
|
||||
public function getFirstItemNumber(): int
|
||||
{
|
||||
return ($this->number - 1) * $this->itemPerPage;
|
||||
}
|
||||
|
||||
public function getLastItemNumber()
|
||||
public function getLastItemNumber(): int
|
||||
{
|
||||
$last = $this->number * $this->itemPerPage - 1;
|
||||
|
||||
return $last < $this->totalItems ? $last : $this->totalItems;
|
||||
}
|
||||
|
||||
public function getNumber()
|
||||
public function getNumber(): int
|
||||
{
|
||||
return $this->number;
|
||||
}
|
||||
|
@@ -27,27 +27,27 @@ class PageGenerator implements Iterator
|
||||
$this->paginator = $paginator;
|
||||
}
|
||||
|
||||
public function current()
|
||||
public function current(): Page
|
||||
{
|
||||
return $this->paginator->getPage($current);
|
||||
return $this->paginator->getPage($this->current);
|
||||
}
|
||||
|
||||
public function key()
|
||||
public function key(): int
|
||||
{
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
public function next()
|
||||
public function next(): void
|
||||
{
|
||||
++$this->current;
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->current = 1;
|
||||
}
|
||||
|
||||
public function valid()
|
||||
public function valid(): bool
|
||||
{
|
||||
return 0 < $this->current
|
||||
&& $this->paginator->countPages() >= $this->current;
|
||||
|
@@ -26,21 +26,21 @@ class Paginator implements PaginatorInterface
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $currentPageNumber;
|
||||
protected int $currentPageNumber;
|
||||
|
||||
/**
|
||||
* the number of items on a single page.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $itemPerPage;
|
||||
protected int $itemPerPage;
|
||||
|
||||
/**
|
||||
* the key in the GET parameter to indicate the number of item per page.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $itemPerPageKey;
|
||||
protected string $itemPerPageKey;
|
||||
|
||||
/**
|
||||
* the key in the GET parameter to indicate the page number in
|
||||
@@ -48,45 +48,45 @@ class Paginator implements PaginatorInterface
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pageKey;
|
||||
protected string $pageKey;
|
||||
|
||||
/**
|
||||
* the route of the pages.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $route;
|
||||
protected string $route;
|
||||
|
||||
/**
|
||||
* the parameters of the route.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $routeParameters;
|
||||
protected array $routeParameters;
|
||||
|
||||
/**
|
||||
* The number of total items.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $totalItems;
|
||||
protected int $totalItems;
|
||||
|
||||
/**
|
||||
* the generator for url.
|
||||
*
|
||||
* @var UrlGeneratorInterface
|
||||
*/
|
||||
protected $urlGenerator;
|
||||
protected UrlGeneratorInterface $urlGenerator;
|
||||
|
||||
public function __construct(
|
||||
$totalItems,
|
||||
$itemPerPage,
|
||||
$currentPageNumber,
|
||||
$route,
|
||||
int $totalItems,
|
||||
int $itemPerPage,
|
||||
int $currentPageNumber,
|
||||
string $route,
|
||||
array $routeParameters,
|
||||
UrlGeneratorInterface $urlGenerator,
|
||||
$pageKey,
|
||||
$itemPerPageKey
|
||||
string $pageKey,
|
||||
string $itemPerPageKey
|
||||
) {
|
||||
$this->totalItems = $totalItems;
|
||||
$this->itemPerPage = $itemPerPage;
|
||||
@@ -98,12 +98,12 @@ class Paginator implements PaginatorInterface
|
||||
$this->itemPerPageKey = $itemPerPageKey;
|
||||
}
|
||||
|
||||
public function count()
|
||||
public function count(): int
|
||||
{
|
||||
return $this->countPages();
|
||||
}
|
||||
|
||||
public function countPages()
|
||||
public function countPages(): int
|
||||
{
|
||||
if (0 === $this->itemPerPage) {
|
||||
return 1;
|
||||
@@ -122,20 +122,17 @@ class Paginator implements PaginatorInterface
|
||||
return 0 === $nb ? 1 : (int) $nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Chill\MainBundle\Pagination\Page
|
||||
*/
|
||||
public function getCurrentPage()
|
||||
public function getCurrentPage(): Page
|
||||
{
|
||||
return $this->getPage($this->currentPageNumber);
|
||||
}
|
||||
|
||||
public function getCurrentPageFirstItemNumber()
|
||||
public function getCurrentPageFirstItemNumber(): int
|
||||
{
|
||||
return $this->getCurrentPage()->getFirstItemNumber();
|
||||
}
|
||||
|
||||
public function getItemsPerPage()
|
||||
public function getItemsPerPage(): int
|
||||
{
|
||||
return $this->itemPerPage;
|
||||
}
|
||||
@@ -145,7 +142,7 @@ class Paginator implements PaginatorInterface
|
||||
*
|
||||
* @return \Chill\MainBundle\Pagination\Page
|
||||
*/
|
||||
public function getNextPage()
|
||||
public function getNextPage(): Page
|
||||
{
|
||||
if (!$this->hasNextPage()) {
|
||||
throw new RuntimeException('this page has no next page');
|
||||
@@ -155,11 +152,10 @@ class Paginator implements PaginatorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type $number
|
||||
*
|
||||
* @return \Chill\MainBundle\Pagination\Page
|
||||
*/
|
||||
public function getPage($number)
|
||||
public function getPage(int $number): Page
|
||||
{
|
||||
if (!$this->hasPage($number)) {
|
||||
throw new RuntimeException("The page with number {$number} does not "
|
||||
@@ -179,7 +175,7 @@ class Paginator implements PaginatorInterface
|
||||
);
|
||||
}
|
||||
|
||||
public function getPagesGenerator()
|
||||
public function getPagesGenerator(): iterable
|
||||
{
|
||||
for ($i = 1; $this->countPages() >= $i; ++$i) {
|
||||
yield $this->getPage($i);
|
||||
@@ -191,7 +187,7 @@ class Paginator implements PaginatorInterface
|
||||
*
|
||||
* @return \Chill\MainBundle\Pagination\Page
|
||||
*/
|
||||
public function getPreviousPage()
|
||||
public function getPreviousPage(): PageInterface
|
||||
{
|
||||
if (!$this->hasPreviousPage()) {
|
||||
throw new RuntimeException('this page has no previous page');
|
||||
@@ -200,7 +196,7 @@ class Paginator implements PaginatorInterface
|
||||
return $this->getPage($this->currentPageNumber - 1);
|
||||
}
|
||||
|
||||
public function getTotalItems()
|
||||
public function getTotalItems(): int
|
||||
{
|
||||
return $this->totalItems;
|
||||
}
|
||||
@@ -208,12 +204,12 @@ class Paginator implements PaginatorInterface
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNextPage()
|
||||
public function hasNextPage(): bool
|
||||
{
|
||||
return $this->hasPage($this->currentPageNumber + 1);
|
||||
}
|
||||
|
||||
public function hasPage($number)
|
||||
public function hasPage($number): bool
|
||||
{
|
||||
if (0 === $this->totalItems) {
|
||||
return 1 === $number;
|
||||
@@ -226,18 +222,18 @@ class Paginator implements PaginatorInterface
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPreviousPage()
|
||||
public function hasPreviousPage(): bool
|
||||
{
|
||||
return $this->hasPage($this->currentPageNumber - 1);
|
||||
}
|
||||
|
||||
public function isCurrentPage(PageInterface $page)
|
||||
public function isCurrentPage(PageInterface $page): bool
|
||||
{
|
||||
return $page->getNumber() === $this->currentPageNumber;
|
||||
}
|
||||
|
||||
public function setItemsPerPage($itemPerPage)
|
||||
public function setItemsPerPage(int $itemsPerPage)
|
||||
{
|
||||
$this->itemPerPage = $itemPerPage;
|
||||
$this->itemPerPage = $itemsPerPage;
|
||||
}
|
||||
}
|
||||
|
@@ -124,12 +124,12 @@ class PaginatorFactory
|
||||
return array_merge(
|
||||
$this->router->getContext()->getParameters(),
|
||||
// get the route parameters
|
||||
$this->requestStack
|
||||
->getCurrentRequest()
|
||||
->attributes->get('_route_params'),
|
||||
$this->requestStack
|
||||
->getCurrentRequest()
|
||||
->attributes->get('_route_params'),
|
||||
// get the query parameters
|
||||
$this->requestStack
|
||||
->getCurrentRequest()->query->all()
|
||||
$this->requestStack
|
||||
->getCurrentRequest()->query->all()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -32,26 +32,26 @@ interface PaginatorInterface extends Countable
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countPages();
|
||||
public function countPages(): int;
|
||||
|
||||
/**
|
||||
* get the current page.
|
||||
*
|
||||
* @return PageInterface
|
||||
*/
|
||||
public function getCurrentPage();
|
||||
public function getCurrentPage(): PageInterface;
|
||||
|
||||
/**
|
||||
* get the first result for the current page.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCurrentPageFirstItemNumber();
|
||||
public function getCurrentPageFirstItemNumber(): int;
|
||||
|
||||
/*
|
||||
* get the number of items per page
|
||||
*/
|
||||
public function getItemsPerPage();
|
||||
public function getItemsPerPage(): int;
|
||||
|
||||
/**
|
||||
* get the next page.
|
||||
@@ -60,7 +60,7 @@ interface PaginatorInterface extends Countable
|
||||
*
|
||||
* @return PageInterface
|
||||
*/
|
||||
public function getNextPage();
|
||||
public function getNextPage(): PageInterface;
|
||||
|
||||
/**
|
||||
* get page by his number.
|
||||
@@ -69,14 +69,14 @@ interface PaginatorInterface extends Countable
|
||||
*
|
||||
* @throws RuntimeException if the pagination has no page with specified number
|
||||
*/
|
||||
public function getPage($number);
|
||||
public function getPage(int $number): PageInterface;
|
||||
|
||||
/**
|
||||
* get a generator to generate pages.
|
||||
*
|
||||
* @return Generator which return PageInterface elements
|
||||
*/
|
||||
public function getPagesGenerator();
|
||||
public function getPagesGenerator(): iterable;
|
||||
|
||||
/**
|
||||
* get the previous page.
|
||||
@@ -85,35 +85,35 @@ interface PaginatorInterface extends Countable
|
||||
*
|
||||
* @return PageInterface
|
||||
*/
|
||||
public function getPreviousPage();
|
||||
public function getPreviousPage(): PageInterface;
|
||||
|
||||
/**
|
||||
* get the number of results for this paginator.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalItems();
|
||||
public function getTotalItems(): int;
|
||||
|
||||
/**
|
||||
* check if the current page has a next page.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNextPage();
|
||||
public function hasNextPage(): bool;
|
||||
|
||||
/**
|
||||
* check if the page with the given number exists.
|
||||
*
|
||||
* @param int $number
|
||||
* @param mixed $number
|
||||
*/
|
||||
public function hasPage($number);
|
||||
public function hasPage($number): bool;
|
||||
|
||||
/**
|
||||
* check if the current page has a page before.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPreviousPage();
|
||||
public function hasPreviousPage(): bool;
|
||||
|
||||
/**
|
||||
* check if the given page is the current page.
|
||||
@@ -122,10 +122,10 @@ interface PaginatorInterface extends Countable
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCurrentPage(PageInterface $page);
|
||||
public function isCurrentPage(PageInterface $page): bool;
|
||||
|
||||
/*
|
||||
* set the number of items per page
|
||||
*/
|
||||
public function setItemsPerPage($itemsPerPage);
|
||||
public function setItemsPerPage(int $itemsPerPage);
|
||||
}
|
||||
|
@@ -187,7 +187,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
|
||||
}
|
||||
|
||||
// filter only number
|
||||
$filtered = preg_replace('/[^0-9]/', '', $phonenumber);
|
||||
$filtered = preg_replace('/[^0-9]/', '', (string) $phonenumber);
|
||||
|
||||
$item = $this->cachePool->getItem('pnum_' . $filtered);
|
||||
|
||||
|
@@ -62,7 +62,7 @@ final class GeographicalUnitRepository implements GeographicalUnitRepositoryInte
|
||||
->innerJoin(Address::class, 'address', Join::WITH, 'ST_CONTAINS(gu.geom, address.point) = TRUE')
|
||||
->where($qb->expr()->eq('address', ':address'))
|
||||
->setParameter('address', $address)
|
||||
;
|
||||
;
|
||||
}
|
||||
|
||||
public function find($id): ?GeographicalUnit
|
||||
|
@@ -18,12 +18,13 @@ function loadDynamicPicker(element) {
|
||||
isMultiple = parseInt(el.dataset.multiple) === 1,
|
||||
uniqId = el.dataset.uniqid,
|
||||
input = element.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'),
|
||||
// the "picked" will always be an array, even if multiple is false
|
||||
picked = isMultiple ?
|
||||
JSON.parse(input.value) : (
|
||||
(input.value === '[]' || input.value === '') ?
|
||||
null : [ JSON.parse(input.value) ]
|
||||
)
|
||||
;
|
||||
suggested = JSON.parse(el.dataset.suggested)
|
||||
|
||||
if (!isMultiple) {
|
||||
if (input.value === '[]'){
|
||||
@@ -37,6 +38,7 @@ function loadDynamicPicker(element) {
|
||||
':types="types" ' +
|
||||
':picked="picked" ' +
|
||||
':uniqid="uniqid" ' +
|
||||
':suggested="notPickedSuggested" ' +
|
||||
'@addNewEntity="addNewEntity" ' +
|
||||
'@removeEntity="removeEntity"></pick-entity>',
|
||||
components: {
|
||||
@@ -48,16 +50,27 @@ function loadDynamicPicker(element) {
|
||||
types: JSON.parse(el.dataset.types),
|
||||
picked: picked === null ? [] : picked,
|
||||
uniqid: el.dataset.uniqid,
|
||||
suggested: suggested
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
notPickedSuggested() {
|
||||
const pickedIds = new Set();
|
||||
for (const p of this.picked) {
|
||||
pickedIds.add(`${p.type}${p.id}`);
|
||||
}
|
||||
return this.suggested.filter(e => !pickedIds.has(`${e.type}${e.id}`))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addNewEntity(entity) {
|
||||
addNewEntity({entity}) {
|
||||
if (this.multiple) {
|
||||
if (!this.picked.some(el => {
|
||||
return el.type === entity.type && el.id === entity.id;
|
||||
})) {
|
||||
this.picked.push(entity);
|
||||
input.value = JSON.stringify(this.picked);
|
||||
console.log(entity)
|
||||
}
|
||||
} else {
|
||||
if (!this.picked.some(el => {
|
||||
@@ -69,9 +82,16 @@ function loadDynamicPicker(element) {
|
||||
}
|
||||
}
|
||||
},
|
||||
removeEntity(entity) {
|
||||
removeEntity({entity}) {
|
||||
if (-1 === this.suggested.findIndex(e => e.type === entity.type && e.id === entity.id)) {
|
||||
this.suggested.push(entity);
|
||||
}
|
||||
this.picked = this.picked.filter(e => !(e.type === entity.type && e.id === entity.id));
|
||||
input.value = JSON.stringify(this.picked);
|
||||
if (this.multiple) {
|
||||
input.value = JSON.stringify(this.picked);
|
||||
} else {
|
||||
input.value = "";
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@@ -17,6 +17,9 @@
|
||||
</add-persons>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li v-for="s in suggested" :key="s.id" @click="addNewSuggested(s)"><span>{{ s.text }}</span></li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -49,6 +52,10 @@ export default {
|
||||
// display picked entities.
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
suggested: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
emits: ['addNewEntity', 'removeEntity'],
|
||||
@@ -61,55 +68,58 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
addPersonsOptions() {
|
||||
return {
|
||||
uniq: !this.multiple,
|
||||
type: this.types,
|
||||
priority: null,
|
||||
button: {
|
||||
size: 'btn-sm',
|
||||
class: 'btn-submit',
|
||||
},
|
||||
};
|
||||
},
|
||||
translatedListOfTypes() {
|
||||
let trans = [];
|
||||
this.types.forEach(t => {
|
||||
if (this.$props.multiple) {
|
||||
trans.push(appMessages.fr.pick_entity[t].toLowerCase());
|
||||
} else {
|
||||
trans.push(appMessages.fr.pick_entity[t + '_one'].toLowerCase());
|
||||
}
|
||||
})
|
||||
addPersonsOptions() {
|
||||
return {
|
||||
uniq: !this.multiple,
|
||||
type: this.types,
|
||||
priority: null,
|
||||
button: {
|
||||
size: 'btn-sm',
|
||||
class: 'btn-submit',
|
||||
},
|
||||
};
|
||||
},
|
||||
translatedListOfTypes() {
|
||||
let trans = [];
|
||||
this.types.forEach(t => {
|
||||
if (this.$props.multiple) {
|
||||
trans.push(appMessages.fr.pick_entity[t].toLowerCase());
|
||||
} else {
|
||||
trans.push(appMessages.fr.pick_entity[t + '_one'].toLowerCase());
|
||||
}
|
||||
})
|
||||
|
||||
if (this.$props.multiple) {
|
||||
return appMessages.fr.pick_entity.modal_title + trans.join(', ');
|
||||
} else {
|
||||
return appMessages.fr.pick_entity.modal_title_one + trans.join(', ');
|
||||
}
|
||||
},
|
||||
listClasses() {
|
||||
return {
|
||||
'list-suggest': true,
|
||||
'remove-items': this.$props.removableIfSet,
|
||||
};
|
||||
},
|
||||
if (this.$props.multiple) {
|
||||
return appMessages.fr.pick_entity.modal_title + trans.join(', ');
|
||||
} else {
|
||||
return appMessages.fr.pick_entity.modal_title_one + trans.join(', ');
|
||||
}
|
||||
},
|
||||
listClasses() {
|
||||
return {
|
||||
'list-suggest': true,
|
||||
'remove-items': this.$props.removableIfSet,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addNewEntity({ selected, modal }) {
|
||||
selected.forEach((item) => {
|
||||
this.$emit('addNewEntity', item.result);
|
||||
}, this
|
||||
);
|
||||
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||
modal.showModal = false;
|
||||
},
|
||||
removeEntity(entity) {
|
||||
if (!this.$props.removableIfSet) {
|
||||
return;
|
||||
}
|
||||
this.$emit('removeEntity', entity);
|
||||
}
|
||||
addNewSuggested(entity) {
|
||||
this.$emit('addNewEntity', {entity: entity});
|
||||
},
|
||||
addNewEntity({ selected, modal }) {
|
||||
selected.forEach((item) => {
|
||||
this.$emit('addNewEntity', { entity: item.result});
|
||||
}, this
|
||||
);
|
||||
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||
modal.showModal = false;
|
||||
},
|
||||
removeEntity(entity) {
|
||||
if (!this.$props.removableIfSet) {
|
||||
return;
|
||||
}
|
||||
this.$emit('removeEntity',{ entity: entity });
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@@ -68,7 +68,8 @@
|
||||
{{- form_errors(form) -}}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-sm">
|
||||
<div class="col-12 clear">{{- form_label(form) -}}</div>
|
||||
<div class="col-sm-12">
|
||||
{{- form_widget(form, widget_attr) -}}
|
||||
{{- form_help(form) -}}
|
||||
{{- form_errors(form) -}}
|
||||
|
@@ -18,43 +18,48 @@
|
||||
|
||||
{% block form_row %}
|
||||
{% apply spaceless %}
|
||||
{% if form.vars.fullWidth is not defined or form.vars.fullWidth == false %}
|
||||
<div class="mb-2">
|
||||
<div class="row">
|
||||
<div class="{% apply spaceless %}
|
||||
{% if attr.class is defined and ('cf-title' in attr.class or 'cf-fields' in attr.class ) %}
|
||||
col-sm-12
|
||||
{% elseif attr.class is defined and 'multiple-cf-inline' in attr.class %}
|
||||
col-sm-2 col-md-4 clear
|
||||
{% else %}
|
||||
col-sm-4 clear
|
||||
{% if form.vars.fullWidth is not defined or form.vars.fullWidth == false %}
|
||||
<div class="{% apply spaceless %}
|
||||
{% if attr.class is defined and ('cf-title' in attr.class or 'cf-fields' in attr.class ) %}
|
||||
col-sm-12
|
||||
{% elseif attr.class is defined and 'multiple-cf-inline' in attr.class %}
|
||||
col-sm-2 col-md-4 clear
|
||||
{% else %}
|
||||
col-sm-4 clear
|
||||
{% endif %}
|
||||
{% endapply %}">
|
||||
{% if attr.class is not defined or ('cf-title' not in attr.class and 'cf-fields' not in attr.class ) %}
|
||||
{{ form_label(form) }}
|
||||
{% endif %}
|
||||
{% endapply %}">
|
||||
{% if attr.class is not defined or ('cf-title' not in attr.class and 'cf-fields' not in attr.class ) %}
|
||||
{{ form_label(form) }}
|
||||
</div>
|
||||
<div class="{% apply spaceless %}
|
||||
{% if attr.class is defined and 'cf-title' in attr.class %}
|
||||
col-sm-12
|
||||
{% elseif attr.class is defined and 'cf-fields' in attr.class %}
|
||||
col-sm-12 parent
|
||||
{% elseif attr.class is defined and 'multiple-cf-inline' in attr.class %}
|
||||
col-sm-2 col-md-8 multiple-cf-inline
|
||||
{% else %}
|
||||
col-sm-8
|
||||
{% endif %}
|
||||
{% endapply %}">
|
||||
{{ form_widget(form) }}
|
||||
{{ form_errors(form) }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-12 clear">{{ form_label(form) }}</div>
|
||||
<div class="col-12">{{ form_widget(form) }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="{% apply spaceless %}
|
||||
{% if attr.class is defined and 'cf-title' in attr.class %}
|
||||
col-sm-12
|
||||
{% elseif attr.class is defined and 'cf-fields' in attr.class %}
|
||||
col-sm-12 parent
|
||||
{% elseif attr.class is defined and 'multiple-cf-inline' in attr.class %}
|
||||
col-sm-2 col-md-8 multiple-cf-inline
|
||||
{% else %}
|
||||
col-sm-8
|
||||
{% endif %}
|
||||
{% endapply %}">
|
||||
{{ form_widget(form) }}
|
||||
{{ form_errors(form) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ form_widget(form) }}
|
||||
{% endif %}
|
||||
{% endapply %}
|
||||
{% endblock form_row %}
|
||||
{#
|
||||
The block 'form_row' above may be removed !
|
||||
Read this note: https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/502#note_1311993084
|
||||
#}
|
||||
|
||||
{% block choice_widget_expanded %}
|
||||
{% apply spaceless %}
|
||||
@@ -200,7 +205,6 @@
|
||||
|
||||
|
||||
{% block private_comment_row %}
|
||||
{{ form_label(form) }}
|
||||
{{ form_row(form) }}
|
||||
{% endblock %}
|
||||
|
||||
@@ -211,7 +215,6 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block comment_row %}
|
||||
{{ form_label(form) }}
|
||||
{{ form_row(form) }}
|
||||
{% endblock %}
|
||||
|
||||
@@ -249,7 +252,11 @@
|
||||
|
||||
{% block pick_entity_dynamic_widget %}
|
||||
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value|escape('html_attr') }}" {% 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>
|
||||
<div data-module="pick-dynamic"
|
||||
data-types="{{ form.vars['types']|json_encode }}"
|
||||
data-multiple="{{ form.vars['multiple'] }}"
|
||||
data-uniqid="{{ form.vars['uniqid'] }}"
|
||||
data-suggested="{{ form.vars['suggested']|json_encode|escape('html_attr') }}"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block pick_postal_code_widget %}
|
||||
@@ -269,4 +276,4 @@
|
||||
{{ form_errors(form.fixedDate) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
@@ -66,6 +66,7 @@
|
||||
{{ form_row(transition_form.future_dest_users) }}
|
||||
|
||||
{{ form_row(transition_form.future_dest_emails) }}
|
||||
{{ form_errors(transition_form.future_dest_users) }}
|
||||
</div>
|
||||
|
||||
<p>{{ form_label(transition_form.comment) }}</p>
|
||||
|
@@ -13,8 +13,18 @@ namespace Chill\MainBundle\Routing;
|
||||
|
||||
use Knp\Menu\MenuItem;
|
||||
|
||||
/**
|
||||
* Implements a builder for menu
|
||||
*
|
||||
* @template TParams of array
|
||||
*/
|
||||
interface LocalMenuBuilderInterface
|
||||
{
|
||||
/**
|
||||
* @param $menuId
|
||||
* @param MenuItem $menu
|
||||
* @param TParams $parameters
|
||||
*/
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters);
|
||||
|
||||
/**
|
||||
|
@@ -11,8 +11,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Chill\MainBundle\Pagination\Paginator;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -30,7 +30,7 @@ class SearchApi
|
||||
|
||||
private PaginatorFactory $paginator;
|
||||
|
||||
private iterable $providers = [];
|
||||
private iterable $providers;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
@@ -42,9 +42,6 @@ class SearchApi
|
||||
$this->paginator = $paginator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Model/Result[]
|
||||
*/
|
||||
public function getResults(string $pattern, array $types, array $parameters): Collection
|
||||
{
|
||||
$queries = $this->findQueries($pattern, $types, $parameters);
|
||||
@@ -53,10 +50,10 @@ class SearchApi
|
||||
throw new SearchApiNoQueryException($pattern, $types, $parameters);
|
||||
}
|
||||
|
||||
$total = $this->countItems($queries, $types, $parameters);
|
||||
$total = $this->countItems($queries);
|
||||
$paginator = $this->paginator->create($total);
|
||||
|
||||
$rawResults = $this->fetchRawResult($queries, $types, $parameters, $paginator);
|
||||
$rawResults = $this->fetchRawResult($queries, $types, $paginator);
|
||||
|
||||
$this->prepareProviders($rawResults);
|
||||
$results = $this->buildResults($rawResults);
|
||||
@@ -64,7 +61,7 @@ class SearchApi
|
||||
return new Collection($results, $paginator);
|
||||
}
|
||||
|
||||
private function buildCountQuery(array $queries, $types, $parameters)
|
||||
private function buildCountQuery(array $queries): array
|
||||
{
|
||||
$query = 'SELECT SUM(c) AS count FROM ({union_unordered}) AS sq';
|
||||
$unions = [];
|
||||
@@ -88,7 +85,7 @@ class SearchApi
|
||||
$items = [];
|
||||
|
||||
foreach ($rawResults as $r) {
|
||||
foreach ($this->providers as $k => $p) {
|
||||
foreach ($this->providers as $p) {
|
||||
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
||||
$items[] = (new SearchApiResult($r['pertinence']))
|
||||
->setResult(
|
||||
@@ -103,7 +100,7 @@ class SearchApi
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function buildUnionQuery(array $queries, $types, $parameters, Paginator $paginator)
|
||||
private function buildUnionQuery(array $queries, PaginatorInterface $paginator): array
|
||||
{
|
||||
$query = '{unions} ORDER BY pertinence DESC LIMIT ? OFFSET ?';
|
||||
$unions = [];
|
||||
@@ -126,9 +123,9 @@ class SearchApi
|
||||
];
|
||||
}
|
||||
|
||||
private function countItems($providers, $types, $parameters): int
|
||||
private function countItems($providers): int
|
||||
{
|
||||
[$countQuery, $parameters] = $this->buildCountQuery($providers, $types, $parameters);
|
||||
[$countQuery, $parameters] = $this->buildCountQuery($providers);
|
||||
$rsmCount = new ResultSetMappingBuilder($this->em);
|
||||
$rsmCount->addScalarResult('count', 'count');
|
||||
$countNq = $this->em->createNativeQuery($countQuery, $rsmCount);
|
||||
@@ -137,9 +134,9 @@ class SearchApi
|
||||
return (int) $countNq->getSingleScalarResult();
|
||||
}
|
||||
|
||||
private function fetchRawResult($queries, $types, $parameters, Paginator $paginator): array
|
||||
private function fetchRawResult($queries, $types, PaginatorInterface $paginator): array
|
||||
{
|
||||
[$union, $parameters] = $this->buildUnionQuery($queries, $types, $parameters, $paginator);
|
||||
[$union, $parameters] = $this->buildUnionQuery($queries, $paginator);
|
||||
$rsm = new ResultSetMappingBuilder($this->em);
|
||||
$rsm->addScalarResult('key', 'key', Types::STRING)
|
||||
->addScalarResult('metadata', 'metadata', Types::JSON)
|
||||
@@ -172,7 +169,7 @@ class SearchApi
|
||||
);
|
||||
}
|
||||
|
||||
private function prepareProviders(array $rawResults)
|
||||
private function prepareProviders(array $rawResults): void
|
||||
{
|
||||
$metadatas = [];
|
||||
$providers = [];
|
||||
|
@@ -16,6 +16,18 @@ use function count;
|
||||
use function implode;
|
||||
use function strtr;
|
||||
|
||||
/**
|
||||
* This create a query optimized for searching for the api response.
|
||||
*
|
||||
* When build, this class generate a SQL string and a list of a parameters which is suitable for running
|
||||
* a native SQL query. This have usually the form of
|
||||
*
|
||||
* `SELECT '<key>' as key, <metadata> as metadata, <pertinence> as pertinence FROM <from clause> WHERE <where clause>`.
|
||||
*
|
||||
* The clause between `<>` are provided through the dedicated method in this class (@link{self::setSelectKey},
|
||||
* @link{self::setFromClause}), etc.).
|
||||
*
|
||||
*/
|
||||
class SearchApiQuery
|
||||
{
|
||||
private ?string $fromClause = null;
|
||||
|
@@ -71,9 +71,9 @@ interface SearchInterface
|
||||
* @param array $terms the string to search
|
||||
* @param int $start the first result (for pagination)
|
||||
* @param int $limit the number of result (for pagination)
|
||||
* @param string $format The format for result
|
||||
* @param "html"|"json" $format The format for result
|
||||
*
|
||||
* @return string, an HTML string
|
||||
* @return string|array a string if format is html, an array if format is json
|
||||
*/
|
||||
public function renderResult(array $terms, $start = 0, $limit = 50, array $options = [], $format = 'html');
|
||||
|
||||
|
@@ -26,28 +26,4 @@ use const E_USER_DEPRECATED;
|
||||
*/
|
||||
abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
||||
{
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
@trigger_error(
|
||||
'This voter should implements the new `supports` '
|
||||
. 'methods introduced by Symfony 3.0, and do not rely on '
|
||||
. 'getSupportedAttributes and getSupportedClasses methods.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
// @TODO: getSupportedAttributes() should be created in here and made abstract or in ChillVoterInterface.
|
||||
// @TODO: getSupportedClasses() should be created in here and made abstract or in ChillVoterInterface.
|
||||
return in_array($attribute, $this->getSupportedAttributes($attribute), true)
|
||||
&& in_array(get_class($subject), $this->getSupportedClasses(), true);
|
||||
}
|
||||
|
||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||
{
|
||||
@trigger_error('This voter should implements the new `voteOnAttribute` '
|
||||
. 'methods introduced by Symfony 3.0, and do not rely on '
|
||||
. 'isGranted method', E_USER_DEPRECATED);
|
||||
|
||||
// @TODO: isGranted() should be created in here and made abstract or in ChillVoterInterface.
|
||||
return $this->isGranted($attribute, $subject, $token->getUser());
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface;
|
||||
use Chill\MainBundle\Security\ParentRoleHelper;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
@@ -34,7 +34,7 @@ use function get_class;
|
||||
*/
|
||||
class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
{
|
||||
private CenterResolverDispatcherInterface $centerResolverDispatcher;
|
||||
private CenterResolverManagerInterface $centerResolverManager;
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
@@ -45,13 +45,13 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
private UserACLAwareRepositoryInterface $userACLAwareRepository;
|
||||
|
||||
public function __construct(
|
||||
CenterResolverDispatcherInterface $centerResolverDispatcher,
|
||||
CenterResolverManagerInterface $centerResolverManager,
|
||||
LoggerInterface $logger,
|
||||
ScopeResolverDispatcher $scopeResolverDispatcher,
|
||||
UserACLAwareRepositoryInterface $userACLAwareRepository,
|
||||
ParentRoleHelper $parentRoleHelper
|
||||
) {
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
$this->centerResolverManager = $centerResolverManager;
|
||||
$this->logger = $logger;
|
||||
$this->scopeResolverDispatcher = $scopeResolverDispatcher;
|
||||
$this->userACLAwareRepository = $userACLAwareRepository;
|
||||
@@ -63,7 +63,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
*
|
||||
* @param User $user The user
|
||||
* @param array $centers a list of centers which are going to be filtered
|
||||
* @param Center|string $role
|
||||
* @param mixed $role
|
||||
*/
|
||||
public function filterReachableCenters(User $user, array $centers, $role): array
|
||||
{
|
||||
@@ -113,13 +113,14 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
* Get reachable Centers for the given user, role,
|
||||
* and optionally Scope.
|
||||
*
|
||||
* @return array|Center[]
|
||||
* @return list<Center>
|
||||
*/
|
||||
public function getReachableCenters(UserInterface $user, string $role, ?Scope $scope = null): array
|
||||
{
|
||||
if ($role instanceof Role) {
|
||||
$role = $role->getRole();
|
||||
}
|
||||
/** @var array<string, Center> $centers */
|
||||
$centers = [];
|
||||
|
||||
foreach ($user->getGroupCenters() as $groupCenter) {
|
||||
@@ -129,13 +130,13 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
//check that the role is in the reachable roles
|
||||
if ($this->isRoleReached($role, $roleScope->getRole())) {
|
||||
if (null === $scope) {
|
||||
$centers[] = $groupCenter->getCenter();
|
||||
$centers[spl_object_hash($groupCenter->getCenter())] = $groupCenter->getCenter();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($scope->getId() === $roleScope->getScope()->getId()) {
|
||||
$centers[] = $groupCenter->getCenter();
|
||||
$centers[spl_object_hash($groupCenter->getCenter())] = $groupCenter->getCenter();
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -143,7 +144,7 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
}
|
||||
}
|
||||
|
||||
return $centers;
|
||||
return array_values($centers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,12 +195,8 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
*
|
||||
* @return array|Scope[]
|
||||
*/
|
||||
public function getReachableScopes(UserInterface $user, string $role, $center): array
|
||||
public function getReachableScopes(UserInterface $user, string $role, Center|array $center): array
|
||||
{
|
||||
if ($role instanceof Role) {
|
||||
$role = $role->getRole();
|
||||
}
|
||||
|
||||
return $this->getReachableCircles($user, $role, $center);
|
||||
}
|
||||
|
||||
@@ -252,27 +249,15 @@ class AuthorizationHelper implements AuthorizationHelperInterface
|
||||
*/
|
||||
public function userHasAccess(User $user, $entity, $attribute)
|
||||
{
|
||||
$center = $this->centerResolverDispatcher->resolveCenter($entity);
|
||||
$centers = $this->centerResolverManager->resolveCenters($entity);
|
||||
|
||||
if (is_iterable($center)) {
|
||||
foreach ($center as $c) {
|
||||
if ($this->userHasAccessForCenter($user, $c, $entity, $attribute)) {
|
||||
return true;
|
||||
}
|
||||
foreach ($centers as $c) {
|
||||
if ($this->userHasAccessForCenter($user, $c, $entity, $attribute)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($center instanceof Center) {
|
||||
return $this->userHasAccessForCenter($user, $center, $entity, $attribute);
|
||||
}
|
||||
|
||||
if (null === $center) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new UnexpectedValueException('could not resolver a center');
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -21,12 +21,12 @@ interface AuthorizationHelperInterface
|
||||
* Get reachable Centers for the given user, role,
|
||||
* and optionnaly Scope.
|
||||
*
|
||||
* @return Center[]
|
||||
* @return list<Center>
|
||||
*/
|
||||
public function getReachableCenters(UserInterface $user, string $role, ?Scope $scope = null): array;
|
||||
|
||||
/**
|
||||
* @param array|Center|Center[] $center
|
||||
* @param Center|list<Center> $center
|
||||
*/
|
||||
public function getReachableScopes(UserInterface $user, string $role, $center): array;
|
||||
public function getReachableScopes(UserInterface $user, string $role, Center|array $center): array;
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ class Counter implements JsonSerializable
|
||||
return $this->counter;
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return ['count' => $this->counter];
|
||||
}
|
||||
|
@@ -98,8 +98,11 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw
|
||||
$data['validFrom'] = $address->getValidFrom();
|
||||
$data['validTo'] = $address->getValidTo();
|
||||
$data['refStatus'] = $address->getRefStatus();
|
||||
$data['point'] = $this->normalizer->normalize($address->getPoint(), $format,
|
||||
[AbstractNormalizer::GROUPS => ['read']]);
|
||||
$data['point'] = $this->normalizer->normalize(
|
||||
$address->getPoint(),
|
||||
$format,
|
||||
[AbstractNormalizer::GROUPS => ['read']]
|
||||
);
|
||||
$data['isNoAddress'] = $address->isNoAddress();
|
||||
} elseif ('docgen' === $format) {
|
||||
$dateContext = array_merge($context, ['docgen:expects' => DateTimeInterface::class]);
|
||||
|
@@ -1,5 +1,14 @@
|
||||
<?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\Service\Import;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
@@ -18,7 +27,7 @@ final class AddressToReferenceMatcher
|
||||
|
||||
private const LOG_PREFIX = '[address_to_reference_matcher] ';
|
||||
|
||||
private const SQL_MARK_TO_REVIEW_ADDRESS_UNMATCHING = <<<SQL
|
||||
private const SQL_MARK_TO_REVIEW_ADDRESS_UNMATCHING = <<<'SQL'
|
||||
UPDATE chill_main_address a SET refstatus = '{{ to_review }}', refstatuslastupdate = NOW()
|
||||
FROM chill_main_address_reference ar
|
||||
WHERE
|
||||
@@ -38,7 +47,7 @@ final class AddressToReferenceMatcher
|
||||
)
|
||||
SQL;
|
||||
|
||||
private const SQL_MARK_MATCHING_ADDRESSES_REVIEWED_OR_TO_REVIEW = <<<SQL
|
||||
private const SQL_MARK_MATCHING_ADDRESSES_REVIEWED_OR_TO_REVIEW = <<<'SQL'
|
||||
UPDATE chill_main_address a SET refstatus = '{{ matching }}', refstatuslastupdate = NOW()
|
||||
FROM chill_main_address_reference ar
|
||||
WHERE
|
||||
|
@@ -90,7 +90,7 @@ class ChillTwigHelper extends AbstractExtension
|
||||
|
||||
return $twig->render($t, array_merge([
|
||||
'value' => $value,
|
||||
'message' => $message ?? 'No value',
|
||||
'message' => $message,
|
||||
], $options));
|
||||
}
|
||||
}
|
||||
|
@@ -11,15 +11,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Templating\Entity;
|
||||
|
||||
/**
|
||||
* @deprecated load @link{BoxUtilsChillEntityRenderTrait} in the render
|
||||
*/
|
||||
abstract class AbstractChillEntityRender implements ChillEntityRenderInterface
|
||||
{
|
||||
protected function getDefaultClosingBox(): string
|
||||
{
|
||||
return '</section>';
|
||||
}
|
||||
|
||||
protected function getDefaultOpeningBox($classSuffix): string
|
||||
{
|
||||
return '<section class="chill-entity entity-' . $classSuffix . '">';
|
||||
}
|
||||
use BoxUtilsChillEntityRenderTrait;
|
||||
}
|
||||
|
@@ -20,6 +20,9 @@ use Symfony\Component\Templating\EngineInterface;
|
||||
use function array_merge;
|
||||
use function strtr;
|
||||
|
||||
/**
|
||||
* @implements ChillEntityRenderInterface<Address>
|
||||
*/
|
||||
class AddressRender implements ChillEntityRenderInterface
|
||||
{
|
||||
public const DEFAULT_OPTIONS = [
|
||||
@@ -39,16 +42,12 @@ class AddressRender implements ChillEntityRenderInterface
|
||||
|
||||
public function __construct(
|
||||
EngineInterface $templating,
|
||||
TranslatableStringHelperInterface $translatableStringHelper)
|
||||
{
|
||||
TranslatableStringHelperInterface $translatableStringHelper
|
||||
) {
|
||||
$this->templating = $templating;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Address addr
|
||||
* @param mixed $addr
|
||||
*/
|
||||
public function renderBox($addr, array $options): string
|
||||
{
|
||||
$options = array_merge(self::DEFAULT_OPTIONS, $options);
|
||||
@@ -70,8 +69,8 @@ class AddressRender implements ChillEntityRenderInterface
|
||||
{
|
||||
$lines = [];
|
||||
|
||||
if (null !== $addr->getPostCode()) {
|
||||
if ($addr->getPostCode()->getCountry()->getCountryCode() === 'FR') {
|
||||
if (null !== $addr->getPostcode()) {
|
||||
if ($addr->getPostcode()->getCountry()->getCountryCode() === 'FR') {
|
||||
$lines[] = $this->renderIntraBuildingLine($addr);
|
||||
$lines[] = $this->renderBuildingLine($addr);
|
||||
$lines[] = $this->renderStreetLine($addr);
|
||||
@@ -118,10 +117,8 @@ class AddressRender implements ChillEntityRenderInterface
|
||||
|
||||
$res = trim($street . ', ' . $streetNumber, ', ');
|
||||
|
||||
if (null !== $addr->getPostCode()->getCountry()->getCountryCode()) {
|
||||
if ($addr->getPostCode()->getCountry()->getCountryCode() === 'FR') {
|
||||
$res = trim($streetNumber . ', ' . $street, ', ');
|
||||
}
|
||||
if ($addr->getPostcode()->getCountry()->getCountryCode() === 'FR') {
|
||||
$res = trim($streetNumber . ', ' . $street, ', ');
|
||||
}
|
||||
|
||||
if ((',' === $res) || ('' === $res)) {
|
||||
@@ -131,10 +128,6 @@ class AddressRender implements ChillEntityRenderInterface
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Address addr
|
||||
* @param mixed $addr
|
||||
*/
|
||||
public function renderString($addr, array $options): string
|
||||
{
|
||||
return implode(' — ', $this->renderLines($addr));
|
||||
@@ -164,10 +157,8 @@ class AddressRender implements ChillEntityRenderInterface
|
||||
$res = null;
|
||||
}
|
||||
|
||||
if (null !== $addr->getPostCode()->getCountry()->getCountryCode()) {
|
||||
if ($addr->getPostCode()->getCountry()->getCountryCode() === 'FR') {
|
||||
$res = $addr->getBuildingName();
|
||||
}
|
||||
if ($addr->getPostcode()->getCountry()->getCountryCode() === 'FR') {
|
||||
$res = $addr->getBuildingName();
|
||||
}
|
||||
|
||||
return $res;
|
||||
|
@@ -0,0 +1,25 @@
|
||||
<?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\Templating\Entity;
|
||||
|
||||
trait BoxUtilsChillEntityRenderTrait
|
||||
{
|
||||
protected function getDefaultClosingBox(): string
|
||||
{
|
||||
return '</section>';
|
||||
}
|
||||
|
||||
protected function getDefaultOpeningBox($classSuffix): string
|
||||
{
|
||||
return '<section class="chill-entity entity-' . $classSuffix . '">';
|
||||
}
|
||||
}
|
@@ -14,8 +14,10 @@ namespace Chill\MainBundle\Templating\Entity;
|
||||
/**
|
||||
* Render an entity using `__toString()`.
|
||||
*/
|
||||
class ChillEntityRender extends AbstractChillEntityRender
|
||||
class ChillEntityRender implements ChillEntityRenderInterface
|
||||
{
|
||||
use BoxUtilsChillEntityRenderTrait;
|
||||
|
||||
public function renderBox($entity, array $options): string
|
||||
{
|
||||
return $this->getDefaultOpeningBox('default') . $entity
|
||||
|
@@ -14,6 +14,8 @@ namespace Chill\MainBundle\Templating\Entity;
|
||||
/**
|
||||
* Interface to implement which will render an entity in template on a custom
|
||||
* manner.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
interface ChillEntityRenderInterface
|
||||
{
|
||||
@@ -29,7 +31,7 @@ interface ChillEntityRenderInterface
|
||||
* </span>
|
||||
* ```
|
||||
*
|
||||
* @param type $entity
|
||||
* @param T $entity
|
||||
*/
|
||||
public function renderBox($entity, array $options): string;
|
||||
|
||||
@@ -38,14 +40,12 @@ interface ChillEntityRenderInterface
|
||||
*
|
||||
* Example: returning the name of a person.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param T $entity
|
||||
*/
|
||||
public function renderString($entity, array $options): string;
|
||||
|
||||
/**
|
||||
* Return true if the class support this object for the given options.
|
||||
*
|
||||
* @param type $entity
|
||||
*/
|
||||
public function supports($entity, array $options): bool;
|
||||
public function supports(object $entity, array $options): bool;
|
||||
}
|
||||
|
@@ -13,33 +13,32 @@ namespace Chill\MainBundle\Templating\Entity;
|
||||
|
||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
|
||||
use function array_merge;
|
||||
|
||||
class CommentRender extends AbstractChillEntityRender
|
||||
/**
|
||||
* @implements ChillEntityRenderInterface<CommentEmbeddable>
|
||||
*/
|
||||
class CommentRender implements ChillEntityRenderInterface
|
||||
{
|
||||
use BoxUtilsChillEntityRenderTrait;
|
||||
/**
|
||||
* @var EngineInterface
|
||||
*/
|
||||
private $engine;
|
||||
|
||||
/**
|
||||
* @var \Chill\MainBundle\Repository\UserRepository
|
||||
*/
|
||||
private $userRepository;
|
||||
private UserRepositoryInterface $userRepository;
|
||||
|
||||
public function __construct(
|
||||
UserRepository $userRepository,
|
||||
UserRepositoryInterface $userRepository,
|
||||
EngineInterface $engine
|
||||
) {
|
||||
$this->userRepository = $userRepository;
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CommentEmbeddable $entity
|
||||
*/
|
||||
public function renderBox($entity, array $options): string
|
||||
{
|
||||
// default options
|
||||
@@ -50,7 +49,7 @@ class CommentRender extends AbstractChillEntityRender
|
||||
'metadata' => true,
|
||||
], $options);
|
||||
|
||||
if ($entity->getUserId()) {
|
||||
if (null !== $entity->getUserId()) {
|
||||
$user = $this->userRepository->find($entity->getUserId());
|
||||
}
|
||||
|
||||
@@ -67,9 +66,6 @@ class CommentRender extends AbstractChillEntityRender
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CommentEmbeddable $entity
|
||||
*/
|
||||
public function renderString($entity, array $options): string
|
||||
{
|
||||
return $entity->getComment();
|
||||
|
@@ -19,6 +19,9 @@ use Symfony\Component\Templating\EngineInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function array_merge;
|
||||
|
||||
/**
|
||||
* @implements ChillEntityRenderInterface<User>
|
||||
*/
|
||||
class UserRender implements ChillEntityRenderInterface
|
||||
{
|
||||
public const DEFAULT_OPTIONS = [
|
||||
@@ -50,9 +53,6 @@ class UserRender implements ChillEntityRenderInterface
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $entity
|
||||
*/
|
||||
public function renderString($entity, array $options): string
|
||||
{
|
||||
$opts = array_merge(self::DEFAULT_OPTIONS, $options);
|
||||
|
@@ -70,23 +70,23 @@ class DelegatedBlockRenderingEvent extends Event implements ArrayAccess
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return isset($this->context[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
public function offsetGet($offset): mixed
|
||||
{
|
||||
return $this->context[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
throw new RuntimeException('The event context is read-only, you are not '
|
||||
. 'allowed to update it.');
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
throw new RuntimeException('The event context is read-only, you are not '
|
||||
. 'allowed to update it.');
|
||||
|
@@ -1,5 +1,14 @@
|
||||
<?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 Controller;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\Point;
|
||||
@@ -10,6 +19,10 @@ use Chill\MainBundle\Test\PrepareClientTrait;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class AddressToReferenceMatcherControllerTest extends WebTestCase
|
||||
{
|
||||
use PrepareClientTrait;
|
||||
|
@@ -1,5 +1,14 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
@@ -7,6 +16,10 @@ use Chill\MainBundle\Test\PrepareClientTrait;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class GeographicalUnitByAddressApiControllerTest extends WebTestCase
|
||||
{
|
||||
use PrepareClientTrait;
|
||||
|
@@ -267,15 +267,15 @@ final class ExportManagerTest extends KernelTestCase
|
||||
)
|
||||
->willReturn(static function ($value) {
|
||||
switch ($value) {
|
||||
case 0:
|
||||
case 1:
|
||||
return $value;
|
||||
case 0:
|
||||
case 1:
|
||||
return $value;
|
||||
|
||||
case '_header':
|
||||
return 'export';
|
||||
case '_header':
|
||||
return 'export';
|
||||
|
||||
default: throw new RuntimeException(sprintf('The value %s is not valid', $value));
|
||||
}
|
||||
default: throw new RuntimeException(sprintf('The value %s is not valid', $value));
|
||||
}
|
||||
});
|
||||
|
||||
$export->getQueryKeys(Argument::Type('array'))->willReturn(['export']);
|
||||
@@ -307,15 +307,15 @@ final class ExportManagerTest extends KernelTestCase
|
||||
)
|
||||
->willReturn(static function ($value) {
|
||||
switch ($value) {
|
||||
case '_header': return 'foo_header';
|
||||
case '_header': return 'foo_header';
|
||||
|
||||
case 'cat a': return 'label cat a';
|
||||
case 'cat a': return 'label cat a';
|
||||
|
||||
case 'cat b': return 'label cat b';
|
||||
case 'cat b': return 'label cat b';
|
||||
|
||||
default:
|
||||
throw new RuntimeException(sprintf('This value (%s) is not valid', $value));
|
||||
}
|
||||
default:
|
||||
throw new RuntimeException(sprintf('This value (%s) is not valid', $value));
|
||||
}
|
||||
});
|
||||
$aggregator->addRole()->willReturn(null);
|
||||
//$aggregator->addRole()->shouldBeCalled();
|
||||
|
@@ -77,13 +77,13 @@ final class IdToEntityDataTransformerTest extends TestCase
|
||||
|
||||
public function testTransformMulti()
|
||||
{
|
||||
$o1 = new class() {
|
||||
$o1 = new class () {
|
||||
public function getId()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
$o2 = new class() {
|
||||
$o2 = new class () {
|
||||
public function getId()
|
||||
{
|
||||
return 2;
|
||||
@@ -104,7 +104,7 @@ final class IdToEntityDataTransformerTest extends TestCase
|
||||
|
||||
public function testTransformSingle()
|
||||
{
|
||||
$o = new class() {
|
||||
$o = new class () {
|
||||
public function getId()
|
||||
{
|
||||
return 1;
|
||||
|
@@ -33,7 +33,7 @@ final class DefaultScopeResolverTest extends TestCase
|
||||
public function testHasScopeInterface()
|
||||
{
|
||||
$scope = new Scope();
|
||||
$entity = new class($scope) implements HasScopeInterface {
|
||||
$entity = new class ($scope) implements HasScopeInterface {
|
||||
public function __construct(Scope $scope)
|
||||
{
|
||||
$this->scope = $scope;
|
||||
@@ -52,7 +52,7 @@ final class DefaultScopeResolverTest extends TestCase
|
||||
|
||||
public function testHasScopesInterface()
|
||||
{
|
||||
$entity = new class($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface {
|
||||
$entity = new class ($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface {
|
||||
public function __construct(Scope $scopeA, Scope $scopeB)
|
||||
{
|
||||
$this->scopes = [$scopeA, $scopeB];
|
||||
|
@@ -34,7 +34,7 @@ final class ScopeResolverDispatcherTest extends TestCase
|
||||
public function testHasScopeInterface()
|
||||
{
|
||||
$scope = new Scope();
|
||||
$entity = new class($scope) implements HasScopeInterface {
|
||||
$entity = new class ($scope) implements HasScopeInterface {
|
||||
public function __construct(Scope $scope)
|
||||
{
|
||||
$this->scope = $scope;
|
||||
@@ -52,7 +52,7 @@ final class ScopeResolverDispatcherTest extends TestCase
|
||||
|
||||
public function testHasScopesInterface()
|
||||
{
|
||||
$entity = new class($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface {
|
||||
$entity = new class ($scopeA = new Scope(), $scopeB = new Scope()) implements HasScopesInterface {
|
||||
public function __construct(Scope $scopeA, Scope $scopeB)
|
||||
{
|
||||
$this->scopes = [$scopeA, $scopeB];
|
||||
|
@@ -1,5 +1,14 @@
|
||||
<?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 Services\Import;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\Point;
|
||||
@@ -53,7 +62,7 @@ class AddressToReferenceMatcherTest extends KernelTestCase
|
||||
->setCode('78910')
|
||||
->setRefPostalCodeId($refPostalCodeId = '78910'.uniqid())
|
||||
->setCountry($belgium)
|
||||
;
|
||||
;
|
||||
$this->entityManager->persist($postalCode);
|
||||
$this->entityManager->flush();
|
||||
|
||||
|
@@ -63,18 +63,18 @@ class RoleScopeScopePresence extends ConstraintValidator
|
||||
|
||||
//if the role scope should have a scope
|
||||
if (
|
||||
!in_array($value->getRole(), $this->roleProvider->getRolesWithoutScopes(), true)
|
||||
&& $value->getScope() === null
|
||||
) {
|
||||
!in_array($value->getRole(), $this->roleProvider->getRolesWithoutScopes(), true)
|
||||
&& $value->getScope() === null
|
||||
) {
|
||||
$this->context->buildViolation($constraint->messagePresenceRequired)
|
||||
->setParameter('%role%', $this->translator->trans($value->getRole()))
|
||||
->addViolation();
|
||||
$this->logger->debug('the role scope should have a scope, but scope is null. Violation build.');
|
||||
} elseif // if the scope should be null
|
||||
(
|
||||
in_array($value->getRole(), $this->roleProvider->getRolesWithoutScopes(), true)
|
||||
&& null !== $value->getScope()
|
||||
) {
|
||||
(
|
||||
in_array($value->getRole(), $this->roleProvider->getRolesWithoutScopes(), true)
|
||||
&& null !== $value->getScope()
|
||||
) {
|
||||
$this->context->buildViolation($constraint->messageNullRequired)
|
||||
->setParameter('%role%', $this->translator->trans($value->getRole()))
|
||||
->addViolation();
|
||||
|
@@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Workflow;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
|
||||
interface EntityWorkflowHandlerInterface
|
||||
@@ -36,6 +37,11 @@ interface EntityWorkflowHandlerInterface
|
||||
*/
|
||||
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string;
|
||||
|
||||
/**
|
||||
* @return User[]
|
||||
*/
|
||||
public function getSuggestedUsers(EntityWorkflow $entityWorkflow): array;
|
||||
|
||||
public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string;
|
||||
|
||||
public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array;
|
||||
|
@@ -2,6 +2,13 @@
|
||||
|
||||
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;
|
||||
|
@@ -2,6 +2,13 @@
|
||||
|
||||
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;
|
||||
|
@@ -2,6 +2,13 @@
|
||||
|
||||
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;
|
||||
|
@@ -2,6 +2,13 @@
|
||||
|
||||
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;
|
||||
|
@@ -45,3 +45,12 @@ workflow:
|
||||
few {# workflows}
|
||||
other {# workflows}
|
||||
}
|
||||
|
||||
duration:
|
||||
minute: >-
|
||||
{m, plural,
|
||||
=0 {Aucune durée}
|
||||
one {# minute}
|
||||
few {# minutes}
|
||||
other {# minutes}
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
"This program is free software: you can redistribute it and/or modify it under the terms of the <strong>GNU Affero General Public License</strong>": "Ce programme est un logiciel libre: vous pouvez le redistribuer et/ou le modifier selon les termes de la licence <strong>GNU Affero GPL</strong>"
|
||||
User manual: Manuel d'utilisation
|
||||
Search: Rechercher
|
||||
"Search persons, ...": "Recherche des personnes, ..."
|
||||
Person name: Nom / Prénom de la personne
|
||||
"Search persons, ...": "Recherche des usagers, ..."
|
||||
Person name: Nom / Prénom de l'usager
|
||||
Login: Connexion
|
||||
Logout: Se déconnecter
|
||||
Bad credentials.: Le mot de passe et le nom d'utilisateur ne correspondent pas.
|
||||
|
Reference in New Issue
Block a user