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 issue133_user_current_location
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\CRUD\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
@@ -14,94 +16,77 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
class AbstractCRUDController extends AbstractController
|
||||
abstract class AbstractCRUDController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* The crud configuration
|
||||
* The crud configuration
|
||||
*
|
||||
* This configuration si defined by `chill_main['crud']` or `chill_main['apis']`
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $crudConfig = [];
|
||||
|
||||
/**
|
||||
* get the instance of the entity with the given id
|
||||
*
|
||||
* @param string $id
|
||||
* @return object
|
||||
*
|
||||
* @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found
|
||||
*/
|
||||
protected function getEntity($action, $id, Request $request): object
|
||||
protected function getEntity($action, string $id, Request $request): object
|
||||
{
|
||||
$e = $this->getDoctrine()
|
||||
$e = $this
|
||||
->getDoctrine()
|
||||
->getRepository($this->getEntityClass())
|
||||
->find($id);
|
||||
|
||||
if (NULL === $e) {
|
||||
if (null === $e) {
|
||||
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
|
||||
}
|
||||
|
||||
return $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an entity.
|
||||
*
|
||||
* @param string $action
|
||||
* @param Request $request
|
||||
* @return object
|
||||
*/
|
||||
protected function createEntity(string $action, Request $request): object
|
||||
{
|
||||
$type = $this->getEntityClass();
|
||||
|
||||
return new $type;
|
||||
$class = $this->getEntityClass();
|
||||
|
||||
return new $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of entities
|
||||
*
|
||||
* By default, count all entities. You can customize the query by
|
||||
* By default, count all entities. You can customize the query by
|
||||
* using the method `customizeQuery`.
|
||||
*
|
||||
* @param string $action
|
||||
* @param Request $request
|
||||
* @return int
|
||||
*/
|
||||
protected function countEntities(string $action, Request $request, $_format): int
|
||||
{
|
||||
return $this->buildQueryEntities($action, $request)
|
||||
->select('COUNT(e)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the entity.
|
||||
*
|
||||
*
|
||||
* By default, get all entities. You can customize the query by using the
|
||||
* method `customizeQuery`.
|
||||
*
|
||||
*
|
||||
* The method `orderEntity` is called internally to order entities.
|
||||
*
|
||||
*
|
||||
* It returns, by default, a query builder.
|
||||
*
|
||||
*/
|
||||
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
|
||||
{
|
||||
$query = $this->buildQueryEntities($action, $request)
|
||||
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
|
||||
->setMaxResults($paginator->getItemsPerPage());
|
||||
|
||||
|
||||
// allow to order queries and return the new query
|
||||
return $this->orderQuery($action, $query, $request, $paginator, $_format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add ordering fields in the query build by self::queryEntities
|
||||
*
|
||||
*/
|
||||
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
|
||||
{
|
||||
@@ -112,14 +97,12 @@ class AbstractCRUDController extends AbstractController
|
||||
* Build the base query for listing all entities.
|
||||
*
|
||||
* This method is used internally by `countEntities` `queryEntities`
|
||||
*
|
||||
*
|
||||
* This base query does not contains any `WHERE` or `SELECT` clauses. You
|
||||
* can add some by using the method `customizeQuery`.
|
||||
*
|
||||
* The alias for the entity is "e".
|
||||
*
|
||||
* @param string $action
|
||||
* @param Request $request
|
||||
*
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
protected function buildQueryEntities(string $action, Request $request)
|
||||
@@ -127,8 +110,7 @@ class AbstractCRUDController extends AbstractController
|
||||
$qb = $this->getDoctrine()->getManager()
|
||||
->createQueryBuilder()
|
||||
->select('e')
|
||||
->from($this->getEntityClass(), 'e')
|
||||
;
|
||||
->from($this->getEntityClass(), 'e');
|
||||
|
||||
$this->customizeQuery($action, $request, $qb);
|
||||
|
||||
@@ -138,55 +120,54 @@ class AbstractCRUDController extends AbstractController
|
||||
protected function customizeQuery(string $action, Request $request, $query): void {}
|
||||
|
||||
/**
|
||||
* Get the result of the query
|
||||
* Get the result of the query.
|
||||
*/
|
||||
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
|
||||
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
|
||||
{
|
||||
return $query->getQuery()->getResult();
|
||||
}
|
||||
|
||||
protected function onPreIndex(string $action, Request $request, string $_format): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* method used by indexAction
|
||||
*/
|
||||
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
|
||||
{
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* method used by indexAction
|
||||
* Method used by indexAction.
|
||||
*/
|
||||
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used by indexAction.
|
||||
*/
|
||||
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* method used by indexAction
|
||||
* Method used by indexAction.
|
||||
*/
|
||||
protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the complete FQDN of the class
|
||||
*
|
||||
* @return string the complete fqdn of the class
|
||||
* Get the FQDN of the class.
|
||||
*
|
||||
* @return class-string The FQDN of the class
|
||||
*/
|
||||
protected function getEntityClass(): string
|
||||
{
|
||||
return $this->crudConfig['class'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* called on post fetch entity
|
||||
* Called on post fetch entity.
|
||||
*/
|
||||
protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response
|
||||
{
|
||||
@@ -194,7 +175,7 @@ class AbstractCRUDController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on post check ACL
|
||||
* Called on post check ACL.
|
||||
*/
|
||||
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
|
||||
{
|
||||
@@ -203,23 +184,23 @@ class AbstractCRUDController extends AbstractController
|
||||
|
||||
/**
|
||||
* check the acl. Called by every action.
|
||||
*
|
||||
* By default, check the role given by `getRoleFor` for the value given in
|
||||
*
|
||||
* By default, check the role given by `getRoleFor` for the value given in
|
||||
* $entity.
|
||||
*
|
||||
*
|
||||
* Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
|
||||
* if not accessible.
|
||||
*
|
||||
*
|
||||
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
|
||||
*/
|
||||
protected function checkACL(string $action, Request $request, string $_format, $entity = null)
|
||||
{
|
||||
// @TODO: Implements abstract getRoleFor method or do it in the interface.
|
||||
$this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string the crud name
|
||||
* @return string The crud name.
|
||||
*/
|
||||
protected function getCrudName(): string
|
||||
{
|
||||
@@ -230,7 +211,7 @@ class AbstractCRUDController extends AbstractController
|
||||
{
|
||||
return $this->crudConfig['actions'][$action];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the crud configuration
|
||||
*
|
||||
@@ -241,9 +222,6 @@ class AbstractCRUDController extends AbstractController
|
||||
$this->crudConfig = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PaginatorFactory
|
||||
*/
|
||||
protected function getPaginatorFactory(): PaginatorFactory
|
||||
{
|
||||
return $this->container->get('chill_main.paginator_factory');
|
||||
@@ -254,9 +232,6 @@ class AbstractCRUDController extends AbstractController
|
||||
return $this->get('validator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedServices(): array
|
||||
{
|
||||
return \array_merge(
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace Chill\MainBundle;
|
||||
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Chill\MainBundle\Search\SearchApiInterface;
|
||||
use Chill\MainBundle\Search\SearchInterface;
|
||||
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleInterface;
|
||||
@@ -41,6 +42,8 @@ class ChillMainBundle extends Bundle
|
||||
->addTag('chill_main.scope_resolver');
|
||||
$container->registerForAutoconfiguration(ChillEntityRenderInterface::class)
|
||||
->addTag('chill.render_entity');
|
||||
$container->registerForAutoconfiguration(SearchApiInterface::class)
|
||||
->addTag('chill.search_api_provider');
|
||||
|
||||
$container->addCompilerPass(new SearchableServicesCompilerPass());
|
||||
$container->addCompilerPass(new ConfigConsistencyCompilerPass());
|
||||
|
@@ -1,7 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Command;
|
||||
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -23,101 +26,51 @@ use League\Csv\Writer;
|
||||
|
||||
class ChillImportUsersCommand extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var ValidatorInterface
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var UserPasswordEncoderInterface
|
||||
*/
|
||||
protected $passwordEncoder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \Chill\MainBundle\Repository\UserRepository
|
||||
*/
|
||||
protected $userRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $doChanges = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var OutputInterface
|
||||
*/
|
||||
protected $tempOutput;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var InputInterface
|
||||
*/
|
||||
protected $tempInput;
|
||||
|
||||
protected EntityManagerInterface $em;
|
||||
|
||||
protected ValidatorInterface $validator;
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
protected UserPasswordEncoderInterface $passwordEncoder;
|
||||
|
||||
protected UserRepository $userRepository;
|
||||
|
||||
protected bool $doChanges = true;
|
||||
|
||||
protected OutputInterface $tempOutput;
|
||||
|
||||
protected InputInterface $tempInput;
|
||||
|
||||
/**
|
||||
* Centers and aliases.
|
||||
*
|
||||
*
|
||||
* key are aliases, values are an array of centers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $centers = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $permissionGroups = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $groupCenters = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Writer
|
||||
*/
|
||||
protected $output = null;
|
||||
|
||||
protected array $centers;
|
||||
|
||||
protected array $permissionGroups;
|
||||
|
||||
protected array $groupCenters;
|
||||
|
||||
protected Writer $output;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
LoggerInterface $logger,
|
||||
UserPasswordEncoderInterface $passwordEncoder,
|
||||
ValidatorInterface $validator
|
||||
ValidatorInterface $validator,
|
||||
UserRepository $userRepository
|
||||
) {
|
||||
$this->em = $em;
|
||||
$this->passwordEncoder = $passwordEncoder;
|
||||
$this->validator = $validator;
|
||||
$this->logger = $logger;
|
||||
|
||||
|
||||
$this->userRepository = $em->getRepository(User::class);
|
||||
|
||||
$this->userRepository = $userRepository;
|
||||
|
||||
parent::__construct('chill:main:import-users');
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
@@ -126,25 +79,24 @@ class ChillImportUsersCommand extends Command
|
||||
->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`')
|
||||
->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias')
|
||||
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit the changes')
|
||||
->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file')
|
||||
;
|
||||
->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->tempOutput = $output;
|
||||
$this->tempInput = $input;
|
||||
|
||||
|
||||
if ($input->getOption('dry-run')) {
|
||||
$this->doChanges = false;
|
||||
}
|
||||
|
||||
|
||||
$this->prepareWriter();
|
||||
|
||||
|
||||
if ($input->hasOption('grouping-centers')) {
|
||||
$this->prepareGroupingCenters();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$this->loadUsers();
|
||||
}
|
||||
@@ -152,19 +104,19 @@ class ChillImportUsersCommand extends Command
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function prepareWriter()
|
||||
{
|
||||
$this->output = $output = Writer::createFromPath($this->tempInput
|
||||
->getOption('csv-dump'), 'a+');
|
||||
|
||||
|
||||
$output->insertOne([
|
||||
'email',
|
||||
'username',
|
||||
'id'
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
protected function appendUserToFile(User $user)
|
||||
{
|
||||
$this->output->insertOne( [
|
||||
@@ -173,35 +125,35 @@ class ChillImportUsersCommand extends Command
|
||||
$user->getId()
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
protected function loadUsers()
|
||||
{
|
||||
$reader = Reader::createFromPath($this->tempInput->getArgument('csvfile'));
|
||||
$reader->setHeaderOffset(0);
|
||||
|
||||
|
||||
foreach ($reader->getRecords() as $line => $r) {
|
||||
$this->logger->debug("starting handling new line", [
|
||||
'line' => $line
|
||||
]);
|
||||
|
||||
|
||||
if ($this->doesUserExists($r)) {
|
||||
$this->tempOutput->writeln(sprintf("User with username '%s' already "
|
||||
. "exists, skipping", $r["username"]));
|
||||
|
||||
|
||||
$this->logger->info("One user already exists, skipping creation", [
|
||||
'username_in_file' => $r['username'],
|
||||
'email_in_file' => $r['email'],
|
||||
'line' => $line
|
||||
]);
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$user = $this->createUser($line, $r);
|
||||
$this->appendUserToFile($user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function doesUserExists($data)
|
||||
{
|
||||
if ($this->userRepository->countByUsernameOrEmail($data['username']) > 0) {
|
||||
@@ -211,10 +163,10 @@ class ChillImportUsersCommand extends Command
|
||||
if ($this->userRepository->countByUsernameOrEmail($data['email']) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected function createUser($offset, $data)
|
||||
{
|
||||
$user = new User();
|
||||
@@ -222,41 +174,41 @@ class ChillImportUsersCommand extends Command
|
||||
->setEmail(\trim($data['email']))
|
||||
->setUsername(\trim($data['username']))
|
||||
->setEnabled(true)
|
||||
->setPassword($this->passwordEncoder->encodePassword($user,
|
||||
->setPassword($this->passwordEncoder->encodePassword($user,
|
||||
\bin2hex(\random_bytes(32))))
|
||||
;
|
||||
|
||||
|
||||
$errors = $this->validator->validate($user);
|
||||
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
$errorMessages = $this->concatenateViolations($errors);
|
||||
|
||||
|
||||
$this->tempOutput->writeln(sprintf("%d errors found with user with username \"%s\" at line %d", $errors->count(), $data['username'], $offset));
|
||||
$this->tempOutput->writeln($errorMessages);
|
||||
|
||||
throw new \RuntimeException("Found errors while creating an user. "
|
||||
. "Watch messages in command output");
|
||||
}
|
||||
|
||||
|
||||
$pgs = $this->getPermissionGroup($data['permission group']);
|
||||
$centers = $this->getCenters($data['center']);
|
||||
|
||||
|
||||
foreach($pgs as $pg) {
|
||||
foreach ($centers as $center) {
|
||||
$groupcenter = $this->createOrGetGroupCenter($center, $pg);
|
||||
|
||||
|
||||
if (FALSE === $user->getGroupCenters()->contains($groupcenter)) {
|
||||
$user->addGroupCenter($groupcenter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($this->doChanges) {
|
||||
$this->em->persist($user);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
|
||||
$this->logger->notice("Create user", [
|
||||
'username' => $user->getUsername(),
|
||||
'id' => $user->getId(),
|
||||
@@ -265,65 +217,58 @@ class ChillImportUsersCommand extends Command
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
||||
protected function getPermissionGroup($alias)
|
||||
{
|
||||
if (\array_key_exists($alias, $this->permissionGroups)) {
|
||||
return $this->permissionGroups[$alias];
|
||||
}
|
||||
|
||||
|
||||
$permissionGroupsByName = [];
|
||||
|
||||
|
||||
foreach($this->em->getRepository(PermissionsGroup::class)
|
||||
->findAll() as $permissionGroup) {
|
||||
$permissionGroupsByName[$permissionGroup->getName()] = $permissionGroup;
|
||||
}
|
||||
|
||||
|
||||
if (count($permissionGroupsByName) === 0) {
|
||||
throw new \RuntimeException("no permission groups found. Create them "
|
||||
. "before importing users");
|
||||
}
|
||||
|
||||
$question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?",
|
||||
|
||||
$question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?",
|
||||
\array_keys($permissionGroupsByName));
|
||||
$question
|
||||
->setMultiselect(true)
|
||||
->setAutocompleterValues(\array_keys($permissionGroupsByName))
|
||||
->setNormalizer(function($value) {
|
||||
if (NULL === $value) { return ''; }
|
||||
|
||||
|
||||
return \trim($value);
|
||||
})
|
||||
;
|
||||
$helper = $this->getHelper('question');
|
||||
|
||||
|
||||
$keys = $helper->ask($this->tempInput, $this->tempOutput, $question);
|
||||
|
||||
|
||||
$this->tempOutput->writeln("You have chosen ".\implode(", ", $keys));
|
||||
|
||||
if ($helper->ask($this->tempInput, $this->tempOutput,
|
||||
|
||||
if ($helper->ask($this->tempInput, $this->tempOutput,
|
||||
new ConfirmationQuestion("Are you sure ?", true))) {
|
||||
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$this->permissionGroups[$alias][] = $permissionGroupsByName[$key];
|
||||
}
|
||||
|
||||
|
||||
return $this->permissionGroups[$alias];
|
||||
} else {
|
||||
$this->logger->error("Error while responding to a a question");
|
||||
|
||||
$this->tempOutput("Ok, I accept, but I do not know what to do. Please try again.");
|
||||
|
||||
throw new \RuntimeException("Error while responding to a question");
|
||||
}
|
||||
|
||||
$this->logger->error('Error while responding to a a question');
|
||||
$this->tempOutput->writeln('Ok, I accept, but I do not know what to do. Please try again.');
|
||||
|
||||
throw new \RuntimeException('Error while responding to a question');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Center $center
|
||||
* @param \Chill\MainBundle\Command\PermissionGroup $pg
|
||||
* @return GroupCenter
|
||||
*/
|
||||
|
||||
protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter
|
||||
{
|
||||
if (\array_key_exists($center->getId(), $this->groupCenters)) {
|
||||
@@ -331,36 +276,36 @@ class ChillImportUsersCommand extends Command
|
||||
return $this->groupCenters[$center->getId()][$pg->getId()];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$repository = $this->em->getRepository(GroupCenter::class);
|
||||
|
||||
|
||||
$groupCenter = $repository->findOneBy(array(
|
||||
'center' => $center,
|
||||
'permissionsGroup' => $pg
|
||||
));
|
||||
|
||||
|
||||
if ($groupCenter === NULL) {
|
||||
$groupCenter = new GroupCenter();
|
||||
$groupCenter
|
||||
->setCenter($center)
|
||||
->setPermissionsGroup($pg)
|
||||
;
|
||||
|
||||
|
||||
$this->em->persist($groupCenter);
|
||||
}
|
||||
|
||||
|
||||
$this->groupCenters[$center->getId()][$pg->getId()] = $groupCenter;
|
||||
|
||||
|
||||
return $groupCenter;
|
||||
}
|
||||
|
||||
|
||||
protected function prepareGroupingCenters()
|
||||
{
|
||||
$reader = Reader::createFromPath($this->tempInput->getOption('grouping-centers'));
|
||||
$reader->setHeaderOffset(0);
|
||||
|
||||
|
||||
foreach ($reader->getRecords() as $r) {
|
||||
$this->centers[$r['alias']] =
|
||||
$this->centers[$r['alias']] =
|
||||
\array_merge(
|
||||
$this->centers[$r['alias']] ?? [],
|
||||
$this->getCenters($r['center']
|
||||
@@ -368,18 +313,18 @@ class ChillImportUsersCommand extends Command
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return a list of centers matching the name of alias.
|
||||
*
|
||||
*
|
||||
* If the name match one center, this center is returned in an array.
|
||||
*
|
||||
* If the name match an alias, the centers corresponding to the alias are
|
||||
*
|
||||
* If the name match an alias, the centers corresponding to the alias are
|
||||
* returned in an array.
|
||||
*
|
||||
*
|
||||
* If the center is not found or alias is not created, a new center is created
|
||||
* and suggested to user
|
||||
*
|
||||
*
|
||||
* @param string $name the name of the center or the alias regrouping center
|
||||
* @return Center[]
|
||||
*/
|
||||
@@ -387,62 +332,62 @@ class ChillImportUsersCommand extends Command
|
||||
{
|
||||
// sanitize
|
||||
$name = \trim($name);
|
||||
|
||||
|
||||
if (\array_key_exists($name, $this->centers)) {
|
||||
return $this->centers[$name];
|
||||
}
|
||||
|
||||
|
||||
// search for a center with given name
|
||||
$center = $this->em->getRepository(Center::class)
|
||||
->findOneByName($name);
|
||||
|
||||
|
||||
if ($center instanceof Center) {
|
||||
$this->centers[$name] = [$center];
|
||||
|
||||
|
||||
return $this->centers[$name];
|
||||
}
|
||||
|
||||
|
||||
// suggest and create
|
||||
$center = (new Center())
|
||||
->setName($name);
|
||||
|
||||
|
||||
$this->tempOutput->writeln("Center with name \"$name\" not found.");
|
||||
$qFormatter = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("Create a center with name \"$name\" ?", true);
|
||||
|
||||
|
||||
if ($qFormatter->ask($this->tempInput, $this->tempOutput, $question)) {
|
||||
$this->centers[$name] = [ $center ];
|
||||
|
||||
|
||||
$errors = $this->validator->validate($center);
|
||||
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
$errorMessages = $this->concatenateViolations($errors);
|
||||
|
||||
|
||||
$this->tempOutput->writeln(sprintf("%d errors found with center with name \"%s\"", $errors->count(), $name));
|
||||
$this->tempOutput->writeln($errorMessages);
|
||||
|
||||
|
||||
throw new \RuntimeException("Found errors while creating one center. "
|
||||
. "Watch messages in command output");
|
||||
}
|
||||
|
||||
|
||||
$this->em->persist($center);
|
||||
|
||||
|
||||
return $this->centers[$name];
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected function concatenateViolations(ConstraintViolationListInterface $list)
|
||||
{
|
||||
$str = [];
|
||||
|
||||
|
||||
foreach ($list as $e) {
|
||||
/* @var $e \Symfony\Component\Validator\ConstraintViolationInterface */
|
||||
$str[] = $e->getMessage();
|
||||
}
|
||||
|
||||
|
||||
return \implode(";", $str);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,20 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||
use Chill\MainBundle\Entity\Country;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
class AdminCountryCRUDController extends CRUDController
|
||||
{
|
||||
|
||||
function __construct(PaginatorFactory $paginator)
|
||||
{
|
||||
$this->paginatorFactory = $paginator;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -137,17 +137,15 @@ class UserController extends CRUDController
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param User $user
|
||||
* @return \Symfony\Component\Form\Form
|
||||
*/
|
||||
private function createEditPasswordForm(User $user)
|
||||
private function createEditPasswordForm(User $user): FormInterface
|
||||
{
|
||||
return $this->createForm(UserPasswordType::class, null, array(
|
||||
'user' => $user
|
||||
))
|
||||
return $this->createForm(
|
||||
UserPasswordType::class,
|
||||
null,
|
||||
[
|
||||
'user' => $user
|
||||
]
|
||||
)
|
||||
->add('submit', SubmitType::class, array('label' => 'Change password'))
|
||||
->remove('actual_password');
|
||||
}
|
||||
@@ -314,7 +312,7 @@ class UserController extends CRUDController
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$form = $this->createForm(UserCurrentLocationType::class, $user)
|
||||
->add('submit', SubmitType::class, ['label' => 'Change current location'])
|
||||
->add('submit', SubmitType::class, ['label' => 'Save'])
|
||||
->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
|
@@ -1,19 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -23,12 +23,11 @@ namespace Chill\MainBundle\Entity;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="centers")
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class Center implements HasCenterInterface
|
||||
{
|
||||
@@ -46,9 +45,10 @@ class Center implements HasCenterInterface
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(type="string", length=255)
|
||||
* @Serializer\Groups({"docgen:read"})
|
||||
*/
|
||||
private $name;
|
||||
|
||||
private string $name = '';
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
*
|
||||
@@ -58,8 +58,8 @@ class Center implements HasCenterInterface
|
||||
* )
|
||||
*/
|
||||
private $groupCenters;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Center constructor.
|
||||
*/
|
||||
@@ -67,7 +67,7 @@ class Center implements HasCenterInterface
|
||||
{
|
||||
$this->groupCenters = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -75,7 +75,7 @@ class Center implements HasCenterInterface
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return $this
|
||||
@@ -85,7 +85,7 @@ class Center implements HasCenterInterface
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
@@ -93,7 +93,7 @@ class Center implements HasCenterInterface
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ArrayCollection|Collection
|
||||
*/
|
||||
@@ -101,7 +101,7 @@ class Center implements HasCenterInterface
|
||||
{
|
||||
return $this->groupCenters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param GroupCenter $groupCenter
|
||||
* @return $this
|
||||
@@ -111,7 +111,7 @@ class Center implements HasCenterInterface
|
||||
$this->groupCenters->add($groupCenter);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -119,7 +119,7 @@ class Center implements HasCenterInterface
|
||||
{
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return $this|Center
|
||||
*/
|
||||
|
@@ -1,22 +1,6 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Chill is a suite of a modules, Chill is a software for social workers
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
@@ -28,38 +12,30 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="role_scopes")
|
||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class RoleScope
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
private $role;
|
||||
|
||||
private ?string $role = null;
|
||||
|
||||
/**
|
||||
* @var Scope
|
||||
*
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="Chill\MainBundle\Entity\Scope",
|
||||
* inversedBy="roleScopes")
|
||||
* @ORM\JoinColumn(nullable=true, name="scope_id")
|
||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
|
||||
*/
|
||||
private $scope;
|
||||
|
||||
private ?Scope $scope = null;
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
*
|
||||
@@ -68,59 +44,37 @@ class RoleScope
|
||||
* mappedBy="roleScopes")
|
||||
*/
|
||||
private $permissionsGroups;
|
||||
|
||||
|
||||
/**
|
||||
* RoleScope constructor.
|
||||
*/
|
||||
|
||||
public function __construct() {
|
||||
$this->new = true;
|
||||
$this->permissionsGroups = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRole()
|
||||
public function getRole(): ?string
|
||||
{
|
||||
return $this->role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Scope
|
||||
*/
|
||||
public function getScope()
|
||||
public function getScope(): ?Scope
|
||||
{
|
||||
return $this->scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type $role
|
||||
* @return RoleScope
|
||||
*/
|
||||
public function setRole($role)
|
||||
|
||||
public function setRole(?string $role = null): self
|
||||
{
|
||||
$this->role = $role;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Scope $scope
|
||||
* @return RoleScope
|
||||
*/
|
||||
public function setScope(Scope $scope = null)
|
||||
public function setScope(?Scope $scope = null): self
|
||||
{
|
||||
$this->scope = $scope;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Entity\Location;
|
||||
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
/**
|
||||
* User
|
||||
@@ -17,7 +17,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="users")
|
||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
||||
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
|
||||
* "user"=User::class
|
||||
* })
|
||||
*/
|
||||
@@ -52,6 +52,7 @@ class User implements AdvancedUserInterface {
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=200)
|
||||
* @Serializer\Groups({"docgen:read"})
|
||||
*/
|
||||
private string $label = '';
|
||||
|
||||
@@ -59,8 +60,9 @@ class User implements AdvancedUserInterface {
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(type="string", length=150, nullable=true)
|
||||
* @Serializer\Groups({"docgen:read"})
|
||||
*/
|
||||
private $email;
|
||||
private ?string $email = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@@ -124,6 +126,7 @@ class User implements AdvancedUserInterface {
|
||||
/**
|
||||
* @var Center|null
|
||||
* @ORM\ManyToOne(targetEntity=Center::class)
|
||||
* @Serializer\Groups({"docgen:read"})
|
||||
*/
|
||||
private ?Center $mainCenter = null;
|
||||
|
||||
|
@@ -1,50 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
* Copyright (C) 2016 Champs-Libres Coopérative <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Pagination;
|
||||
|
||||
/**
|
||||
* PageGenerator associated with a Paginator
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* @author Champs Libres <info@champs-libres.coop>
|
||||
* PageGenerator associated with a Paginator.
|
||||
*/
|
||||
class PageGenerator implements \Iterator
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var Paginator
|
||||
*/
|
||||
protected $paginator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $current = 1;
|
||||
|
||||
public function __construct(Paginator $paginator)
|
||||
protected Paginator $paginator;
|
||||
|
||||
protected int $current = 1;
|
||||
|
||||
public function __construct(Paginator $paginator)
|
||||
{
|
||||
$this->paginator = $paginator;;
|
||||
}
|
||||
|
||||
|
||||
public function current()
|
||||
{
|
||||
return $this->paginator->getPage($current);
|
||||
@@ -67,7 +40,7 @@ class PageGenerator implements \Iterator
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return $this->current > 0
|
||||
return $this->current > 0
|
||||
&& $this->current <= $this->paginator->countPages();
|
||||
}
|
||||
}
|
||||
|
@@ -402,3 +402,11 @@ input.belgian_national_number {
|
||||
&.daily_counter {}
|
||||
&.control_digit {}
|
||||
}
|
||||
|
||||
// replace abbr
|
||||
span.item-key {
|
||||
font-variant: all-small-caps;
|
||||
font-size: 90%;
|
||||
background-color: #0000000a;
|
||||
//text-decoration: dotted underline;
|
||||
}
|
||||
|
@@ -8,3 +8,43 @@ require('./bootstrap.scss');
|
||||
import Dropdown from 'bootstrap/js/src/dropdown';
|
||||
import Modal from 'bootstrap/js/dist/modal';
|
||||
import Collapse from 'bootstrap/js/src/collapse';
|
||||
import Carousel from 'bootstrap/js/src/carousel';
|
||||
|
||||
//
|
||||
// ACHeaderSlider is a small slider used in banner of AccompanyingCourse Section
|
||||
// Initialize options, and show/hide controls in first/last slides
|
||||
//
|
||||
let ACHeaderSlider = document.querySelector('#ACHeaderSlider');
|
||||
if (ACHeaderSlider) {
|
||||
let controlPrev = ACHeaderSlider.querySelector('button[data-bs-slide="prev"]'),
|
||||
controlNext = ACHeaderSlider.querySelector('button[data-bs-slide="next"]'),
|
||||
length = ACHeaderSlider.querySelectorAll('.carousel-item').length,
|
||||
last = length-1,
|
||||
carousel = new Carousel(ACHeaderSlider, {
|
||||
interval: false,
|
||||
wrap: false,
|
||||
ride: false,
|
||||
keyboard: false,
|
||||
touch: true
|
||||
})
|
||||
;
|
||||
document.addEventListener('DOMContentLoaded', (e) => {
|
||||
controlNext.classList.remove('visually-hidden');
|
||||
});
|
||||
ACHeaderSlider.addEventListener('slid.bs.carousel', (e) => {
|
||||
//console.log('from slide', e.direction, e.relatedTarget, e.from, e.to );
|
||||
switch (e.to) {
|
||||
case 0:
|
||||
controlPrev.classList.add('visually-hidden');
|
||||
controlNext.classList.remove('visually-hidden');
|
||||
break;
|
||||
case last:
|
||||
controlPrev.classList.remove('visually-hidden');
|
||||
controlNext.classList.add('visually-hidden');
|
||||
break;
|
||||
default:
|
||||
controlPrev.classList.remove('visually-hidden');
|
||||
controlNext.classList.remove('visually-hidden');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>{{ installation.name }} - {% block title %}{% endblock %}</title>
|
||||
<title>{{ installation.name }} - {% block title %}{{ 'Homepage'|trans }}{% endblock %}</title>
|
||||
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
|
||||
|
||||
{{ encore_entry_link_tags('mod_bootstrap') }}
|
||||
@@ -68,6 +68,9 @@
|
||||
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
||||
</button>
|
||||
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
|
||||
</a>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
|
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Routing;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
@@ -8,43 +10,28 @@ use Knp\Menu\FactoryInterface;
|
||||
use Knp\Menu\ItemInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
|
||||
/**
|
||||
* This class permit to build menu from the routing information
|
||||
* stored in each bundle.
|
||||
*
|
||||
* how to must come here FIXME
|
||||
*
|
||||
* how to must come here FIXME
|
||||
*
|
||||
* @author julien
|
||||
*/
|
||||
class MenuComposer
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @var RouterInterface
|
||||
*/
|
||||
private $router;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var FactoryInterface
|
||||
*/
|
||||
private $menuFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private $translator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
private $localMenuBuilders = [];
|
||||
|
||||
|
||||
|
||||
private RouterInterface $router;
|
||||
|
||||
private FactoryInterface $menuFactory;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
private array $localMenuBuilders = [];
|
||||
|
||||
private RouteCollection $routeCollection;
|
||||
|
||||
|
||||
function __construct(
|
||||
RouterInterface $router,
|
||||
FactoryInterface $menuFactory,
|
||||
@@ -60,7 +47,7 @@ class MenuComposer
|
||||
* This function is needed for testing purpose: routeCollection is not
|
||||
* available as a service (RouterInterface is provided as a service and
|
||||
* added to this class as paramater in __construct)
|
||||
*
|
||||
*
|
||||
* @param RouteCollection $routeCollection
|
||||
*/
|
||||
public function setRouteCollection(RouteCollection $routeCollection)
|
||||
@@ -71,7 +58,7 @@ class MenuComposer
|
||||
/**
|
||||
* Return an array of routes added to $menuId,
|
||||
* The array is aimed to build route with MenuTwig
|
||||
*
|
||||
*
|
||||
* @param string $menuId
|
||||
* @param array $parameters see https://redmine.champs-libres.coop/issues/179
|
||||
* @return array
|
||||
@@ -83,7 +70,7 @@ class MenuComposer
|
||||
|
||||
foreach ($routeCollection->all() as $routeKey => $route) {
|
||||
if ($route->hasOption('menus')) {
|
||||
|
||||
|
||||
if (array_key_exists($menuId, $route->getOption('menus'))) {
|
||||
$route = $route->getOption('menus')[$menuId];
|
||||
|
||||
@@ -101,12 +88,12 @@ class MenuComposer
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
|
||||
public function getMenuFor($menuId, array $parameters = array())
|
||||
{
|
||||
$routes = $this->getRoutesFor($menuId, $parameters);
|
||||
$menu = $this->menuFactory->createItem($menuId);
|
||||
|
||||
|
||||
// build menu from routes
|
||||
foreach ($routes as $order => $route) {
|
||||
$menu->addChild($this->translator->trans($route['label']), [
|
||||
@@ -121,24 +108,24 @@ class MenuComposer
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
if ($this->hasLocalMenuBuilder($menuId)) {
|
||||
foreach ($this->localMenuBuilders[$menuId] as $builder) {
|
||||
/* @var $builder LocalMenuBuilderInterface */
|
||||
$builder->buildMenu($menuId, $menu, $parameters['args']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->reorderMenu($menu);
|
||||
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* recursive function to resolve the order of a array of routes.
|
||||
* If the order chosen in routing.yml is already in used, find the
|
||||
* If the order chosen in routing.yml is already in used, find the
|
||||
* first next order available.
|
||||
*
|
||||
*
|
||||
* @param array $routes the routes previously added
|
||||
* @param int $order
|
||||
* @return int
|
||||
@@ -151,41 +138,41 @@ class MenuComposer
|
||||
return $order;
|
||||
}
|
||||
}
|
||||
|
||||
private function reorderMenu(ItemInterface $menu)
|
||||
|
||||
private function reorderMenu(ItemInterface $menu)
|
||||
{
|
||||
$ordered = [];
|
||||
$unordered = [];
|
||||
|
||||
|
||||
foreach ($menu->getChildren() as $name => $item) {
|
||||
$order = $item->getExtra('order');
|
||||
|
||||
|
||||
if ($order !== null) {
|
||||
$ordered[$this->resolveOrder($ordered, $order)] = $name;
|
||||
} else {
|
||||
$unordered = $name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ksort($ordered);
|
||||
|
||||
|
||||
$menus = \array_merge(\array_values($ordered), $unordered);
|
||||
$menu->reorderChildren($menus);
|
||||
}
|
||||
|
||||
|
||||
public function addLocalMenuBuilder(LocalMenuBuilderInterface $menuBuilder, $menuId)
|
||||
|
||||
|
||||
public function addLocalMenuBuilder(LocalMenuBuilderInterface $menuBuilder, $menuId)
|
||||
{
|
||||
$this->localMenuBuilders[$menuId][] = $menuBuilder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the menu has at least one builder.
|
||||
*
|
||||
*
|
||||
* This function is a helper to determine if the method `getMenuFor`
|
||||
* should be used, or `getRouteFor`. The method `getMenuFor` should be used
|
||||
* if the result is true (it **does** exists at least one menu builder.
|
||||
*
|
||||
*
|
||||
* @param string $menuId
|
||||
* @return bool
|
||||
*/
|
||||
|
@@ -26,10 +26,10 @@ use Chill\MainBundle\Search\ParsingException;
|
||||
|
||||
/**
|
||||
* This class implements abstract search with most common responses.
|
||||
*
|
||||
*
|
||||
* you should use this abstract class instead of SearchInterface : if the signature of
|
||||
* search interface change, the generic method will be implemented here.
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
*/
|
||||
@@ -37,7 +37,7 @@ abstract class AbstractSearch implements SearchInterface
|
||||
{
|
||||
/**
|
||||
* parse string expected to be a date and transform to a DateTime object
|
||||
*
|
||||
*
|
||||
* @param type $string
|
||||
* @return \DateTime
|
||||
* @throws ParsingException if the date is not parseable
|
||||
@@ -51,14 +51,14 @@ abstract class AbstractSearch implements SearchInterface
|
||||
. 'not parsable', 0, $ex);
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* recompose a pattern, retaining only supported terms
|
||||
*
|
||||
*
|
||||
* the outputted string should be used to show users their search
|
||||
*
|
||||
*
|
||||
* @param array $terms
|
||||
* @param array $supportedTerms
|
||||
* @param string $domain if your domain is NULL, you should set NULL. You should set used domain instead
|
||||
@@ -67,35 +67,35 @@ abstract class AbstractSearch implements SearchInterface
|
||||
protected function recomposePattern(array $terms, array $supportedTerms, $domain = NULL)
|
||||
{
|
||||
$recomposed = '';
|
||||
|
||||
|
||||
if ($domain !== NULL)
|
||||
{
|
||||
$recomposed .= '@'.$domain.' ';
|
||||
}
|
||||
|
||||
|
||||
foreach ($supportedTerms as $term) {
|
||||
if (array_key_exists($term, $terms) && $term !== '_default') {
|
||||
$recomposed .= ' '.$term.':';
|
||||
$containsSpace = \strpos($terms[$term], " ") !== false;
|
||||
if ($containsSpace) {
|
||||
$recomposed .= "(";
|
||||
$recomposed .= '"';
|
||||
}
|
||||
$recomposed .= (mb_stristr(' ', $terms[$term]) === FALSE) ? $terms[$term] : '('.$terms[$term].')';
|
||||
if ($containsSpace) {
|
||||
$recomposed .= ")";
|
||||
$recomposed .= '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($terms['_default'] !== '') {
|
||||
$recomposed .= ' '.$terms['_default'];
|
||||
}
|
||||
|
||||
|
||||
//strip first character if empty
|
||||
if (mb_strcut($recomposed, 0, 1) === ' '){
|
||||
$recomposed = mb_strcut($recomposed, 1);
|
||||
}
|
||||
|
||||
|
||||
return $recomposed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,19 +20,15 @@ class SearchApi
|
||||
private EntityManagerInterface $em;
|
||||
private PaginatorFactory $paginator;
|
||||
|
||||
private array $providers = [];
|
||||
private iterable $providers = [];
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
SearchPersonApiProvider $searchPerson,
|
||||
ThirdPartyApiSearch $thirdPartyApiSearch,
|
||||
SearchUserApiProvider $searchUser,
|
||||
iterable $providers,
|
||||
PaginatorFactory $paginator
|
||||
) {
|
||||
$this->em = $em;
|
||||
$this->providers[] = $searchPerson;
|
||||
$this->providers[] = $thirdPartyApiSearch;
|
||||
$this->providers[] = $searchUser;
|
||||
$this->providers = $providers;
|
||||
$this->paginator = $paginator;
|
||||
}
|
||||
|
||||
@@ -68,10 +64,15 @@ class SearchApi
|
||||
|
||||
private function findProviders(string $pattern, array $types, array $parameters): array
|
||||
{
|
||||
return \array_filter(
|
||||
$this->providers,
|
||||
fn($p) => $p->supportsTypes($pattern, $types, $parameters)
|
||||
);
|
||||
$providers = [];
|
||||
|
||||
foreach ($this->providers as $provider) {
|
||||
if ($provider->supportsTypes($pattern, $types, $parameters)) {
|
||||
$providers[] = $provider;
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
private function countItems($providers, $types, $parameters): int
|
||||
@@ -82,12 +83,12 @@ class SearchApi
|
||||
$countNq = $this->em->createNativeQuery($countQuery, $rsmCount);
|
||||
$countNq->setParameters($parameters);
|
||||
|
||||
return $countNq->getSingleScalarResult();
|
||||
return (int) $countNq->getSingleScalarResult();
|
||||
}
|
||||
|
||||
private function buildCountQuery(array $queries, $types, $parameters)
|
||||
{
|
||||
$query = "SELECT COUNT(*) AS count FROM ({union_unordered}) AS sq";
|
||||
$query = "SELECT SUM(c) AS count FROM ({union_unordered}) AS sq";
|
||||
$unions = [];
|
||||
$parameters = [];
|
||||
|
||||
@@ -141,17 +142,20 @@ class SearchApi
|
||||
private function prepareProviders(array $rawResults)
|
||||
{
|
||||
$metadatas = [];
|
||||
$providers = [];
|
||||
|
||||
foreach ($rawResults as $r) {
|
||||
foreach ($this->providers as $k => $p) {
|
||||
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
||||
$metadatas[$k][] = $r['metadata'];
|
||||
$providers[$k] = $p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($metadatas as $k => $m) {
|
||||
$this->providers[$k]->prepare($m);
|
||||
$providers[$k]->prepare($m);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,8 @@ namespace Chill\MainBundle\Search;
|
||||
|
||||
class SearchApiQuery
|
||||
{
|
||||
private array $select = [];
|
||||
private array $selectParams = [];
|
||||
private ?string $selectKey = null;
|
||||
private array $selectKeyParams = [];
|
||||
private ?string $jsonbMetadata = null;
|
||||
@@ -15,6 +17,38 @@ class SearchApiQuery
|
||||
private array $whereClauses = [];
|
||||
private array $whereClausesParams = [];
|
||||
|
||||
public function addSelectClause(string $select, array $params = []): self
|
||||
{
|
||||
$this->select[] = $select;
|
||||
$this->selectParams = [...$this->selectParams, ...$params];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function resetSelectClause(): self
|
||||
{
|
||||
$this->select = [];
|
||||
$this->selectParams = [];
|
||||
$this->selectKey = null;
|
||||
$this->selectKeyParams = [];
|
||||
$this->jsonbMetadata = null;
|
||||
$this->jsonbMetadataParams = [];
|
||||
$this->pertinence = null;
|
||||
$this->pertinenceParams = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSelectClauses(): array
|
||||
{
|
||||
return $this->select;
|
||||
}
|
||||
|
||||
public function getSelectParams(): array
|
||||
{
|
||||
return $this->selectParams;
|
||||
}
|
||||
|
||||
public function setSelectKey(string $selectKey, array $params = []): self
|
||||
{
|
||||
$this->selectKey = $selectKey;
|
||||
@@ -47,6 +81,16 @@ class SearchApiQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFromClause(): string
|
||||
{
|
||||
return $this->fromClause;
|
||||
}
|
||||
|
||||
public function getFromParams(): array
|
||||
{
|
||||
return $this->fromClauseParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the where clause and replace all existing ones.
|
||||
*
|
||||
@@ -54,7 +98,7 @@ class SearchApiQuery
|
||||
public function setWhereClauses(string $whereClause, array $params = []): self
|
||||
{
|
||||
$this->whereClauses = [$whereClause];
|
||||
$this->whereClausesParams = [$params];
|
||||
$this->whereClausesParams = $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -71,11 +115,53 @@ class SearchApiQuery
|
||||
public function andWhereClause(string $whereClause, array $params = []): self
|
||||
{
|
||||
$this->whereClauses[] = $whereClause;
|
||||
$this->whereClausesParams[] = $params;
|
||||
\array_push($this->whereClausesParams, ...$params);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function buildSelectParams(bool $count = false): array
|
||||
{
|
||||
if ($count) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$args = $this->getSelectParams();
|
||||
|
||||
if (null !== $this->selectKey) {
|
||||
$args = [...$args, ...$this->selectKeyParams];
|
||||
}
|
||||
if (null !== $this->jsonbMetadata) {
|
||||
$args = [...$args, ...$this->jsonbMetadataParams];
|
||||
}
|
||||
if (null !== $this->pertinence) {
|
||||
$args = [...$args, ...$this->pertinenceParams];
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
private function buildSelectClause(bool $countOnly = false): string
|
||||
{
|
||||
if ($countOnly) {
|
||||
return 'count(*) AS c';
|
||||
}
|
||||
|
||||
$selects = $this->getSelectClauses();
|
||||
|
||||
if (null !== $this->selectKey) {
|
||||
$selects[] = \strtr("'{key}' AS key", [ '{key}' => $this->selectKey ]);
|
||||
}
|
||||
if (null !== $this->jsonbMetadata) {
|
||||
$selects[] = \strtr('{metadata} AS metadata', [ '{metadata}' => $this->jsonbMetadata]);
|
||||
}
|
||||
if (null !== $this->pertinence) {
|
||||
$selects[] = \strtr('{pertinence} AS pertinence', [ '{pertinence}' => $this->pertinence]);
|
||||
}
|
||||
|
||||
return \implode(', ', $selects);
|
||||
}
|
||||
|
||||
public function buildQuery(bool $countOnly = false): string
|
||||
{
|
||||
$isMultiple = count($this->whereClauses);
|
||||
@@ -87,19 +173,8 @@ class SearchApiQuery
|
||||
($isMultiple ? ')' : '')
|
||||
;
|
||||
|
||||
if (!$countOnly) {
|
||||
$select = \strtr("
|
||||
'{key}' AS key,
|
||||
{metadata} AS metadata,
|
||||
{pertinence} AS pertinence
|
||||
", [
|
||||
'{key}' => $this->selectKey,
|
||||
'{metadata}' => $this->jsonbMetadata,
|
||||
'{pertinence}' => $this->pertinence,
|
||||
]);
|
||||
} else {
|
||||
$select = "1 AS c";
|
||||
}
|
||||
$select = $this->buildSelectClause($countOnly);
|
||||
|
||||
|
||||
return \strtr("SELECT
|
||||
{select}
|
||||
@@ -116,18 +191,16 @@ class SearchApiQuery
|
||||
public function buildParameters(bool $countOnly = false): array
|
||||
{
|
||||
if (!$countOnly) {
|
||||
return \array_merge(
|
||||
$this->selectKeyParams,
|
||||
$this->jsonbMetadataParams,
|
||||
$this->pertinenceParams,
|
||||
$this->fromClauseParams,
|
||||
\array_merge([], ...$this->whereClausesParams),
|
||||
);
|
||||
return [
|
||||
...$this->buildSelectParams($countOnly),
|
||||
...$this->fromClauseParams,
|
||||
...$this->whereClausesParams,
|
||||
];
|
||||
} else {
|
||||
return \array_merge(
|
||||
$this->fromClauseParams,
|
||||
\array_merge([], ...$this->whereClausesParams),
|
||||
);
|
||||
return [
|
||||
...$this->fromClauseParams,
|
||||
...$this->whereClausesParams,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
@@ -10,6 +12,8 @@ class SearchApiResult
|
||||
|
||||
private $result;
|
||||
|
||||
private float $relevance;
|
||||
|
||||
public function __construct(float $relevance)
|
||||
{
|
||||
$this->relevance = $relevance;
|
||||
@@ -20,7 +24,7 @@ class SearchApiResult
|
||||
$this->result = $result;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Serializer\Groups({"read"})
|
||||
|
@@ -10,10 +10,10 @@ use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
|
||||
* installed into the app.
|
||||
* the service is callable from the container with
|
||||
* $container->get('chill_main.search_provider')
|
||||
*
|
||||
* the syntax for search string is :
|
||||
* - domain, which begin with `@`. Example: `@person`. Restrict the search to some
|
||||
* entities. It may exists multiple search provider for the same domain (example:
|
||||
*
|
||||
* the syntax for search string is :
|
||||
* - domain, which begin with `@`. Example: `@person`. Restrict the search to some
|
||||
* entities. It may exists multiple search provider for the same domain (example:
|
||||
* a search provider for people which performs regular search, and suggestion search
|
||||
* with phonetical algorithms
|
||||
* - terms, which are the terms of the search. There are terms with argument (example :
|
||||
@@ -25,17 +25,17 @@ class SearchProvider
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @var SearchInterface[]
|
||||
*/
|
||||
private $searchServices = array();
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var HasAdvancedSearchForm[]
|
||||
*/
|
||||
private $hasAdvancedFormSearchServices = array();
|
||||
|
||||
|
||||
/*
|
||||
* return search services in an array, ordered by
|
||||
* the order key (defined in service definition)
|
||||
@@ -59,7 +59,7 @@ class SearchProvider
|
||||
|
||||
return $this->searchServices;
|
||||
}
|
||||
|
||||
|
||||
public function getHasAdvancedFormSearchServices()
|
||||
{
|
||||
//sort the array
|
||||
@@ -75,7 +75,7 @@ class SearchProvider
|
||||
|
||||
/**
|
||||
* parse the search string to extract domain and terms
|
||||
*
|
||||
*
|
||||
* @param string $pattern
|
||||
* @return string[] an array where the keys are _domain, _default (residual terms) or term
|
||||
*/
|
||||
@@ -95,9 +95,9 @@ class SearchProvider
|
||||
|
||||
/**
|
||||
* Extract the domain of the subject
|
||||
*
|
||||
*
|
||||
* The domain begins with `@`. Example: `@person`, `@report`, ....
|
||||
*
|
||||
*
|
||||
* @param type $subject
|
||||
* @return string
|
||||
* @throws ParsingException
|
||||
@@ -121,14 +121,15 @@ class SearchProvider
|
||||
private function extractTerms(&$subject)
|
||||
{
|
||||
$terms = array();
|
||||
preg_match_all('/([a-z\-]+):([\w\-]+|\([^\(\r\n]+\))/', $subject, $matches);
|
||||
$matches = [];
|
||||
preg_match_all('/([a-z\-]+):(([^"][\S\-]+)|"[^"]*")/', $subject, $matches);
|
||||
|
||||
foreach ($matches[2] as $key => $match) {
|
||||
//remove from search pattern
|
||||
$this->mustBeExtracted[] = $matches[0][$key];
|
||||
//strip parenthesis
|
||||
if (mb_substr($match, 0, 1) === '(' &&
|
||||
mb_substr($match, mb_strlen($match) - 1) === ')') {
|
||||
if (mb_substr($match, 0, 1) === '"' &&
|
||||
mb_substr($match, mb_strlen($match) - 1) === '"') {
|
||||
$match = trim(mb_substr($match, 1, mb_strlen($match) - 2));
|
||||
}
|
||||
$terms[$matches[1][$key]] = $match;
|
||||
@@ -139,14 +140,14 @@ class SearchProvider
|
||||
|
||||
/**
|
||||
* store string which must be extracted to find default arguments
|
||||
*
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $mustBeExtracted = array();
|
||||
|
||||
/**
|
||||
* extract default (residual) arguments
|
||||
*
|
||||
*
|
||||
* @param string $subject
|
||||
* @return string
|
||||
*/
|
||||
@@ -158,7 +159,7 @@ class SearchProvider
|
||||
/**
|
||||
* search through services which supports domain and give
|
||||
* results as an array of resultsfrom different SearchInterface
|
||||
*
|
||||
*
|
||||
* @param string $pattern
|
||||
* @param number $start
|
||||
* @param number $limit
|
||||
@@ -167,25 +168,25 @@ class SearchProvider
|
||||
* @return array of results from different SearchInterface
|
||||
* @throws UnknowSearchDomainException if the domain is unknow
|
||||
*/
|
||||
public function getSearchResults($pattern, $start = 0, $limit = 50,
|
||||
public function getSearchResults($pattern, $start = 0, $limit = 50,
|
||||
array $options = array(), $format = 'html')
|
||||
{
|
||||
$terms = $this->parse($pattern);
|
||||
$results = array();
|
||||
|
||||
|
||||
//sort searchServices by order
|
||||
$sortedSearchServices = array();
|
||||
foreach($this->searchServices as $service) {
|
||||
$sortedSearchServices[$service->getOrder()] = $service;
|
||||
}
|
||||
|
||||
|
||||
if ($terms['_domain'] !== NULL) {
|
||||
foreach ($sortedSearchServices as $service) {
|
||||
if ($service->supports($terms['_domain'], $format)) {
|
||||
$results[] = $service->renderResult($terms, $start, $limit, $options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (count($results) === 0) {
|
||||
throw new UnknowSearchDomainException($terms['_domain']);
|
||||
}
|
||||
@@ -196,24 +197,24 @@ class SearchProvider
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//sort array
|
||||
ksort($results);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
public function getResultByName($pattern, $name, $start = 0, $limit = 50,
|
||||
array $options = array(), $format = 'html')
|
||||
array $options = array(), $format = 'html')
|
||||
{
|
||||
$terms = $this->parse($pattern);
|
||||
$search = $this->getByName($name);
|
||||
|
||||
|
||||
if ($terms['_domain'] !== NULL && !$search->supports($terms['_domain'], $format))
|
||||
{
|
||||
throw new ParsingException("The domain is not supported for the search name");
|
||||
}
|
||||
|
||||
|
||||
return $search->renderResult($terms, $start, $limit, $options, $format);
|
||||
}
|
||||
|
||||
@@ -232,16 +233,16 @@ class SearchProvider
|
||||
throw new UnknowSearchNameException($name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return searchservice with an advanced form, defined in service
|
||||
* return searchservice with an advanced form, defined in service
|
||||
* definition.
|
||||
*
|
||||
*
|
||||
* @param string $name
|
||||
* @return HasAdvancedSearchForm
|
||||
* @throws UnknowSearchNameException
|
||||
*/
|
||||
public function getHasAdvancedFormByName($name)
|
||||
public function getHasAdvancedFormByName($name)
|
||||
{
|
||||
if (\array_key_exists($name, $this->hasAdvancedFormSearchServices)) {
|
||||
return $this->hasAdvancedFormSearchServices[$name];
|
||||
@@ -253,7 +254,7 @@ class SearchProvider
|
||||
public function addSearchService(SearchInterface $service, $name)
|
||||
{
|
||||
$this->searchServices[$name] = $service;
|
||||
|
||||
|
||||
if ($service instanceof HasAdvancedSearchFormInterface) {
|
||||
$this->hasAdvancedFormSearchServices[$name] = $service;
|
||||
}
|
||||
@@ -477,7 +478,7 @@ class SearchProvider
|
||||
$string = strtr($string, $chars);
|
||||
} /* remove from wordpress: we use only utf 8
|
||||
* else {
|
||||
|
||||
|
||||
// Assume ISO-8859-1 if not UTF-8
|
||||
$chars['in'] = chr(128) . chr(131) . chr(138) . chr(142) . chr(154) . chr(158)
|
||||
. chr(159) . chr(162) . chr(165) . chr(181) . chr(192) . chr(193) . chr(194)
|
||||
|
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Search\Utils;
|
||||
|
||||
use \DateTimeImmutable;
|
||||
|
||||
class ExtractDateFromPattern
|
||||
{
|
||||
private const DATE_PATTERN = [
|
||||
["([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))", 'Y-m-d'], // 1981-05-12
|
||||
["((0[1-9]|[12]\d|3[01])\/(0[1-9]|1[0-2])\/([12]\d{3}))", 'd/m/Y'], // 15/12/1980
|
||||
["((0[1-9]|[12]\d|3[01])-(0[1-9]|1[0-2])-([12]\d{3}))", 'd-m-Y'], // 15/12/1980
|
||||
];
|
||||
|
||||
public function extractDates(string $subject): SearchExtractionResult
|
||||
{
|
||||
$dates = [];
|
||||
$filteredSubject = $subject;
|
||||
|
||||
foreach (self::DATE_PATTERN as [$pattern, $format]) {
|
||||
$matches = [];
|
||||
\preg_match_all($pattern, $filteredSubject, $matches);
|
||||
|
||||
foreach ($matches[0] as $match) {
|
||||
$date = DateTimeImmutable::createFromFormat($format, $match);
|
||||
if (false !== $date) {
|
||||
$dates[] = $date;
|
||||
// filter string: remove what is found
|
||||
$filteredSubject = \trim(\strtr($filteredSubject, [$match => ""]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new SearchExtractionResult($filteredSubject, $dates);
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Search\Utils;
|
||||
|
||||
|
||||
class ExtractPhonenumberFromPattern
|
||||
{
|
||||
private const PATTERN = "([\+]{0,1}[0-9\ ]{5,})";
|
||||
|
||||
public function extractPhonenumber(string $subject): SearchExtractionResult
|
||||
{
|
||||
$matches = [];
|
||||
\preg_match(self::PATTERN, $subject,$matches);
|
||||
|
||||
if (0 < count($matches)) {
|
||||
$phonenumber = [];
|
||||
$length = 0;
|
||||
|
||||
foreach (\str_split(\trim($matches[0])) as $key => $char) {
|
||||
switch ($char) {
|
||||
case '0':
|
||||
$length++;
|
||||
if ($key === 0) { $phonenumber[] = '+32'; }
|
||||
else { $phonenumber[] = $char; }
|
||||
break;
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
$length++;
|
||||
$phonenumber[] = $char;
|
||||
break;
|
||||
case ' ':
|
||||
break;
|
||||
default:
|
||||
throw new \LogicException("should not match not alnum character");
|
||||
}
|
||||
}
|
||||
|
||||
if ($length > 5) {
|
||||
$filtered = \trim(\strtr($subject, [$matches[0] => '']));
|
||||
|
||||
return new SearchExtractionResult($filtered, [\implode('', $phonenumber)] );
|
||||
}
|
||||
}
|
||||
|
||||
return new SearchExtractionResult($subject, []);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\MainBundle\Search\Utils;
|
||||
|
||||
class SearchExtractionResult
|
||||
{
|
||||
private string $filteredSubject;
|
||||
private array $found;
|
||||
|
||||
public function __construct(string $filteredSubject, array $found)
|
||||
{
|
||||
$this->filteredSubject = $filteredSubject;
|
||||
$this->found = $found;
|
||||
}
|
||||
|
||||
public function getFound(): array
|
||||
{
|
||||
return $this->found;
|
||||
}
|
||||
|
||||
public function hasResult(): bool
|
||||
{
|
||||
return [] !== $this->found;
|
||||
}
|
||||
|
||||
public function getFilteredSubject(): string
|
||||
{
|
||||
return $this->filteredSubject;
|
||||
}
|
||||
}
|
@@ -1,34 +1,17 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* Voter for Chill software.
|
||||
*
|
||||
* This abstract Voter provide generic methods to handle object specific to Chill
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
||||
{
|
||||
@@ -39,6 +22,8 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
||||
. '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))
|
||||
&& \in_array(\get_class($subject), $this->getSupportedClasses());
|
||||
}
|
||||
@@ -49,7 +34,7 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
||||
. '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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,48 +1,24 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class ChillExportVoter extends Voter
|
||||
{
|
||||
const EXPORT = 'chill_export';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
public function __construct(AuthorizationHelper $authorizationHelper)
|
||||
public const EXPORT = 'chill_export';
|
||||
|
||||
protected AuthorizationHelperInterface $authorizationHelper;
|
||||
|
||||
public function __construct(AuthorizationHelperInterface $authorizationHelper)
|
||||
{
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
}
|
||||
|
||||
|
||||
protected function supports($attribute, $subject): bool
|
||||
{
|
||||
return $attribute === self::EXPORT;
|
||||
@@ -53,10 +29,7 @@ class ChillExportVoter extends Voter
|
||||
if (!$token->getUser() instanceof User) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$centers = $this->authorizationHelper
|
||||
->getReachableCenters($token->getUser(), new Role($attribute));
|
||||
|
||||
return count($centers) > 0;
|
||||
|
||||
return [] !== $this->authorizationHelper->getReachableCenters($token->getUser(), $attribute);
|
||||
}
|
||||
}
|
||||
|
@@ -1,27 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
class CenterResolverDispatcher
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
|
||||
/**
|
||||
* @deprecated Use CenterResolverManager and its interface CenterResolverManagerInterface
|
||||
*/
|
||||
final class CenterResolverDispatcher
|
||||
{
|
||||
/**
|
||||
* @var iterabble|CenterResolverInterface[]
|
||||
* @var CenterResolverInterface[]
|
||||
*/
|
||||
private iterable $resolvers = [];
|
||||
private iterable $resolvers;
|
||||
|
||||
public function __construct(iterable $resolvers)
|
||||
public function __construct(iterable $resolvers = [])
|
||||
{
|
||||
$this->resolvers = $resolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $entity
|
||||
* @param array|null $options
|
||||
* @param object $entity
|
||||
* @return null|Center|Center[]
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = [])
|
||||
{
|
||||
foreach($this->resolvers as $priority => $resolver) {
|
||||
trigger_deprecation(
|
||||
'ChillMainBundle',
|
||||
'dev-master',
|
||||
'
|
||||
Use the service CenterResolverManager through the interface CenterResolverManagerInterface.
|
||||
The new method "CenterResolverManagerInterface::resolveCenters(): array" is available and the typing
|
||||
has been improved in order to avoid mixing types.
|
||||
'
|
||||
);
|
||||
|
||||
foreach($this->resolvers as $resolver) {
|
||||
if ($resolver->supports($entity, $options)) {
|
||||
return $resolver->resolveCenter($entity, $options);
|
||||
}
|
||||
|
@@ -6,12 +6,14 @@ use Chill\MainBundle\Entity\Center;
|
||||
|
||||
interface CenterResolverInterface
|
||||
{
|
||||
/**
|
||||
* @param object $entity
|
||||
*/
|
||||
public function supports($entity, ?array $options = []): bool;
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
* @param array|null $options
|
||||
* @return Center|array|Center[]
|
||||
* @param object $entity
|
||||
* @return Center|Center[]
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = []);
|
||||
|
||||
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
|
||||
final class CenterResolverManager implements CenterResolverManagerInterface
|
||||
{
|
||||
/**
|
||||
* @var CenterResolverInterface[]
|
||||
*/
|
||||
private iterable $resolvers;
|
||||
|
||||
public function __construct(iterable $resolvers = [])
|
||||
{
|
||||
$this->resolvers = $resolvers;
|
||||
}
|
||||
|
||||
public function resolveCenters($entity, ?array $options = []): array
|
||||
{
|
||||
foreach($this->resolvers as $resolver) {
|
||||
if ($resolver->supports($entity, $options)) {
|
||||
$resolved = $resolver->resolveCenter($entity, $options);
|
||||
if (null === $resolved) {
|
||||
return [];
|
||||
} elseif ($resolved instanceof Center) {
|
||||
return [$resolved];
|
||||
} elseif (\is_array($resolved)) {
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException(sprintf("the return type of a %s should be an instance of %s, an array or null. Resolver is %s",
|
||||
CenterResolverInterface::class, Center::class, get_class($resolver)));
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Resolver;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
|
||||
interface CenterResolverManagerInterface
|
||||
{
|
||||
/**
|
||||
* @param object $entity
|
||||
* @return Center[]
|
||||
*/
|
||||
public function resolveCenters($entity, ?array $options = []): array;
|
||||
}
|
@@ -6,20 +6,21 @@ use Twig\TwigFilter;
|
||||
|
||||
final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
|
||||
{
|
||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||
private CenterResolverManagerInterface $centerResolverDispatcher;
|
||||
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||
|
||||
/**
|
||||
* @param CenterResolverDispatcher $centerResolverDispatcher
|
||||
*/
|
||||
public function __construct(CenterResolverDispatcher $centerResolverDispatcher)
|
||||
public function __construct(CenterResolverManagerInterface $centerResolverDispatcher, ScopeResolverDispatcher $scopeResolverDispatcher)
|
||||
{
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
$this->scopeResolverDispatcher = $scopeResolverDispatcher;
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('chill_resolve_center', [$this, 'resolveCenter'])
|
||||
new TwigFilter('chill_resolve_center', [$this, 'resolveCenter']),
|
||||
new TwigFilter('chill_resolve_scope', [$this, 'resolveScope']),
|
||||
new TwigFilter('chill_is_scope_concerned', [$this, 'isScopeConcerned']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -30,7 +31,26 @@ final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
|
||||
*/
|
||||
public function resolveCenter($entity, ?array $options = [])
|
||||
{
|
||||
return $this->centerResolverDispatcher->resolveCenter($entity, $options);
|
||||
return $this->centerResolverDispatcher->resolveCenters($entity, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
* @param array|null $options
|
||||
* @return bool
|
||||
*/
|
||||
public function isScopeConcerned($entity, ?array $options = [])
|
||||
{
|
||||
return $this->scopeResolverDispatcher->isConcerned($entity, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
* @param array|null $options
|
||||
* @return array|\Chill\MainBundle\Entity\Scope|\Chill\MainBundle\Entity\Scope[]
|
||||
*/
|
||||
public function resolveScope($entity, ?array $options = [])
|
||||
{
|
||||
return $this->scopeResolverDispatcher->resolveScope();
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,18 @@
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -27,7 +27,7 @@ use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class CenterNormalizer implements NormalizerInterface, DenormalizerInterface
|
||||
@@ -52,7 +52,7 @@ class CenterNormalizer implements NormalizerInterface, DenormalizerInterface
|
||||
|
||||
public function supportsNormalization($data, string $format = null): bool
|
||||
{
|
||||
return $data instanceof Center;
|
||||
return $data instanceof Center && $format === 'json';
|
||||
}
|
||||
|
||||
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||
|
@@ -19,23 +19,78 @@
|
||||
|
||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
|
||||
class DateNormalizer implements NormalizerInterface, DenormalizerInterface
|
||||
class DateNormalizer implements ContextAwareNormalizerInterface, DenormalizerInterface
|
||||
{
|
||||
private RequestStack $requestStack;
|
||||
private ParameterBagInterface $parameterBag;
|
||||
|
||||
public function __construct(RequestStack $requestStack, ParameterBagInterface $parameterBag)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->parameterBag = $parameterBag;
|
||||
}
|
||||
|
||||
public function normalize($date, string $format = null, array $context = array())
|
||||
{
|
||||
/** @var \DateTimeInterface $date */
|
||||
return [
|
||||
'datetime' => $date->format(\DateTimeInterface::ISO8601)
|
||||
];
|
||||
switch($format) {
|
||||
case 'json':
|
||||
return [
|
||||
'datetime' => $date->format(\DateTimeInterface::ISO8601)
|
||||
];
|
||||
case 'docgen':
|
||||
|
||||
if (null === $date) {
|
||||
return [
|
||||
'long' => '', 'short' => ''
|
||||
];
|
||||
}
|
||||
|
||||
$hasTime = $date->format('His') !== "000000";
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
$locale = null !== $request ? $request->getLocale() : $this->parameterBag->get('kernel.default_locale');
|
||||
$formatterLong = \IntlDateFormatter::create(
|
||||
$locale,
|
||||
\IntlDateFormatter::LONG,
|
||||
$hasTime ? \IntlDateFormatter::SHORT: \IntlDateFormatter::NONE
|
||||
);
|
||||
$formatterShort = \IntlDateFormatter::create(
|
||||
$locale,
|
||||
\IntlDateFormatter::SHORT,
|
||||
$hasTime ? \IntlDateFormatter::SHORT: \IntlDateFormatter::NONE
|
||||
);
|
||||
|
||||
return [
|
||||
'short' => $formatterShort->format($date),
|
||||
'long' => $formatterLong->format($date)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function supportsNormalization($data, string $format = null): bool
|
||||
public function supportsNormalization($data, string $format = null, array $context = []): bool
|
||||
{
|
||||
return $data instanceof \DateTimeInterface;
|
||||
if ($format === 'json') {
|
||||
return $data instanceof \DateTimeInterface;
|
||||
} elseif ($format === 'docgen') {
|
||||
return $data instanceof \DateTimeInterface || (
|
||||
$data === null
|
||||
&& \array_key_exists('docgen:expects', $context)
|
||||
&& (
|
||||
$context['docgen:expects'] === \DateTimeInterface::class
|
||||
|| $context['docgen:expects'] === \DateTime::class
|
||||
|| $context['docgen:expects'] === \DateTimeImmutable::class
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||
|
@@ -52,6 +52,6 @@ class UserNormalizer implements NormalizerInterface, NormalizerAwareInterface
|
||||
|
||||
public function supportsNormalization($data, string $format = null): bool
|
||||
{
|
||||
return $data instanceof User;
|
||||
return $format === 'json' && $data instanceof User;
|
||||
}
|
||||
}
|
||||
|
@@ -1,92 +1,56 @@
|
||||
<?php
|
||||
/*
|
||||
* Chill is a suite of a modules, Chill is a software for social workers
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Templating;
|
||||
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
* This helper helps to find the string in current locale from translatable_strings
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
*/
|
||||
class TranslatableStringHelper
|
||||
final class TranslatableStringHelper implements TranslatableStringHelperInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var RequestStack
|
||||
*/
|
||||
private $requestStack;
|
||||
|
||||
private $fallbackLocales;
|
||||
|
||||
public function __construct(RequestStack $requestStack, Translator $translator)
|
||||
private RequestStack $requestStack;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(RequestStack $requestStack, TranslatorInterface $translator)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->fallbackLocales = $translator->getFallbackLocales();
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the string in current locale if it exists.
|
||||
*
|
||||
* If it does not exists; return the name in the first language available.
|
||||
*
|
||||
* Return a blank string if any strings are available.
|
||||
* Return NULL if $translatableString is NULL
|
||||
*
|
||||
* @param array $translatableStrings
|
||||
* @return string
|
||||
*/
|
||||
public function localize(array $translatableStrings)
|
||||
{
|
||||
if (NULL === $translatableStrings) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$language = $this->requestStack->getCurrentRequest()->getLocale();
|
||||
|
||||
|
||||
if (isset($translatableStrings[$language])) {
|
||||
|
||||
return $translatableStrings[$language];
|
||||
} else {
|
||||
foreach ($this->fallbackLocales as $locale) {
|
||||
if (array_key_exists($locale, $translatableStrings)) {
|
||||
|
||||
return $translatableStrings[$locale];
|
||||
}
|
||||
}
|
||||
|
||||
public function localize(array $translatableStrings): ?string
|
||||
{
|
||||
if ([] === $translatableStrings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
if (null === $request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$language = $request->getLocale();
|
||||
|
||||
if (array_key_exists($language, $translatableStrings)) {
|
||||
return $translatableStrings[$language];
|
||||
}
|
||||
|
||||
foreach ($this->translator->getFallbackLocales() as $locale) {
|
||||
if (array_key_exists($locale, $translatableStrings)) {
|
||||
return $translatableStrings[$locale];
|
||||
}
|
||||
}
|
||||
|
||||
// no fallback translation... trying the first available
|
||||
$langs = array_keys($translatableStrings);
|
||||
|
||||
if (count($langs) === 0) {
|
||||
|
||||
if ([] === $langs) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $translatableStrings[$langs[0]];
|
||||
|
||||
return $translatableStrings[$langs[0]];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Templating;
|
||||
|
||||
interface TranslatableStringHelperInterface
|
||||
{
|
||||
/**
|
||||
* Return the string in current locale if it exists.
|
||||
*
|
||||
* If it does not exists; return the name in the first language available.
|
||||
*
|
||||
* Return a blank string if any strings are available.
|
||||
*/
|
||||
public function localize(array $translatableStrings): ?string;
|
||||
}
|
@@ -12,7 +12,7 @@ class SearchApiQueryTest extends TestCase
|
||||
$q = new SearchApiQuery();
|
||||
$q->setSelectJsonbMetadata('boum')
|
||||
->setSelectKey('bim')
|
||||
->setSelectPertinence('1')
|
||||
->setSelectPertinence('?', ['gamma'])
|
||||
->setFromClause('badaboum')
|
||||
->andWhereClause('foo', [ 'alpha' ])
|
||||
->andWhereClause('bar', [ 'beta' ])
|
||||
@@ -21,12 +21,12 @@ class SearchApiQueryTest extends TestCase
|
||||
$query = $q->buildQuery();
|
||||
|
||||
$this->assertStringContainsString('(foo) AND (bar)', $query);
|
||||
$this->assertEquals(['alpha', 'beta'], $q->buildParameters());
|
||||
$this->assertEquals(['gamma', 'alpha', 'beta'], $q->buildParameters());
|
||||
|
||||
$query = $q->buildQuery(true);
|
||||
|
||||
$this->assertStringContainsString('(foo) AND (bar)', $query);
|
||||
$this->assertEquals(['alpha', 'beta'], $q->buildParameters());
|
||||
$this->assertEquals(['gamma', 'alpha', 'beta'], $q->buildParameters());
|
||||
}
|
||||
|
||||
public function testWithoutWhereClause()
|
||||
@@ -42,4 +42,20 @@ class SearchApiQueryTest extends TestCase
|
||||
$this->assertEquals([], $q->buildParameters());
|
||||
}
|
||||
|
||||
public function testBuildParams()
|
||||
{
|
||||
$q = new SearchApiQuery();
|
||||
|
||||
$q
|
||||
->addSelectClause('bada', [ 'one', 'two' ])
|
||||
->addSelectClause('boum', ['three', 'four'])
|
||||
->setWhereClauses('mywhere', [ 'six', 'seven'])
|
||||
;
|
||||
|
||||
$params = $q->buildParameters();
|
||||
|
||||
$this->assertEquals(['six', 'seven'], $q->buildParameters(true));
|
||||
$this->assertEquals(['one', 'two', 'three', 'four', 'six', 'seven'], $q->buildParameters());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -26,17 +26,17 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SearchProviderTest extends TestCase
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var SearchProvider
|
||||
* @var SearchProvider
|
||||
*/
|
||||
private $search;
|
||||
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->search = new SearchProvider();
|
||||
|
||||
|
||||
//add a default service
|
||||
$this->addSearchService(
|
||||
$this->createDefaultSearchService('I am default', 10), 'default'
|
||||
@@ -46,7 +46,7 @@ class SearchProviderTest extends TestCase
|
||||
$this->createNonDefaultDomainSearchService('I am domain bar', 20, FALSE), 'bar'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException \Chill\MainBundle\Search\UnknowSearchNameException
|
||||
*/
|
||||
@@ -54,11 +54,11 @@ class SearchProviderTest extends TestCase
|
||||
{
|
||||
$this->search->getByName("invalid name");
|
||||
}
|
||||
|
||||
|
||||
public function testSimplePattern()
|
||||
{
|
||||
$terms = $this->p("@person birthdate:2014-01-02 name:(my name) is not my name");
|
||||
|
||||
$terms = $this->p("@person birthdate:2014-01-02 name:\"my name\" is not my name");
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => 'person',
|
||||
'birthdate' => '2014-01-02',
|
||||
@@ -66,40 +66,40 @@ class SearchProviderTest extends TestCase
|
||||
'name' => 'my name'
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
public function testWithoutDomain()
|
||||
{
|
||||
$terms = $this->p('foo:bar residual');
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => null,
|
||||
'foo' => 'bar',
|
||||
'_default' => 'residual'
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
public function testWithoutDefault()
|
||||
{
|
||||
$terms = $this->p('@person foo:bar');
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => 'person',
|
||||
'foo' => 'bar',
|
||||
'_default' => ''
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
public function testCapitalLetters()
|
||||
{
|
||||
$terms = $this->p('Foo:Bar LOL marCi @PERSON');
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => 'person',
|
||||
'_default' => 'lol marci',
|
||||
'foo' => 'bar'
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException Chill\MainBundle\Search\ParsingException
|
||||
*/
|
||||
@@ -107,12 +107,11 @@ class SearchProviderTest extends TestCase
|
||||
{
|
||||
$term = $this->p("@person @report");
|
||||
}
|
||||
|
||||
|
||||
public function testDoubleParenthesis()
|
||||
{
|
||||
$terms = $this->p("@papamobile name:(my beautiful name) residual "
|
||||
. "surname:(i love techno)");
|
||||
|
||||
$terms = $this->p('@papamobile name:"my beautiful name" residual surname:"i love techno"');
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => 'papamobile',
|
||||
'name' => 'my beautiful name',
|
||||
@@ -120,65 +119,65 @@ class SearchProviderTest extends TestCase
|
||||
'surname' => 'i love techno'
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
public function testAccentued()
|
||||
{
|
||||
//$this->markTestSkipped('accentued characters must be implemented');
|
||||
|
||||
|
||||
$terms = $this->p('manço bélier aztèque à saloù ê');
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => NULL,
|
||||
'_default' => 'manco belier azteque a salou e'
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
public function testAccentuedCapitals()
|
||||
{
|
||||
//$this->markTestSkipped('accentued characters must be implemented');
|
||||
|
||||
|
||||
$terms = $this->p('MANÉÀ oÛ lÎ À');
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => null,
|
||||
'_default' => 'manea ou li a'
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
public function testTrimInParenthesis()
|
||||
{
|
||||
$terms = $this->p('foo:(bar )');
|
||||
|
||||
$terms = $this->p('foo:"bar "');
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => null,
|
||||
'foo' => 'bar',
|
||||
'_default' => ''
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
public function testTrimInDefault()
|
||||
{
|
||||
$terms = $this->p(' foo bar ');
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => null,
|
||||
'_default' => 'foo bar'
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
public function testArgumentNameWithTrait()
|
||||
{
|
||||
$terms = $this->p('date-from:2016-05-04');
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
'_domain' => null,
|
||||
'date-from' => '2016-05-04',
|
||||
'_default' => ''
|
||||
), $terms);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test the behaviour when no domain is provided in the search pattern :
|
||||
* Test the behaviour when no domain is provided in the search pattern :
|
||||
* the default search should be enabled
|
||||
*/
|
||||
public function testSearchResultDefault()
|
||||
@@ -186,12 +185,12 @@ class SearchProviderTest extends TestCase
|
||||
$response = $this->search->getSearchResults('default search');
|
||||
|
||||
//$this->markTestSkipped();
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
"I am default"
|
||||
), $response);
|
||||
), $response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException \Chill\MainBundle\Search\UnknowSearchDomainException
|
||||
*/
|
||||
@@ -200,49 +199,49 @@ class SearchProviderTest extends TestCase
|
||||
$response = $this->search->getSearchResults('@unknow domain');
|
||||
|
||||
//$this->markTestSkipped();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testSearchResultDomainSearch()
|
||||
{
|
||||
//add a search service which will be supported
|
||||
$this->addSearchService(
|
||||
$this->createNonDefaultDomainSearchService("I am domain foo", 100, TRUE), 'foo'
|
||||
);
|
||||
|
||||
|
||||
$response = $this->search->getSearchResults('@foo default search');
|
||||
|
||||
|
||||
$this->assertEquals(array(
|
||||
"I am domain foo"
|
||||
), $response);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testSearchWithinSpecificSearchName()
|
||||
{
|
||||
//add a search service which will be supported
|
||||
$this->addSearchService(
|
||||
$this->createNonDefaultDomainSearchService("I am domain foo", 100, TRUE), 'foo'
|
||||
);
|
||||
|
||||
|
||||
$response = $this->search->getResultByName('@foo search', 'foo');
|
||||
|
||||
|
||||
$this->assertEquals('I am domain foo', $response);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException \Chill\MainBundle\Search\ParsingException
|
||||
*/
|
||||
public function testSearchWithinSpecificSearchNameInConflictWithSupport()
|
||||
{
|
||||
$response = $this->search->getResultByName('@foo default search', 'bar');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* shortcut for executing parse method
|
||||
*
|
||||
*
|
||||
* @param unknown $pattern
|
||||
* @return string[]
|
||||
*/
|
||||
@@ -250,12 +249,12 @@ class SearchProviderTest extends TestCase
|
||||
{
|
||||
return $this->search->parse($pattern);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a search service to the chill.main.search_provider
|
||||
*
|
||||
*
|
||||
* Useful for mocking the SearchInterface
|
||||
*
|
||||
*
|
||||
* @param SearchInterface $search
|
||||
* @param string $name
|
||||
*/
|
||||
@@ -264,52 +263,52 @@ class SearchProviderTest extends TestCase
|
||||
$this->search
|
||||
->addSearchService($search, $name);
|
||||
}
|
||||
|
||||
|
||||
private function createDefaultSearchService($result, $order)
|
||||
{
|
||||
$mock = $this
|
||||
->getMockForAbstractClass('Chill\MainBundle\Search\AbstractSearch');
|
||||
|
||||
|
||||
//set the mock as default
|
||||
$mock->expects($this->any())
|
||||
->method('isActiveByDefault')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getOrder')
|
||||
->will($this->returnValue($order));
|
||||
|
||||
|
||||
//set the return value
|
||||
$mock->expects($this->any())
|
||||
->method('renderResult')
|
||||
->will($this->returnValue($result));
|
||||
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
|
||||
private function createNonDefaultDomainSearchService($result, $order, $domain)
|
||||
{
|
||||
$mock = $this
|
||||
->getMockForAbstractClass('Chill\MainBundle\Search\AbstractSearch');
|
||||
|
||||
|
||||
//set the mock as default
|
||||
$mock->expects($this->any())
|
||||
->method('isActiveByDefault')
|
||||
->will($this->returnValue(FALSE));
|
||||
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getOrder')
|
||||
->will($this->returnValue($order));
|
||||
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('supports')
|
||||
->will($this->returnValue($domain));
|
||||
|
||||
|
||||
//set the return value
|
||||
$mock->expects($this->any())
|
||||
->method('renderResult')
|
||||
->will($this->returnValue($result));
|
||||
|
||||
|
||||
return $mock;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Search\Utils;
|
||||
|
||||
use Chill\MainBundle\Search\Utils\ExtractDateFromPattern;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ExtractDateFromPatternTest extends TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @dataProvider provideSubjects
|
||||
*/
|
||||
public function testExtractDate(string $subject, string $filtered, int $count, ...$datesSearched)
|
||||
{
|
||||
$extractor = new ExtractDateFromPattern();
|
||||
$result = $extractor->extractDates($subject);
|
||||
|
||||
$this->assertCount($count, $result->getFound());
|
||||
$this->assertEquals($filtered, $result->getFilteredSubject());
|
||||
$this->assertContainsOnlyInstancesOf(\DateTimeImmutable::class, $result->getFound());
|
||||
|
||||
$dates = \array_map(
|
||||
function (\DateTimeImmutable $d) {
|
||||
return $d->format('Y-m-d');
|
||||
}, $result->getFound()
|
||||
);
|
||||
|
||||
foreach ($datesSearched as $date) {
|
||||
$this->assertContains($date, $dates);
|
||||
}
|
||||
}
|
||||
|
||||
public function provideSubjects()
|
||||
{
|
||||
yield ["15/06/1981", "", 1, '1981-06-15'];
|
||||
yield ["15/06/1981 30/12/1987", "", 2, '1981-06-15', '1987-12-30'];
|
||||
yield ["diallo 15/06/1981", "diallo", 1, '1981-06-15'];
|
||||
yield ["diallo 31/03/1981", "diallo", 1, '1981-03-31'];
|
||||
yield ["diallo 15-06-1981", "diallo", 1, '1981-06-15'];
|
||||
yield ["diallo 1981-12-08", "diallo", 1, '1981-12-08'];
|
||||
yield ["diallo", "diallo", 0];
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Search\Utils;
|
||||
|
||||
use Chill\MainBundle\Search\Utils\ExtractPhonenumberFromPattern;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
class ExtractPhonenumberFromPatternTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData
|
||||
*/
|
||||
public function testExtract($subject, $expectedCount, $expected, $filteredSubject, $msg)
|
||||
{
|
||||
$extractor = new ExtractPhonenumberFromPattern();
|
||||
$result = $extractor->extractPhonenumber($subject);
|
||||
|
||||
$this->assertCount($expectedCount, $result->getFound());
|
||||
$this->assertEquals($filteredSubject, $result->getFilteredSubject());
|
||||
$this->assertEquals($expected, $result->getFound());
|
||||
}
|
||||
|
||||
public function provideData()
|
||||
{
|
||||
yield ['Diallo', 0, [], 'Diallo', "no phonenumber"];
|
||||
yield ['Diallo 15/06/2021', 0, [], 'Diallo 15/06/2021', "no phonenumber and a date"];
|
||||
yield ['Diallo 0486 123 456', 1, ['+32486123456'], 'Diallo', "a phonenumber and a name"];
|
||||
yield ['Diallo 123 456', 1, ['123456'], 'Diallo', "a number and a name, without leadiing 0"];
|
||||
yield ['123 456', 1, ['123456'], '', "only phonenumber"];
|
||||
yield ['0123 456', 1, ['+32123456'], '', "only phonenumber with a leading 0"];
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Serializer\Normalizer;
|
||||
|
||||
use Chill\MainBundle\Serializer\Normalizer\DateNormalizer;
|
||||
use Prophecy\Prophet;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
class DateNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private Prophet $prophet;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->prophet = new Prophet();
|
||||
}
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'json'));
|
||||
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTimeImmutable(), 'json'));
|
||||
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'docgen'));
|
||||
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTimeImmutable(), 'docgen'));
|
||||
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTimeImmutable::class]));
|
||||
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTimeInterface::class]));
|
||||
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTime::class]));
|
||||
$this->assertFalse($this->buildDateNormalizer()->supportsNormalization(new \stdClass(), 'docgen'));
|
||||
$this->assertFalse($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'xml'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider generateDataNormalize
|
||||
*/
|
||||
public function testNormalize($expected, $date, $format, $locale, $msg)
|
||||
{
|
||||
$this->assertEquals($expected, $this->buildDateNormalizer($locale)->normalize($date, $format, []), $msg);
|
||||
}
|
||||
|
||||
private function buildDateNormalizer(string $locale = null): DateNormalizer
|
||||
{
|
||||
$requestStack = $this->prophet->prophesize(RequestStack::class);
|
||||
$parameterBag = new ParameterBag();
|
||||
$parameterBag->set('kernel.default_locale', 'fr');
|
||||
|
||||
if ($locale === null) {
|
||||
$requestStack->getCurrentRequest()->willReturn(null);
|
||||
} else {
|
||||
$request = $this->prophet->prophesize(Request::class);
|
||||
$request->getLocale()->willReturn($locale);
|
||||
$requestStack->getCurrentRequest()->willReturn($request->reveal());
|
||||
}
|
||||
|
||||
return new DateNormalizer($requestStack->reveal(), $parameterBag);
|
||||
}
|
||||
|
||||
public function generateDataNormalize()
|
||||
{
|
||||
$datetime = \DateTime::createFromFormat('Y-m-d H:i:sO', '2021-06-05 15:05:01+02:00');
|
||||
$date = \DateTime::createFromFormat('Y-m-d H:i:sO', '2021-06-05 00:00:00+02:00');
|
||||
yield [
|
||||
['datetime' => '2021-06-05T15:05:01+0200'],
|
||||
$datetime, 'json', null, 'simple normalization to json'
|
||||
];
|
||||
|
||||
yield [
|
||||
['long' => '5 juin 2021', 'short' => '05/06/2021'],
|
||||
$date, 'docgen', 'fr', 'normalization to docgen for a date, with current request'
|
||||
];
|
||||
|
||||
yield [
|
||||
['long' => '5 juin 2021', 'short' => '05/06/2021'],
|
||||
$date, 'docgen', null, 'normalization to docgen for a date, without current request'
|
||||
];
|
||||
|
||||
yield [
|
||||
['long' => '5 juin 2021 à 15:05', 'short' => '05/06/2021 15:05'],
|
||||
$datetime, 'docgen', null, 'normalization to docgen for a datetime, without current request'
|
||||
];
|
||||
|
||||
yield [
|
||||
['long' => '', 'short' => ''],
|
||||
null, 'docgen', null, 'normalization to docgen for a null datetime'
|
||||
];
|
||||
}
|
||||
}
|
@@ -8,38 +8,29 @@ services:
|
||||
|
||||
Chill\MainBundle\Repository\:
|
||||
resource: '../Repository/'
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Repository\UserACLAwareRepositoryInterface: '@Chill\MainBundle\Repository\UserACLAwareRepository'
|
||||
|
||||
Chill\MainBundle\Serializer\Normalizer\:
|
||||
resource: '../Serializer/Normalizer'
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
tags:
|
||||
- { name: 'serializer.normalizer', priority: 64 }
|
||||
|
||||
Chill\MainBundle\Form\Type\:
|
||||
resource: '../Form/Type'
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
Chill\MainBundle\Doctrine\Event\:
|
||||
resource: '../Doctrine/Event/'
|
||||
autowire: true
|
||||
tags:
|
||||
- { name: 'doctrine.event_subscriber' }
|
||||
|
||||
chill.main.helper.translatable_string:
|
||||
class: Chill\MainBundle\Templating\TranslatableStringHelper
|
||||
arguments:
|
||||
- "@request_stack"
|
||||
- "@translator.default"
|
||||
|
||||
Chill\MainBundle\Templating\TranslatableStringHelper: '@chill.main.helper.translatable_string'
|
||||
Chill\MainBundle\Templating\TranslatableStringHelperInterface: '@Chill\MainBundle\Templating\TranslatableStringHelper'
|
||||
|
||||
chill.main.twig.translatable_string:
|
||||
class: Chill\MainBundle\Templating\TranslatableStringTwig
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill_main.tag_aware_cache:
|
||||
class: Symfony\Component\Cache\Adapter\TagAwareAdapter
|
||||
arguments:
|
||||
|
@@ -1,10 +1,9 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Command\ChillImportUsersCommand:
|
||||
arguments:
|
||||
$em: '@Doctrine\ORM\EntityManagerInterface'
|
||||
$logger: '@Psr\Log\LoggerInterface'
|
||||
$passwordEncoder: '@Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface'
|
||||
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
|
||||
tags:
|
||||
- { name: console.command }
|
||||
|
||||
|
@@ -1,12 +1,13 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Controller\:
|
||||
autowire: true
|
||||
resource: '../../Controller'
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
Chill\MainBundle\Controller\PasswordController:
|
||||
autowire: true
|
||||
arguments:
|
||||
$chillLogger: '@monolog.logger.chill'
|
||||
tags: ['controller.service_arguments']
|
||||
@@ -28,10 +29,6 @@ services:
|
||||
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
Chill\MainBundle\Controller\UserController:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Controller\NotificationController:
|
||||
arguments:
|
||||
$security: '@Symfony\Component\Security\Core\Security'
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader:
|
||||
arguments:
|
||||
$crudConfig: '%chill_main_crud_route_loader_config%'
|
||||
|
@@ -1,3 +1,7 @@
|
||||
---
|
||||
services:
|
||||
'Chill\MainBundle\Doctrine\Migrations\VersionComparator': ~
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Doctrine\Migrations\VersionComparator: ~
|
||||
|
@@ -1,9 +1,13 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill.main.export_element_validator:
|
||||
class: Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraintValidator
|
||||
tags:
|
||||
- { name: validator.constraint_validator }
|
||||
|
||||
|
||||
# deprecated in favor of spreadsheet_formatter
|
||||
# chill.main.export.csv_formatter:
|
||||
# class: Chill\MainBundle\Export\Formatter\CSVFormatter
|
||||
@@ -11,7 +15,7 @@ services:
|
||||
# - "@translator"
|
||||
# tags:
|
||||
# - { name: chill.export_formatter, alias: 'csv' }
|
||||
|
||||
|
||||
chill.main.export.spreadsheet_formatter:
|
||||
class: Chill\MainBundle\Export\Formatter\SpreadSheetFormatter
|
||||
arguments:
|
||||
@@ -19,7 +23,7 @@ services:
|
||||
$exportManager: '@Chill\MainBundle\Export\ExportManager'
|
||||
tags:
|
||||
- { name: chill.export_formatter, alias: 'spreadsheet' }
|
||||
|
||||
|
||||
chill.main.export.list_formatter:
|
||||
class: Chill\MainBundle\Export\Formatter\CSVListFormatter
|
||||
arguments:
|
||||
@@ -27,7 +31,7 @@ services:
|
||||
$exportManager: '@Chill\MainBundle\Export\ExportManager'
|
||||
tags:
|
||||
- { name: chill.export_formatter, alias: 'csvlist' }
|
||||
|
||||
|
||||
chill.main.export.list_spreadsheet_formatter:
|
||||
class: Chill\MainBundle\Export\Formatter\SpreadsheetListFormatter
|
||||
arguments:
|
||||
@@ -35,7 +39,7 @@ services:
|
||||
$exportManager: '@Chill\MainBundle\Export\ExportManager'
|
||||
tags:
|
||||
- { name: chill.export_formatter, alias: 'spreadlist' }
|
||||
|
||||
|
||||
chill.main.export.pivoted_list_formatter:
|
||||
class: Chill\MainBundle\Export\Formatter\CSVPivotedListFormatter
|
||||
arguments:
|
||||
@@ -43,4 +47,3 @@ services:
|
||||
$exportManager: '@Chill\MainBundle\Export\ExportManager'
|
||||
tags:
|
||||
- { name: chill.export_formatter, alias: 'csv_pivoted_list' }
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\DataFixtures\ORM\:
|
||||
resource: ../../DataFixtures/ORM
|
||||
tags: [ 'doctrine.fixture.orm' ]
|
||||
|
@@ -1,4 +1,7 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill.main.form.type.translatable.string:
|
||||
class: Chill\MainBundle\Form\Type\TranslatableStringFormType
|
||||
@@ -39,9 +42,7 @@ services:
|
||||
tags:
|
||||
- { name: form.type, alias: select2_chill_language }
|
||||
|
||||
Chill\MainBundle\Form\Type\PickCenterType:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
Chill\MainBundle\Form\Type\PickCenterType: ~
|
||||
|
||||
chill.main.form.type.composed_role_scope:
|
||||
class: Chill\MainBundle\Form\Type\ComposedRoleScopeType
|
||||
@@ -62,9 +63,7 @@ services:
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
Chill\MainBundle\Form\ChoiceLoader\PostalCodeChoiceLoader:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
Chill\MainBundle\Form\ChoiceLoader\PostalCodeChoiceLoader: ~
|
||||
|
||||
chill.main.form.type.export:
|
||||
class: Chill\MainBundle\Form\Type\Export\ExportType
|
||||
@@ -96,14 +95,10 @@ services:
|
||||
arguments:
|
||||
- '@Chill\MainBundle\Export\ExportManager'
|
||||
|
||||
Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer: ~
|
||||
|
||||
chill.main.form.advanced_search_type:
|
||||
class: Chill\MainBundle\Form\AdvancedSearchType
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
arguments:
|
||||
- "@chill_main.search_provider"
|
||||
tags:
|
||||
@@ -116,9 +111,7 @@ services:
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
Chill\MainBundle\Form\UserType:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
Chill\MainBundle\Form\UserType: ~
|
||||
|
||||
Chill\MainBundle\Form\PermissionsGroupType:
|
||||
tags:
|
||||
@@ -131,10 +124,9 @@ services:
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
Chill\MainBundle\Form\Type\PickAddressType: ~
|
||||
|
||||
Chill\MainBundle\Form\Type\PickAddressType:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
Chill\MainBundle\Form\DataTransform\AddressToIdDataTransformer: ~
|
||||
|
||||
Chill\MainBundle\Form\DataTransform\AddressToIdDataTransformer:
|
||||
autoconfigure: true
|
||||
@@ -147,3 +139,5 @@ services:
|
||||
Chill\MainBundle\Form\UserCurrentLocationType:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Form\Type\LocationFormType: ~
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill.main.logger:
|
||||
# a logger to log events from the app (deletion, remove, etc.)
|
||||
alias: monolog.logger.chill
|
||||
|
@@ -1,9 +1,11 @@
|
||||
services:
|
||||
Chill\MainBundle\Routing\MenuBuilder\:
|
||||
resource: '../../Routing/MenuBuilder'
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Routing\MenuBuilder\:
|
||||
resource: '../../Routing/MenuBuilder'
|
||||
|
||||
Chill\MainBundle\Routing\MenuBuilder\UserMenuBuilder:
|
||||
arguments:
|
||||
$tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Notification\Mailer:
|
||||
arguments:
|
||||
$logger: '@Psr\Log\LoggerInterface'
|
||||
@@ -9,6 +13,4 @@ services:
|
||||
$translator: '@Symfony\Component\Translation\TranslatorInterface'
|
||||
$routeParameters: '%chill_main.notifications%'
|
||||
|
||||
Chill\MainBundle\Notification\NotificationRenderer:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
Chill\MainBundle\Notification\NotificationRenderer: ~
|
||||
|
@@ -1,9 +1,11 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill_main.paginator_factory:
|
||||
class: Chill\MainBundle\Pagination\PaginatorFactory
|
||||
public: true
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
arguments:
|
||||
- "@request_stack"
|
||||
- "@router"
|
||||
|
@@ -1,19 +1,21 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Phonenumber\PhonenumberHelper:
|
||||
arguments:
|
||||
$logger: '@Psr\Log\LoggerInterface'
|
||||
$config: '%chill_main.phone_helper%'
|
||||
$cachePool: '@cache.user_data'
|
||||
|
||||
|
||||
Chill\MainBundle\Phonenumber\Templating:
|
||||
arguments:
|
||||
$phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
|
||||
|
||||
Chill\MainBundle\Validation\Validator\ValidPhonenumber:
|
||||
arguments:
|
||||
$logger: '@Psr\Log\LoggerInterface'
|
||||
$phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
|
||||
tags:
|
||||
- { name: validator.constraint_validator }
|
||||
|
@@ -1,10 +1,13 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Redis\RedisConnectionFactory:
|
||||
arguments:
|
||||
$parameters: "%chill_main.redis%"
|
||||
tags:
|
||||
- { name: kernel.event_subcriber }
|
||||
|
||||
|
||||
Chill\MainBundle\Redis\ChillRedis:
|
||||
factory: [ '@Chill\MainBundle\Redis\RedisConnectionFactory', 'create' ]
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill.main.menu_composer:
|
||||
class: Chill\MainBundle\Routing\MenuComposer
|
||||
arguments:
|
||||
@@ -6,7 +10,7 @@ services:
|
||||
- '@Knp\Menu\FactoryInterface'
|
||||
- '@Symfony\Component\Translation\TranslatorInterface'
|
||||
Chill\MainBundle\Routing\MenuComposer: '@chill.main.menu_composer'
|
||||
|
||||
|
||||
chill.main.routes_loader:
|
||||
class: Chill\MainBundle\Routing\Loader\ChillRoutesLoader
|
||||
arguments:
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill_main.search_provider:
|
||||
class: Chill\MainBundle\Search\SearchProvider
|
||||
|
||||
@@ -7,8 +11,13 @@ services:
|
||||
Chill\MainBundle\Search\SearchApi:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
arguments:
|
||||
$providers: !tagged_iterator chill.search_api_provider
|
||||
|
||||
Chill\MainBundle\Search\Entity\:
|
||||
resource: '../../Search/Entity'
|
||||
|
||||
Chill\MainBundle\Search\Utils\:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
resource: '../../Search/Entity'
|
||||
resource: './../Search/Utils/'
|
||||
|
@@ -3,46 +3,35 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
# do not autowire the directory Security/Resolver
|
||||
Chill\MainBundle\Security\Resolver\CenterResolverDispatcher:
|
||||
arguments:
|
||||
- !tagged_iterator chill_main.center_resolver
|
||||
|
||||
Chill\MainBundle\Security\Resolver\CenterResolverManager:
|
||||
arguments:
|
||||
- !tagged_iterator chill_main.center_resolver
|
||||
Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface: '@Chill\MainBundle\Security\Resolver\CenterResolverManager'
|
||||
|
||||
Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher:
|
||||
arguments:
|
||||
- !tagged_iterator chill_main.scope_resolver
|
||||
|
||||
# do not autowire the directory Security/Resolver
|
||||
Chill\MainBundle\Security\Resolver\DefaultCenterResolver:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
Chill\MainBundle\Security\Resolver\DefaultCenterResolver: ~
|
||||
|
||||
Chill\MainBundle\Security\Resolver\DefaultScopeResolver:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
Chill\MainBundle\Security\Resolver\DefaultScopeResolver: ~
|
||||
|
||||
# do not autowire the directory Security/Resolver
|
||||
Chill\MainBundle\Security\Resolver\ResolverTwigExtension:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
Chill\MainBundle\Security\Resolver\ResolverTwigExtension: ~
|
||||
|
||||
# do not autowire the directory Security/Resolver
|
||||
Chill\MainBundle\Security\Authorization\DefaultVoterHelperFactory:
|
||||
autowire: true
|
||||
Chill\MainBundle\Security\Authorization\DefaultVoterHelperFactory: ~
|
||||
|
||||
# do not autowire the directory Security/Resolver
|
||||
Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface: '@Chill\MainBundle\Security\Authorization\DefaultVoterHelperFactory'
|
||||
|
||||
chill.main.security.authorization.helper:
|
||||
class: Chill\MainBundle\Security\Authorization\AuthorizationHelper
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
Chill\MainBundle\Security\Authorization\AuthorizationHelper: '@chill.main.security.authorization.helper'
|
||||
Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface: '@chill.main.security.authorization.helper'
|
||||
|
||||
Chill\MainBundle\Security\ParentRoleHelper:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
Chill\MainBundle\Security\ParentRoleHelper: ~
|
||||
|
||||
chill.main.role_provider:
|
||||
class: Chill\MainBundle\Security\RoleProvider
|
||||
@@ -54,20 +43,16 @@ services:
|
||||
Symfony\Component\Security\Core\User\UserProviderInterface: "@chill.main.user_provider"
|
||||
|
||||
Chill\MainBundle\Security\Authorization\ChillExportVoter:
|
||||
arguments:
|
||||
$authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
|
||||
tags:
|
||||
- { name: security.voter }
|
||||
|
||||
Chill\MainBundle\Security\PasswordRecover\TokenManager:
|
||||
arguments:
|
||||
$secret: '%kernel.secret%'
|
||||
$logger: '@Psr\Log\LoggerInterface'
|
||||
|
||||
Chill\MainBundle\Security\PasswordRecover\RecoverPasswordHelper:
|
||||
arguments:
|
||||
$tokenManager: '@Chill\MainBundle\Security\PasswordRecover\TokenManager'
|
||||
$urlGenerator: '@Symfony\Component\Routing\Generator\UrlGeneratorInterface'
|
||||
$mailer: '@Chill\MainBundle\Notification\Mailer'
|
||||
$routeParameters: "%chill_main.notifications%"
|
||||
|
||||
@@ -80,11 +65,9 @@ services:
|
||||
Chill\MainBundle\Security\PasswordRecover\PasswordRecoverLocker:
|
||||
arguments:
|
||||
$chillRedis: '@Chill\MainBundle\Redis\ChillRedis'
|
||||
$logger: '@Psr\Log\LoggerInterface'
|
||||
|
||||
Chill\MainBundle\Security\PasswordRecover\PasswordRecoverVoter:
|
||||
arguments:
|
||||
$locker: '@Chill\MainBundle\Security\PasswordRecover\PasswordRecoverLocker'
|
||||
$requestStack: '@Symfony\Component\HttpFoundation\RequestStack'
|
||||
tags:
|
||||
- { name: security.voter }
|
||||
|
@@ -1,11 +1,13 @@
|
||||
---
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
# note: the autowiring for serializers and normalizers is declared
|
||||
# into ../services.yaml
|
||||
|
||||
Chill\MainBundle\Serializer\Normalizer\DoctrineExistingEntityNormalizer:
|
||||
autowire: true
|
||||
tags:
|
||||
- { name: 'serializer.normalizer', priority: 8 }
|
||||
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
# twig_intl:
|
||||
# class: Twig_Extensions_Extension_Intl
|
||||
# tags:
|
||||
@@ -32,8 +36,6 @@ services:
|
||||
- { name: twig.extension }
|
||||
|
||||
Chill\MainBundle\Templating\Entity\CommentRender:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
tags:
|
||||
- { name: 'chill.render_entity' }
|
||||
|
||||
@@ -41,17 +43,11 @@ services:
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
|
||||
Chill\MainBundle\Templating\Entity\AddressRender:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
Chill\MainBundle\Templating\Entity\AddressRender: ~
|
||||
|
||||
Chill\MainBundle\Templating\Entity\UserRender:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
Chill\MainBundle\Templating\Entity\UserRender: ~
|
||||
|
||||
Chill\MainBundle\Templating\Listing\:
|
||||
resource: './../../Templating/Listing'
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
|
||||
Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface: '@Chill\MainBundle\Templating\Listing\FilterOrderHelperFactory'
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill_main.timeline_builder:
|
||||
class: Chill\MainBundle\Timeline\TimelineBuilder
|
||||
arguments:
|
||||
|
@@ -1,11 +1,15 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
chill_main.validator_user_circle_consistency:
|
||||
class: Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistencyValidator
|
||||
arguments:
|
||||
- "@chill.main.security.authorization.helper"
|
||||
tags:
|
||||
- { name: "validator.constraint_validator" }
|
||||
|
||||
|
||||
Chill\MainBundle\Validation\Validator\UserUniqueEmailAndUsername:
|
||||
arguments:
|
||||
$em: '@Doctrine\ORM\EntityManagerInterface'
|
||||
|
@@ -1,2 +1,6 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Templating\UI\CountNotificationUser: ~
|
||||
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20211119173554 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'remove comment on deprecated json_array type';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$columns = [
|
||||
'users.attributes'
|
||||
];
|
||||
|
||||
foreach ($columns as $col) {
|
||||
$this->addSql("COMMENT ON COLUMN $col IS NULL");
|
||||
}
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->throwIrreversibleMigrationException();
|
||||
}
|
||||
}
|
@@ -86,6 +86,10 @@ address more:
|
||||
Create a new address: Créer une nouvelle adresse
|
||||
Create an address: Créer une adresse
|
||||
Update address: Modifier l'adresse
|
||||
City or postal code: Ville ou code postal
|
||||
|
||||
# contact
|
||||
Part of the phonenumber: Partie du numéro de téléphone
|
||||
|
||||
#serach
|
||||
Your search is empty. Please provide search terms.: La recherche est vide. Merci de fournir des termes de recherche.
|
||||
@@ -196,7 +200,7 @@ Location: Localisation
|
||||
Location type list: Liste des types de localisation
|
||||
Create a new location type: Créer un nouveau type de localisation
|
||||
Available for users: Disponible aux utilisateurs
|
||||
Address required: Adresse requise?
|
||||
Address required: Adresse requise?
|
||||
Contact data: Données de contact?
|
||||
optional: optionnel
|
||||
required: requis
|
||||
|
Reference in New Issue
Block a user