refactor: Update source code based on PHP conventions.

This commit is contained in:
Pol Dellaiera
2021-04-26 15:45:05 +02:00
parent 6b2eda0f94
commit 054a28ecf4
766 changed files with 52425 additions and 53000 deletions

View File

@@ -1,73 +1,65 @@
<?php
/*
* Copyright (C) 2016-2019 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Actions;
use Symfony\Component\EventDispatcher\Event;
/**
* Event triggered when an entity attached to a person is removed.
*
*
*/
class ActionEvent extends Event
{
const DELETE = 'CHILL_PERSON.DELETE_ASSOCIATED_ENTITY';
const MOVE = 'CHILL_PERSON.MOVE_ASSOCIATED_ENTITY';
public const DELETE = 'CHILL_PERSON.DELETE_ASSOCIATED_ENTITY';
public const MOVE = 'CHILL_PERSON.MOVE_ASSOCIATED_ENTITY';
/**
*
* @var int
*/
protected $personId;
/**
* the FQDN class name as recorded in doctrine
* the FQDN class name as recorded in doctrine.
*
* @var string
*/
protected $entity;
/**
* an array of key value data to describe the movement
* an array of key value data to describe the movement.
*
* @var array
*/
protected $metadata;
/**
* the sql statement
*
* @var string
* @var int
*/
protected $sqlStatement;
protected $personId;
/**
*
* @var string[]
*/
protected $preSql = [];
/**
*
* @var string[]
*/
protected $postSql = [];
/**
* @var string[]
*/
protected $preSql = [];
/**
* the sql statement.
*
* @var string
*/
protected $sqlStatement;
public function __construct($personId, $entity, $sqlStatement, $metadata = [])
{
$this->personId = $personId;
@@ -77,21 +69,17 @@ class ActionEvent extends Event
}
/**
*
* @return string[]
* Add Sql which will be executed **after** the delete statement.
*
* @param type $postSql
*
* @return $this
*/
public function getPreSql(): array
public function addPostSql(string $postSql)
{
return $this->preSql;
}
$this->postSql[] = $postSql;
/**
*
* @return string[]
*/
public function getPostSql(): array
{
return $this->postSql;
return $this;
}
/*
@@ -100,19 +88,21 @@ class ActionEvent extends Event
public function addPreSql(string $preSql)
{
$this->preSql[] = $preSql;
return $this;
}
/**
* Add Sql which will be executed **after** the delete statement
*
* @param type $postSql
* @return $this
* get the entity name, as recorded in doctrine.
*/
public function addPostSql(string $postSql)
public function getEntity(): string
{
$this->postSql[] = $postSql;
return $this;
return $this->entity;
}
public function getMetadata()
{
return $this->metadata;
}
public function getPersonId(): int
@@ -121,23 +111,23 @@ class ActionEvent extends Event
}
/**
* get the entity name, as recorded in doctrine
*
* @return string
* @return string[]
*/
public function getEntity(): string
public function getPostSql(): array
{
return $this->entity;
return $this->postSql;
}
/**
* @return string[]
*/
public function getPreSql(): array
{
return $this->preSql;
}
public function getSqlStatement()
{
return $this->sqlStatement;
}
public function getMetadata()
{
return $this->metadata;
}
}

View File

@@ -1,54 +1,48 @@
<?php
/*
* Copyright (C) 2016-2019 Champs-Libres <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/>.
*/
namespace Chill\PersonBundle\Actions\Remove;
use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\Mapping\ClassMetadata;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Chill\PersonBundle\Actions\ActionEvent;
/**
* Move or delete entities associated to a person to a new one, and delete the
* old person. The data associated to a person (birthdate, name, ...) are left
* untouched on the "new one".
*
* See `getSql` for details.
*
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Actions\Remove;
use Chill\PersonBundle\Actions\ActionEvent;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use function in_array;
/**
* Move or delete entities associated to a person to a new one, and delete the
* old person. The data associated to a person (birthdate, name, ...) are left
* untouched on the "new one".
*
* See `getSql` for details.
*/
class PersonMove
{
/**
*
* @var EntityManagerInterface
*/
protected $em;
/**
*
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
public function __construct(
EntityManagerInterface $em,
EntityManagerInterface $em,
EventDispatcherInterface $eventDispatcher
) {
$this->em = $em;
@@ -56,128 +50,136 @@ class PersonMove
}
/**
* Return the sql used to move or delete entities associated to a person to
* a new one, and delete the old person. The data associated to a person
* Return the sql used to move or delete entities associated to a person to
* a new one, and delete the old person. The data associated to a person
* (birthdate, name, ...) are left untouched on the "new one".
*
* The accompanying periods associated to a person are always removed. The other
*
* The accompanying periods associated to a person are always removed. The other
* associated entity are updated: the new person id is associated to the entity.
*
*
* Optionnaly, you can ask for removing entity by passing them in $deleteEntities
* parameters.
*
* parameters.
*
* The following events are triggered:
* - `'CHILL_PERSON.DELETE_ASSOCIATED_ENTITY'` is triggered when an entity
* will be removed ;
* - `'CHILL_PERSON.MOVE_ASSOCIATED_ENTITY'` is triggered when an entity
* will be moved ;
*
*
* Those events have the following metadata:
*
*
* - 'original_action' : always 'move' ;
* - 'to': the person id to move ;
*
* @param Person $from
* @param Person $to
* @param array $deleteEntities
*
* @return type
*/
public function getSQL(Person $from, Person $to, array $deleteEntities = [])
{
$sqls = [];
$toDelete = \array_merge($deleteEntities, $this->getDeleteEntities());
$toDelete = array_merge($deleteEntities, $this->getDeleteEntities());
foreach ($this->em->getMetadataFactory()->getAllMetadata() as $metadata) {
if ($metadata->isMappedSuperclass) {
continue;
}
foreach ($metadata->getAssociationMappings() as $field => $mapping) {
if ($mapping['targetEntity'] === Person::class) {
if (\in_array($metadata->getName(), $toDelete)) {
if (Person::class === $mapping['targetEntity']) {
if (in_array($metadata->getName(), $toDelete, true)) {
$sql = $this->createDeleteSQL($metadata, $from, $field);
$event = new ActionEvent($from->getId(), $metadata->getName(), $sql,
['to' => $to->getId(), 'original_action' => 'move']);
$event = new ActionEvent(
$from->getId(),
$metadata->getName(),
$sql,
['to' => $to->getId(), 'original_action' => 'move']
);
$this->eventDispatcher->dispatch(ActionEvent::DELETE, $event);
} else {
$sql = $this->createMoveSQL($metadata, $from, $to, $field);
$event = new ActionEvent($from->getId(), $metadata->getName(), $sql,
['to' => $to->getId(), 'original_action' => 'move']);
$event = new ActionEvent(
$from->getId(),
$metadata->getName(),
$sql,
['to' => $to->getId(), 'original_action' => 'move']
);
$this->eventDispatcher->dispatch(ActionEvent::MOVE, $event);
}
$sqls = \array_merge($sqls, $event->getPreSql(), [$event->getSqlStatement()], $event->getPostSql());
$sqls = array_merge($sqls, $event->getPreSql(), [$event->getSqlStatement()], $event->getPostSql());
}
}
}
$personMetadata = $this->em->getClassMetadata(Person::class);
$sqls[] = sprintf("DELETE FROM %s WHERE id = %d",
$sqls[] = sprintf(
'DELETE FROM %s WHERE id = %d',
$this->getTableName($personMetadata),
$from->getId());
$from->getId()
);
return $sqls ?? [];
}
protected function createMoveSQL(ClassMetadata $metadata, Person $from, Person $to, $field): string
{
$mapping = $metadata->getAssociationMapping($field);
// Set part of the query, aka <here> in "UPDATE table SET <here> "
$sets = [];
foreach ($mapping["joinColumns"] as $columns) {
$sets[] = sprintf("%s = %d", $columns["name"], $to->getId());
}
$conditions = [];
foreach ($mapping["joinColumns"] as $columns) {
$conditions[] = sprintf("%s = %d", $columns["name"], $from->getId());
}
return \sprintf("UPDATE %s SET %s WHERE %s",
$this->getTableName($metadata),
\implode(" ", $sets),
\implode(" AND ", $conditions)
);
}
protected function createDeleteSQL(ClassMetadata $metadata, Person $from, $field): string
{
$mapping = $metadata->getAssociationMapping($field);
$conditions = [];
foreach ($mapping["joinColumns"] as $columns) {
$conditions[] = sprintf("%s = %d", $columns["name"], $from->getId());
foreach ($mapping['joinColumns'] as $columns) {
$conditions[] = sprintf('%s = %d', $columns['name'], $from->getId());
}
return \sprintf("DELETE FROM %s WHERE %s",
return sprintf(
'DELETE FROM %s WHERE %s',
$this->getTableName($metadata),
\implode(" AND ", $conditions)
);
implode(' AND ', $conditions)
);
}
protected function createMoveSQL(ClassMetadata $metadata, Person $from, Person $to, $field): string
{
$mapping = $metadata->getAssociationMapping($field);
// Set part of the query, aka <here> in "UPDATE table SET <here> "
$sets = [];
foreach ($mapping['joinColumns'] as $columns) {
$sets[] = sprintf('%s = %d', $columns['name'], $to->getId());
}
$conditions = [];
foreach ($mapping['joinColumns'] as $columns) {
$conditions[] = sprintf('%s = %d', $columns['name'], $from->getId());
}
return sprintf(
'UPDATE %s SET %s WHERE %s',
$this->getTableName($metadata),
implode(' ', $sets),
implode(' AND ', $conditions)
);
}
/**
* return an array of classes where entities should be deleted
* instead of moved
*
* @return array
* return an array of classes where entities should be deleted
* instead of moved.
*/
protected function getDeleteEntities(): array
{
return [
AccompanyingPeriod::class
AccompanyingPeriod::class,
];
}
/**
* get the full table name with schema if it does exists
* get the full table name with schema if it does exists.
*/
private function getTableName(ClassMetadata $metadata): string
{
return empty($metadata->getSchemaName()) ?
$metadata->getTableName() :
$metadata->getSchemaName().".".$metadata->getTableName();
return empty($metadata->getSchemaName()) ?
$metadata->getTableName() :
$metadata->getSchemaName() . '.' . $metadata->getTableName();
}
}

View File

@@ -1,113 +1,128 @@
<?php
/*
* Copyright (C) 2016-2019 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\CRUD\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Symfony\Component\HttpFoundation\Request;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\QueryBuilder;
use Exception;
use Symfony\Component\HttpFoundation\Request;
/**
* Class EntityPersonCRUDController
* CRUD Controller for entities attached to a Person
*
* @package Chill\PersonBundle\CRUD\Controller
* CRUD Controller for entities attached to a Person.
*/
class EntityPersonCRUDController extends CRUDController
{
/**
* Extract the person from the request
*
* the person parameter will be `person_id` and must be
* Override the base method to add a filtering step to a person.
*
* @return QueryBuilder
*/
protected function buildQueryEntities(string $action, Request $request)
{
$qb = parent::buildQueryEntities($action, $request);
return $this->filterQueryEntitiesByPerson($action, $qb, $request);
}
/**
* @param \Chill\MainBundle\CRUD\Controller\string|string $action
*/
protected function createEntity($action, Request $request): object
{
$entity = parent::createEntity($action, $request);
$person = $this->getPerson($request);
$entity->setPerson($person);
return $entity;
}
/**
* Add a where clause to the buildQuery.
*
* @param \Chill\PersonBundle\CRUD\Controller\QueryBuilder $qb
*
* @return \Chill\PersonBundle\CRUD\Controller\QueryBuilder
*/
protected function filterQueryEntitiesByPerson(string $action, QueryBuilder $qb, Request $request): QueryBuilder
{
$qb->andWhere($qb->expr()->eq('e.person', ':person'));
$qb->setParameter('person', $this->getPerson($request));
return $qb;
}
/**
* @param mixed $entity
*
* @throws Exception
*/
protected function generateTemplateParameter(string $action, $entity, Request $request, array $defaultTemplateParameters = []): array
{
$person = $this->getPerson($request);
if (null === $person) {
throw new Exception('the `person_id` parameter is not set in the query. '
. 'You should set it or override the current method to allow another '
. 'behaviour: ' . __METHOD__);
}
return parent::generateTemplateParameter(
$action,
$entity,
$request,
array_merge(['person' => $person], $defaultTemplateParameters)
);
}
/**
* Extract the person from the request.
*
* the person parameter will be `person_id` and must be
* present in the query
*
*
* If the parameter is not set, this method will return null.
*
* If the person id does not exists, the method will throw a
*
* If the person id does not exists, the method will throw a
* Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*
* @param Request $request
*
* @throws Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the person with given id is not found
*/
protected function getPerson(Request $request): ?Person
{
if (FALSE === $request->query->has('person_id')) {
if (false === $request->query->has('person_id')) {
return null;
}
$person = $this->getDoctrine()
->getRepository(Person::class)
->find($request->query->getInt('person_id'))
;
if (NULL === $person) {
->find($request->query->getInt('person_id'));
if (null === $person) {
throw $this->createNotFoundException('the person with this id is not found');
}
return $person;
}
/**
* @param \Chill\MainBundle\CRUD\Controller\string|string $action
* @param Request $request
* @return object
*/
protected function createEntity($action, Request $request): object
{
$entity = parent::createEntity($action, $request);
$person = $this->getPerson($request);
$entity->setPerson($person);
return $entity;
}
/**
* @param string $action
* @param mixed $entity
* @param Request $request
* @param array $defaultTemplateParameters
* @return array
* @throws \Exception
*/
protected function generateTemplateParameter(string $action, $entity, Request $request, array $defaultTemplateParameters = array()): array
{
$person = $this->getPerson($request);
if (NULL === $person) {
throw new \Exception("the `person_id` parameter is not set in the query. "
. "You should set it or override the current method to allow another "
. "behaviour: ".__METHOD__);
}
return parent::generateTemplateParameter(
$action,
$entity,
$request,
\array_merge([ 'person' => $person ], $defaultTemplateParameters)
);
}
/**
* @param string $action
* @param mixed $entity
* @param Request $request
*
* @return string
*/
protected function getTemplateFor($action, $entity, Request $request)
@@ -115,11 +130,11 @@ class EntityPersonCRUDController extends CRUDController
if ($this->hasCustomTemplate($action, $entity, $request)) {
return $this->getActionConfig($action)['template'];
}
switch ($action) {
case 'new':
return '@ChillPerson/CRUD/new.html.twig';
case 'edit':
case 'edit':
return '@ChillPerson/CRUD/edit.html.twig';
case 'view':
return '@ChillPerson/CRUD/view.html.twig';
@@ -127,71 +142,41 @@ class EntityPersonCRUDController extends CRUDController
return '@ChillPerson/CRUD/delete.html.twig';
case 'index':
return '@ChillPerson/CRUD/index.html.twig';
default:
return parent::getTemplateFor($action, $entity, $request);
}
}
/**
* @param string $action
* @param mixed $entity
* @param \Symfony\Component\Form\FormInterface $form
* @param Request $request
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
protected function onBeforeRedirectAfterSubmission(string $action, $entity, \Symfony\Component\Form\FormInterface $form, Request $request)
{
$next = $request->request->get("submit", "save-and-close");
$next = $request->request->get('submit', 'save-and-close');
switch ($next) {
case "save-and-close":
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_index', [
'person_id' => $this->getPerson($request)->getId()
case 'save-and-close':
return $this->redirectToRoute('chill_crud_' . $this->getCrudName() . '_index', [
'person_id' => $this->getPerson($request)->getId(),
]);
case "save-and-new":
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_new', [
'person_id' => $this->getPerson($request)->getId()
case 'save-and-new':
return $this->redirectToRoute('chill_crud_' . $this->getCrudName() . '_new', [
'person_id' => $this->getPerson($request)->getId(),
]);
case "new":
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', [
case 'new':
return $this->redirectToRoute('chill_crud_' . $this->getCrudName() . '_view', [
'id' => $entity->getId(),
'person_id' => $this->getPerson($request)->getId()
'person_id' => $this->getPerson($request)->getId(),
]);
default:
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', [
return $this->redirectToRoute('chill_crud_' . $this->getCrudName() . '_view', [
'id' => $entity->getId(),
'person_id' => $this->getPerson($request)->getId()
]);
'person_id' => $this->getPerson($request)->getId(),
]);
}
}
/**
* Override the base method to add a filtering step to a person.
*
* @param string $action
* @param Request $request
* @return QueryBuilder
*/
protected function buildQueryEntities(string $action, Request $request)
{
$qb = parent::buildQueryEntities($action, $request);
return $this->filterQueryEntitiesByPerson($action, $qb, $request);
}
/**
* Add a where clause to the buildQuery
*
* @param string $action
* @param \Chill\PersonBundle\CRUD\Controller\QueryBuilder $qb
* @param Request $request
* @return \Chill\PersonBundle\CRUD\Controller\QueryBuilder
*/
protected function filterQueryEntitiesByPerson(string $action, QueryBuilder $qb, Request $request): QueryBuilder
{
$qb->andWhere($qb->expr()->eq('e.person', ':person'));
$qb->setParameter('person', $this->getPerson($request));
return $qb;
}
}

View File

@@ -1,89 +1,85 @@
<?php
/*
* Copyright (C) 2016-2019 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\CRUD\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Symfony\Component\HttpFoundation\Request;
use Chill\PersonBundle\Entity\Person;
use LogicException;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Controller for entities attached as one-to-on to a person
*
* Controller for entities attached as one-to-on to a person.
*/
class OneToOneEntityPersonCRUDController extends CRUDController
{
protected function getTemplateFor($action, $entity, Request $request)
protected function generateRedirectOnCreateRoute($action, Request $request, $entity)
{
if (!empty($this->crudConfig[$action]['template'])) {
return $this->crudConfig[$action]['template'];
}
switch ($action) {
case 'new':
return '@ChillPerson/CRUD/new.html.twig';
case 'edit':
return '@ChillPerson/CRUD/edit.html.twig';
case 'index':
return '@ChillPerson/CRUD/index.html.twig';
default:
throw new \LogicException("the view for action $action is not "
. "defined. You should override ".__METHOD__." to add this "
. "action");
}
throw new BadMethodCallException('not implemtented yet');
}
protected function getEntity($action, $id, Request $request): ?object
{
$entity = parent::getEntity($action, $id, $request);
if (NULL === $entity) {
if (null === $entity) {
$entity = $this->createEntity($action, $request);
$person = $this->getDoctrine()
->getManager()
->getRepository(Person::class)
->find($id);
$entity->setPerson($person);
}
return $entity;
}
protected function getTemplateFor($action, $entity, Request $request)
{
if (!empty($this->crudConfig[$action]['template'])) {
return $this->crudConfig[$action]['template'];
}
switch ($action) {
case 'new':
return '@ChillPerson/CRUD/new.html.twig';
case 'edit':
return '@ChillPerson/CRUD/edit.html.twig';
case 'index':
return '@ChillPerson/CRUD/index.html.twig';
default:
throw new LogicException("the view for action {$action} is not "
. 'defined. You should override ' . __METHOD__ . ' to add this '
. 'action');
}
}
protected function onPostFetchEntity($action, Request $request, $entity): ?Response
{
if (false === $this->getDoctrine()->getManager()->contains($entity)) {
return new RedirectResponse($this->generateRedirectOnCreateRoute($action, $request, $entity));
}
return null;
}
protected function onPreFlush(string $action, $entity, FormInterface $form, Request $request)
{
$this->getDoctrine()->getManager()->persist($entity);
}
protected function onPostFetchEntity($action, Request $request, $entity): ?Response
{
if (FALSE === $this->getDoctrine()->getManager()->contains($entity)) {
return new RedirectResponse($this->generateRedirectOnCreateRoute($action, $request, $entity));
}
return null;
}
protected function generateRedirectOnCreateRoute($action, Request $request, $entity)
{
throw new BadMethodCallException("not implemtented yet");
}
}

View File

@@ -1,21 +1,32 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Chill\PersonBundle\Widget\PersonListWidgetFactory;
use Chill\PersonBundle\DependencyInjection\CompilerPass\AccompanyingPeriodTimelineCompilerPass;
use Chill\PersonBundle\Widget\PersonListWidgetFactory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ChillPersonBundle extends Bundle
{
public function build(ContainerBuilder $container)
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->getExtension('chill_main')
->addWidgetFactory(new PersonListWidgetFactory());
$container->addCompilerPass(new AccompanyingPeriodTimelineCompilerPass());
}
}

View File

@@ -1,150 +1,144 @@
<?php
/*
* Copyright (C) 2016-2019 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Command;
use Chill\PersonBundle\Actions\Remove\PersonMove;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Chill\PersonBundle\Actions\Remove\PersonMove;
use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Console\Exception\RuntimeException;
use Psr\Log\LoggerInterface;
class ChillPersonMoveCommand extends ContainerAwareCommand
{
/**
*
* @var PersonMove
*/
protected $mover;
/**
*
* @var EntityManagerInterface
*/
protected $em;
/**
*
* @var LoggerInterface
*/
protected $chillLogger;
/**
* @var EntityManagerInterface
*/
protected $em;
/**
* @var PersonMove
*/
protected $mover;
public function __construct(
PersonMove $mover,
PersonMove $mover,
EntityManagerInterface $em,
LoggerInterface $chillLogger
) {
parent::__construct('chill:person:move');
$this->mover = $mover;
$this->em = $em;
$this->chillLogger = $chillLogger;
}
protected function buildLoggingContext(Person $from, Person $to, $deleteEntities, $sqls)
{
$ctxt = [
'from' => $from->getId(),
'to' => $to->getId(),
];
foreach ($deleteEntities as $key => $de) {
$ctxt['delete_entity_' . $key] = $de;
}
foreach ($sqls as $key => $sql) {
$ctxt['sql_' . $key] = $sql;
}
return $ctxt;
}
protected function configure()
{
$this
->setName('chill:person:move')
->setDescription('Move all the associated entities on a "from" person to a "to" person and remove the old person')
->addOption('from', 'f', InputOption::VALUE_REQUIRED, "The person id to delete, all associated data will be moved before")
->addOption('to', 't', InputOption::VALUE_REQUIRED, "The person id which will received data")
->addOption('dump-sql', null, InputOption::VALUE_NONE, "dump sql to stdout")
->addOption('force', null, InputOption::VALUE_NONE, "execute sql instead of dumping it")
->addOption('delete-entity', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, "entity to delete", [])
;
}
protected function interact(InputInterface $input, OutputInterface $output)
{
if (FALSE === $input->hasOption('dump-sql') && FALSE === $input->hasOption('force')) {
$msg = "You must use \"--dump-sql\" or \"--force\"";
throw new RuntimeException($msg);
}
foreach (["from", "to"] as $name) {
if (empty($input->getOption($name))) {
throw new RuntimeException("You must set a \"$name\" option");
}
$id = $input->getOption($name);
if (\ctype_digit($id) === FALSE) {
throw new RuntimeException("The id in \"$name\" field does not contains "
. "only digits: $id");
}
}
->addOption('from', 'f', InputOption::VALUE_REQUIRED, 'The person id to delete, all associated data will be moved before')
->addOption('to', 't', InputOption::VALUE_REQUIRED, 'The person id which will received data')
->addOption('dump-sql', null, InputOption::VALUE_NONE, 'dump sql to stdout')
->addOption('force', null, InputOption::VALUE_NONE, 'execute sql instead of dumping it')
->addOption('delete-entity', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'entity to delete', []);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$repository = $this->em->getRepository(Person::class);
$from = $repository->find($input->getOption('from'));
$to = $repository->find($input->getOption('to'));
$to = $repository->find($input->getOption('to'));
$deleteEntities = $input->getOption('delete-entity');
if ($from === NULL) {
throw new RuntimeException(sprintf("Person \"from\" with id %d not found", $input->getOption('from')));
if (null === $from) {
throw new RuntimeException(sprintf('Person "from" with id %d not found', $input->getOption('from')));
}
if ($to === NULL) {
throw new RuntimeException(sprintf("Person \"to\" with id %d not found", $input->getOption('to')));
if (null === $to) {
throw new RuntimeException(sprintf('Person "to" with id %d not found', $input->getOption('to')));
}
$sqls = $this->mover->getSQL($from, $to, $deleteEntities);
if ($input->getOption('dump-sql')) {
foreach($sqls as $sql) {
foreach ($sqls as $sql) {
$output->writeln($sql);
}
} else {
$ctxt = $this->buildLoggingContext($from, $to, $deleteEntities, $sqls);
$this->chillLogger->notice("Trying to move a person from command line", $ctxt);
$this->chillLogger->notice('Trying to move a person from command line', $ctxt);
$connection = $this->em->getConnection();
$connection->beginTransaction();
foreach($sqls as $sql) {
foreach ($sqls as $sql) {
if ($output->isVerbose()) {
$output->writeln($sql);
}
$connection->executeQuery($sql);
}
$connection->commit();
$this->chillLogger->notice("Move a person from command line succeeded", $ctxt);
$this->chillLogger->notice('Move a person from command line succeeded', $ctxt);
}
}
protected function buildLoggingContext(Person $from, Person $to, $deleteEntities, $sqls)
{
$ctxt = [
'from' => $from->getId(),
'to' => $to->getId()
];
foreach ($deleteEntities as $key => $de) {
$ctxt['delete_entity_'.$key] = $de;
}
foreach ($sqls as $key => $sql) {
$ctxt['sql_'.$key] = $sql;
}
return $ctxt;
}
protected function interact(InputInterface $input, OutputInterface $output)
{
if (false === $input->hasOption('dump-sql') && false === $input->hasOption('force')) {
$msg = 'You must use "--dump-sql" or "--force"';
throw new RuntimeException($msg);
}
foreach (['from', 'to'] as $name) {
if (empty($input->getOption($name))) {
throw new RuntimeException("You must set a \"{$name}\" option");
}
$id = $input->getOption($name);
if (ctype_digit($id) === false) {
throw new RuntimeException("The id in \"{$name}\" field does not contains "
. "only digits: {$id}");
}
}
}
}

View File

@@ -1,61 +1,60 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Config;
use function count;
/**
* Give help to interact with the config for alt names
*
*
* Give help to interact with the config for alt names.
*/
class ConfigPersonAltNamesHelper
{
/**
* the raw config, directly from the container parameter
* the raw config, directly from the container parameter.
*
* @var array
*/
private $config = [];
public function __construct($config)
{
$this->config = $config;
}
/**
* Return true if at least one alt name is configured
*
* @return bool
*/
public function hasAltNames(): bool
{
return count($this->config) > 0;
}
/**
* get the choices as key => values
*
* @return array
* get the choices as key => values.
*/
public function getChoices(): array
{
$choices = [];
foreach ($this->config as $entry) {
$labels = $entry['labels'];
$lang = false;
$label = false;
$cur = reset($labels);
while ($cur) {
if (key($labels) === 'lang') {
$lang = current($labels);
}
if (key($labels) === 'label') {
$label = current($labels);
}
if ($lang !== FALSE && $label !== FALSE) {
if (false !== $lang && false !== $label) {
$choices[$entry['key']][$lang] = $label;
$lang = false;
$label = false;
@@ -63,8 +62,15 @@ class ConfigPersonAltNamesHelper
$cur = next($labels);
}
}
return $choices;
}
/**
* Return true if at least one alt name is configured.
*/
public function hasAltNames(): bool
{
return count($this->config) > 0;
}
}

View File

@@ -1,5 +1,16 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
@@ -7,24 +18,28 @@ use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Class AccompanyingCourseController
*
* @package Chill\PersonBundle\Controller
*/
class AccompanyingCourseController extends Controller
{
/**
* Homepage of Accompanying Course section
* History page of Accompanying Course section.
*
* the page show anti chronologic history with all actions, title of page is 'accompanying course details'
*
* @Route("/{_locale}/parcours/{accompanying_period_id}/history", name="chill_person_accompanying_course_history")
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
*/
public function historyAction(AccompanyingPeriod $accompanyingCourse): Response
{
return $this->render('@ChillPerson/AccompanyingCourse/history.html.twig', [
'accompanyingCourse' => $accompanyingCourse,
]);
}
/**
* Homepage of Accompanying Course section.
*
* @Route("/{_locale}/parcours/{accompanying_period_id}", name="chill_person_accompanying_course_index")
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
@@ -32,12 +47,12 @@ class AccompanyingCourseController extends Controller
public function indexAction(AccompanyingPeriod $accompanyingCourse): Response
{
return $this->render('@ChillPerson/AccompanyingCourse/index.html.twig', [
'accompanyingCourse' => $accompanyingCourse
'accompanyingCourse' => $accompanyingCourse,
]);
}
/**
* Show page of Accompanying Course section
* Show page of Accompanying Course section.
*
* the page show all blocks except one active edit block, managed by vuejs component
* that's why title of page is 'edit accompanying course'
@@ -48,37 +63,23 @@ class AccompanyingCourseController extends Controller
public function showAction(AccompanyingPeriod $accompanyingCourse): Response
{
return $this->render('@ChillPerson/AccompanyingCourse/show.html.twig', [
'accompanyingCourse' => $accompanyingCourse
'accompanyingCourse' => $accompanyingCourse,
]);
}
/**
* History page of Accompanying Course section
* Sérialise temporairement quelques données pour donner à manger au composant vuejs.
*
* the page show anti chronologic history with all actions, title of page is 'accompanying course details'
*
* @Route("/{_locale}/parcours/{accompanying_period_id}/history", name="chill_person_accompanying_course_history")
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
*/
public function historyAction(AccompanyingPeriod $accompanyingCourse): Response
{
return $this->render('@ChillPerson/AccompanyingCourse/history.html.twig', [
'accompanyingCourse' => $accompanyingCourse
]);
}
/**
* Sérialise temporairement quelques données pour donner à manger au composant vuejs
* @Route(
* "/{_locale}/api/parcours/{accompanying_period_id}/show",
* name="chill_person_accompanying_course_api_show")
* name="chill_person_accompanying_course_api_show")
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
* @param SerializerInterface $serializer
*/
public function showAPI(AccompanyingPeriod $accompanyingCourse): Response
{
$persons = [];
foreach ($accompanyingCourse->getParticipations() as $k => $participation ) {
foreach ($accompanyingCourse->getParticipations() as $k => $participation) {
/**
* @var AccompanyingPeriodParticipation $participation
* @var Person $person
@@ -91,7 +92,7 @@ class AccompanyingCourseController extends Controller
'email' => $person->getEmail(),
'phone' => $person->getPhonenumber(),
'startdate' => ($participation->getStartDate()) ? $participation->getStartDate()->format('Y-m-d') : null,
'enddate' => ($participation->getEndDate()) ? $participation->getEndDate()->format('Y-m-d') : null
'enddate' => ($participation->getEndDate()) ? $participation->getEndDate()->format('Y-m-d') : null,
];
}
$data = [
@@ -100,13 +101,13 @@ class AccompanyingCourseController extends Controller
'closing_motive' => $accompanyingCourse->getClosingMotive() ? $accompanyingCourse->getClosingMotive()->getName()['fr'] : null,
'opening_date' => ($accompanyingCourse->getOpeningDate()) ? $accompanyingCourse->getOpeningDate()->format('Y-m-d') : null,
'closing_date' => ($accompanyingCourse->getClosingDate()) ? $accompanyingCourse->getClosingDate()->format('Y-m-d') : null,
'persons' => $persons
'persons' => $persons,
];
$serialized = \json_encode($data);
$serialized = json_encode($data);
$response = new Response($serialized);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}

View File

@@ -1,276 +1,121 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Doctrine\DBAL\Exception;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Form\AccompanyingPeriodType;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\Common\Collections\Criteria;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use DateTime;
use Doctrine\Common\Collections\Criteria;
use Doctrine\DBAL\Exception;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Class AccompanyingPeriodController
*
* @package Chill\PersonBundle\Controller
*/
use function count;
class AccompanyingPeriodController extends AbstractController
{
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var ValidatorInterface
*/
protected $validator;
/**
* AccompanyingPeriodController constructor.
*
* @param EventDispatcherInterface $eventDispatcher
* @param ValidatorInterface $validator
*/
public function __construct(EventDispatcherInterface $eventDispatcher, ValidatorInterface $validator)
{
$this->eventDispatcher = $eventDispatcher;
$this->validator = $validator;
}
public function listAction(int $person_id): Response
{
$person = $this->_getPerson($person_id);
$event = new PrivacyEvent($person, [
'element_class' => AccompanyingPeriod::class,
'action' => 'list'
]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->render('ChillPersonBundle:AccompanyingPeriod:list.html.twig', [
'accompanying_periods' => $person->getAccompanyingPeriodsOrdered(),
'person' => $person
]);
}
public function createAction(int $person_id, Request $request): Response
{
$person = $this->_getPerson($person_id);
$this->denyAccessUnlessGranted(PersonVoter::UPDATE, $person,
'You are not allowed to update this person');
$accompanyingPeriod = new AccompanyingPeriod(new \DateTime('now'));
$accompanyingPeriod->setClosingDate(new \DateTime('now'));
$accompanyingPeriod->addPerson($person);
//or $person->addAccompanyingPeriod($accompanyingPeriod);
$form = $this->createForm(
AccompanyingPeriodType::class,
$accompanyingPeriod, [
'period_action' => 'create',
'center' => $person->getCenter()
]);
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
$errors = $this->_validatePerson($person);
$flashBag = $this->get('session')->getFlashBag();
if ($form->isValid(['Default', 'closed'])
&& count($errors) === 0) {
$em = $this->getDoctrine()->getManager();
$em->persist($accompanyingPeriod);
$em->flush();
$flashBag->add('success',
$this->get('translator')->trans(
'A period has been created.'));
return $this->redirect(
$this->generateUrl('chill_person_accompanying_period_list', [
'person_id' => $person->getId()
]));
} else {
$flashBag->add('error', $this->get('translator')
->trans('Error! Period not created!'));
foreach($errors as $error) {
$flashBag->add('info', $error->getMessage());
}
}
}
return $this->render('ChillPersonBundle:AccompanyingPeriod:form.html.twig', [
'form' => $form->createView(),
'person' => $person,
'accompanying_period' => $accompanyingPeriod
]);
}
/**
* @throws Exception
*/
public function updateAction(int $person_id, int $period_id, Request $request): Response
{
$em = $this->getDoctrine()->getManager();
/** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($period_id);
if ($accompanyingPeriod === null) {
throw $this->createNotFoundException("Period with id " . $period_id . " is not found");
}
/** @var Person $person */
$person = $this->_getPerson($person_id);
// CHECK
if (! $accompanyingPeriod->containsPerson($person)) {
throw new Exception("Accompanying period " . $period_id . " does not contain person " . $person_id);
}
$this->denyAccessUnlessGranted(PersonVoter::UPDATE, $person,
'You are not allowed to update this person');
$form = $this->createForm(AccompanyingPeriodType::class,
$accompanyingPeriod, [
'period_action' => 'update',
'center' => $person->getCenter()
]);
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
$errors = $this->_validatePerson($person);
$flashBag = $this->get('session')->getFlashBag();
if ($form->isValid(['Default', 'closed'])
&& count($errors) === 0) {
$em->flush();
$flashBag->add('success',
$this->get('translator')->trans('An accompanying period has been updated.'));
return $this->redirect(
$this->generateUrl('chill_person_accompanying_period_list', [
'person_id' => $person->getId()
]));
} else {
$flashBag->add('error', $this->get('translator')
->trans('Error when updating the period'));
foreach($errors as $error) {
$flashBag->add('info', $error->getMessage());
}
}
}
return $this->render('ChillPersonBundle:AccompanyingPeriod:form.html.twig', [
'form' => $form->createView(),
'person' => $person,
'accompanying_period' => $accompanyingPeriod
]);
}
/**
* @throws \Exception
*/
public function closeAction(int $person_id, Request $request): Response
{
$person = $this->_getPerson($person_id);
$this->denyAccessUnlessGranted(PersonVoter::UPDATE, $person, 'You are not allowed to update this person');
if ($person->isOpen() === false) {
$this->get('session')->getFlashBag()
->add('error', $this->get('translator')
->trans('Beware period is closed', ['%name%' => $person->__toString()]
));
->trans(
'Beware period is closed',
['%name%' => $person->__toString()]
));
return $this->redirect(
$this->generateUrl('chill_person_accompanying_period_list', [
'person_id' => $person->getId()
]));
'person_id' => $person->getId(),
])
);
}
$current = $person->getCurrentAccompanyingPeriod();
$form = $this->createForm(AccompanyingPeriodType::class, $current, [
'period_action' => 'close',
'center' => $person->getCenter()
'center' => $person->getCenter(),
]);
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
if ($form->isValid()){
if ($form->isValid()) {
$person->close($current);
$errors = $this->_validatePerson($person);
if (count($errors) === 0) {
$this->get('session')->getFlashBag()
->add('success', $this->get('translator')
->trans('An accompanying period has been closed.', [
'%name%' => $person->__toString()
]));
->trans('An accompanying period has been closed.', [
'%name%' => $person->__toString(),
]));
$this->getDoctrine()->getManager()->flush();
return $this->redirect(
$this->generateUrl('chill_person_accompanying_period_list', [
'person_id' => $person->getId()
'person_id' => $person->getId(),
])
);
} else {
$this->get('session')->getFlashBag()
->add('error', $this->get('translator')
->trans('Error! Period not closed!'));
foreach ($errors as $error) {
$this->get('session')->getFlashBag()
->add('info', $error->getMessage());
}
}
$this->get('session')->getFlashBag()
->add('error', $this->get('translator')
->trans('Error! Period not closed!'));
foreach ($errors as $error) {
$this->get('session')->getFlashBag()
->add('info', $error->getMessage());
}
} else { //if form is not valid
$this->get('session')->getFlashBag()
->add('error',
->add(
'error',
$this->get('translator')
->trans('Pediod closing form is not valid')
);
@@ -285,57 +130,127 @@ class AccompanyingPeriodController extends AbstractController
return $this->render('ChillPersonBundle:AccompanyingPeriod:form.html.twig', [
'form' => $form->createView(),
'person' => $person,
'accompanying_period' => $current
'accompanying_period' => $current,
]);
}
private function _validatePerson(Person $person): ConstraintViolationListInterface
{
$errors = $this->validator->validate($person, null,
['Default']);
// Can be disabled with config
if (false === $this->container->getParameter('chill_person.allow_multiple_simultaneous_accompanying_periods')) {
$errors_accompanying_period = $this->validator->validate($person, null,
['accompanying_period_consistent']);
foreach($errors_accompanying_period as $error ) {
$errors->add($error);
public function createAction(int $person_id, Request $request): Response
{
$person = $this->_getPerson($person_id);
$this->denyAccessUnlessGranted(
PersonVoter::UPDATE,
$person,
'You are not allowed to update this person'
);
$accompanyingPeriod = new AccompanyingPeriod(new DateTime('now'));
$accompanyingPeriod->setClosingDate(new DateTime('now'));
$accompanyingPeriod->addPerson($person);
//or $person->addAccompanyingPeriod($accompanyingPeriod);
$form = $this->createForm(
AccompanyingPeriodType::class,
$accompanyingPeriod,
[
'period_action' => 'create',
'center' => $person->getCenter(),
]
);
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
$errors = $this->_validatePerson($person);
$flashBag = $this->get('session')->getFlashBag();
if (
$form->isValid(['Default', 'closed'])
&& count($errors) === 0
) {
$em = $this->getDoctrine()->getManager();
$em->persist($accompanyingPeriod);
$em->flush();
$flashBag->add(
'success',
$this->get('translator')->trans(
'A period has been created.'
)
);
return $this->redirect(
$this->generateUrl('chill_person_accompanying_period_list', [
'person_id' => $person->getId(),
])
);
}
$flashBag->add('error', $this->get('translator')
->trans('Error! Period not created!'));
foreach ($errors as $error) {
$flashBag->add('info', $error->getMessage());
}
}
return $errors;
return $this->render('ChillPersonBundle:AccompanyingPeriod:form.html.twig', [
'form' => $form->createView(),
'person' => $person,
'accompanying_period' => $accompanyingPeriod,
]);
}
public function listAction(int $person_id): Response
{
$person = $this->_getPerson($person_id);
$event = new PrivacyEvent($person, [
'element_class' => AccompanyingPeriod::class,
'action' => 'list',
]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->render('ChillPersonBundle:AccompanyingPeriod:list.html.twig', [
'accompanying_periods' => $person->getAccompanyingPeriodsOrdered(),
'person' => $person,
]);
}
public function openAction(int $person_id, Request $request): Response
{
$person = $this->_getPerson($person_id);
$this->denyAccessUnlessGranted(PersonVoter::UPDATE, $person,
'You are not allowed to update this person');
$this->denyAccessUnlessGranted(
PersonVoter::UPDATE,
$person,
'You are not allowed to update this person'
);
//in case the person is already open
if ($person->isOpen()) {
$this->get('session')->getFlashBag()
->add('error', $this->get('translator')
->trans('Error! Period %name% is not closed ; it can be open',
['%name%' => $person->__toString()]
));
->trans(
'Error! Period %name% is not closed ; it can be open',
['%name%' => $person->__toString()]
));
return $this->redirect(
$this->generateUrl('chill_person_accompanying_period_list', [
'person_id' => $person->getId()
]));
'person_id' => $person->getId(),
])
);
}
$accompanyingPeriod = new AccompanyingPeriod(new \DateTime());
$accompanyingPeriod = new AccompanyingPeriod(new DateTime());
$form = $this->createForm(AccompanyingPeriodType::class,
$accompanyingPeriod, [
$form = $this->createForm(
AccompanyingPeriodType::class,
$accompanyingPeriod,
[
'period_action' => 'open',
'center' => $person->getCenter()
]);
'center' => $person->getCenter(),
]
);
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
@@ -348,43 +263,44 @@ class AccompanyingPeriodController extends AbstractController
if (count($errors) <= 0) {
$this->get('session')->getFlashBag()
->add('success', $this->get('translator')
->trans('An accompanying period has been opened.',
->trans(
'An accompanying period has been opened.',
['%name%' => $person->__toString()]
));
));
$this->getDoctrine()->getManager()->flush();
return $this->redirect(
$this->generateUrl('chill_person_accompanying_period_list', [
'person_id' => $person->getId()
]));
} else {
$this->get('session')->getFlashBag()
->add('error', $this->get('translator')
->trans('Period not opened'));
foreach ($errors as $error) {
$this->get('session')->getFlashBag()
->add('info', $error->getMessage());
}
'person_id' => $person->getId(),
])
);
}
$this->get('session')->getFlashBag()
->add('error', $this->get('translator')
->trans('Period not opened'));
foreach ($errors as $error) {
$this->get('session')->getFlashBag()
->add('info', $error->getMessage());
}
} else { // if errors in forms
$this->get('session')->getFlashBag()
->add('error', $this->get('translator')
->trans('Period not opened : form is invalid')
);
->add(
'error',
$this->get('translator')
->trans('Period not opened : form is invalid')
);
}
}
return $this->render('ChillPersonBundle:AccompanyingPeriod:form.html.twig', [
'form' => $form->createView(),
'person' => $person,
'accompanying_period' => $accompanyingPeriod
'accompanying_period' => $accompanyingPeriod,
]);
}
public function reOpenAction(int $person_id, int $period_id, Request $request): Response
{
/** @var Person $person */
@@ -393,18 +309,18 @@ class AccompanyingPeriodController extends AbstractController
$criteria = Criteria::create();
$criteria->where($criteria->expr()->eq('id', $period_id));
/* @var $period AccompanyingPeriod */
/** @var AccompanyingPeriod $period */
$period = $person->getAccompanyingPeriods()
->matching($criteria)
->first();
->matching($criteria)
->first();
if ($period === NULL) {
if (null === $period) {
throw $this->createNotFoundException('period not found');
}
$confirm = $request->query->getBoolean('confirm', false);
if ($confirm === true && $period->canBeReOpened($person)) {
if (true === $confirm && $period->canBeReOpened($person)) {
$period->reOpen();
$this->_validatePerson($person);
@@ -412,40 +328,143 @@ class AccompanyingPeriodController extends AbstractController
$this->getDoctrine()->getManager()->flush();
$this->addFlash('success', $this->get('translator')->trans(
'The period has been re-opened'));
'The period has been re-opened'
));
return $this->redirectToRoute('chill_person_accompanying_period_list', [
'person_id' => $person->getId()
'person_id' => $person->getId(),
]);
} elseif ($confirm === false && $period->canBeReOpened($person)) {
}
if (false === $confirm && $period->canBeReOpened($person)) {
return $this->render('ChillPersonBundle:AccompanyingPeriod:re_open.html.twig', [
'period' => $period,
'person' => $person
'person' => $person,
]);
} else {
return (new Response())
->setStatusCode(Response::HTTP_BAD_REQUEST)
->setContent("You cannot re-open this period");
}
return (new Response())
->setStatusCode(Response::HTTP_BAD_REQUEST)
->setContent('You cannot re-open this period');
}
/**
* @throws Exception
*/
public function updateAction(int $person_id, int $period_id, Request $request): Response
{
$em = $this->getDoctrine()->getManager();
/** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($period_id);
if (null === $accompanyingPeriod) {
throw $this->createNotFoundException('Period with id ' . $period_id . ' is not found');
}
/** @var Person $person */
$person = $this->_getPerson($person_id);
// CHECK
if (!$accompanyingPeriod->containsPerson($person)) {
throw new Exception('Accompanying period ' . $period_id . ' does not contain person ' . $person_id);
}
$this->denyAccessUnlessGranted(
PersonVoter::UPDATE,
$person,
'You are not allowed to update this person'
);
$form = $this->createForm(
AccompanyingPeriodType::class,
$accompanyingPeriod,
[
'period_action' => 'update',
'center' => $person->getCenter(),
]
);
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
$errors = $this->_validatePerson($person);
$flashBag = $this->get('session')->getFlashBag();
if (
$form->isValid(['Default', 'closed'])
&& count($errors) === 0
) {
$em->flush();
$flashBag->add(
'success',
$this->get('translator')->trans('An accompanying period has been updated.')
);
return $this->redirect(
$this->generateUrl('chill_person_accompanying_period_list', [
'person_id' => $person->getId(),
])
);
}
$flashBag->add('error', $this->get('translator')
->trans('Error when updating the period'));
foreach ($errors as $error) {
$flashBag->add('info', $error->getMessage());
}
}
return $this->render('ChillPersonBundle:AccompanyingPeriod:form.html.twig', [
'form' => $form->createView(),
'person' => $person,
'accompanying_period' => $accompanyingPeriod,
]);
}
/**
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
private function _getPerson(int $id) : Person
private function _getPerson(int $id): Person
{
$person = $this->getDoctrine()->getManager()
->getRepository('ChillPersonBundle:Person')->find($id);
if ($person === null) {
if (null === $person) {
throw $this->createNotFoundException('Person not found');
}
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person,
"You are not allowed to see this person");
$this->denyAccessUnlessGranted(
PersonVoter::SEE,
$person,
'You are not allowed to see this person'
);
return $person;
}
private function _validatePerson(Person $person): ConstraintViolationListInterface
{
$errors = $this->validator->validate(
$person,
null,
['Default']
);
// Can be disabled with config
if (false === $this->container->getParameter('chill_person.allow_multiple_simultaneous_accompanying_periods')) {
$errors_accompanying_period = $this->validator->validate(
$person,
null,
['accompanying_period_consistent']
);
foreach ($errors_accompanying_period as $error) {
$errors->add($error);
}
}
return $errors;
}
}

View File

@@ -1,50 +1,55 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Symfony\Component\HttpFoundation\Request;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Class AdminClosingMotiveController
* Controller for closing motives
*
* @package Chill\PersonBundle\Controller
* Controller for closing motives.
*/
class AdminClosingMotiveController extends CRUDController
{
/**
* @param \Chill\MainBundle\CRUD\Controller\string|string $action
* @param Request $request
* @return object
*/
protected function createEntity($action, Request $request): object
{
$entity = parent::createEntity($action, $request);
if ($request->query->has('parent_id')) {
$parentId = $request->query->getInt('parent_id');
$parent = $this->getDoctrine()->getManager()
->getRepository($this->getEntityClass())
->find($parentId);
if (NULL === $parent) {
if (null === $parent) {
throw $this->createNotFoundException('parent id not found');
}
$entity->setParent($parent);
}
return $entity;
}
/**
* @param string $action
* @param \Doctrine\ORM\QueryBuilder|mixed $query
* @param Request $request
* @param PaginatorInterface $paginator
*
* @return \Doctrine\ORM\QueryBuilder|mixed
*/
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)

View File

@@ -1,26 +1,32 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class AdminController
*
* @package Chill\PersonBundle\Controller
*/
class AdminController extends AbstractController
{
/**
* @param $_locale
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction($_locale)
{
return $this->render('ChillPersonBundle:Admin:layout.html.twig', []);
}
/**
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/

View File

@@ -1,15 +1,21 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController;
/**
* Class AdminMaritalStatusController
*
* @package Chill\PersonBundle\Controller
*/
class AdminMaritalStatusController extends CRUDController
{
// for minimal cases, nothing is required here !
}
}

View File

@@ -1,43 +1,32 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2016, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Form\Type\AddressType;
use Chill\PersonBundle\Entity\Person;
use Doctrine\Common\Collections\Criteria;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Form\Type\AddressType;
use Chill\MainBundle\Entity\Address;
use Doctrine\Common\Collections\Criteria;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use function count;
/**
* Class PersonAddressController
* Controller for addresses associated with person
*
* @package Chill\PersonBundle\Controller
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @author Champs Libres <info@champs-libres.coop>
* Controller for addresses associated with person.
*/
class PersonAddressController extends AbstractController
{
@@ -45,81 +34,30 @@ class PersonAddressController extends AbstractController
* @var ValidatorInterface
*/
protected $validator;
/**
* PersonAddressController constructor.
*
* @param ValidatorInterface $validator
*/
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
public function listAction($person_id)
{
$person = $this->getDoctrine()->getManager()
->getRepository('ChillPersonBundle:Person')
->find($person_id);
if ($person === null) {
throw $this->createNotFoundException("Person with id $person_id not"
. " found ");
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_SEE',
$person,
"You are not allowed to edit this person."
);
return $this->render('ChillPersonBundle:Address:list.html.twig', array(
'person' => $person
));
}
public function newAction($person_id)
{
$person = $this->getDoctrine()->getManager()
->getRepository('ChillPersonBundle:Person')
->find($person_id);
if ($person === null) {
throw $this->createNotFoundException("Person with id $person_id not"
. " found ");
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_UPDATE',
$person,
"You are not allowed to edit this person."
);
$address = new Address();
$form = $this->createCreateForm($person, $address);
return $this->render('ChillPersonBundle:Address:new.html.twig', array(
'person' => $person,
'form' => $form->createView()
));
}
public function createAction($person_id, Request $request)
{
$person = $this->getDoctrine()->getManager()
->getRepository('ChillPersonBundle:Person')
->find($person_id);
->getRepository('ChillPersonBundle:Person')
->find($person_id);
if ($person === null) {
throw $this->createNotFoundException("Person with id $person_id not"
. " found ");
if (null === $person) {
throw $this->createNotFoundException("Person with id {$person_id} not"
. ' found ');
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_UPDATE',
$person,
"You are not allowed to edit this person."
'You are not allowed to edit this person.'
);
$address = new Address();
@@ -137,7 +75,6 @@ class PersonAddressController extends AbstractController
$this->addFlash('error', $error->getMessage());
}
} elseif ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->flush();
@@ -146,47 +83,96 @@ class PersonAddressController extends AbstractController
$this->get('translator')->trans('The new address was created successfully')
);
return $this->redirectToRoute('chill_person_address_list', array(
'person_id' => $person->getId()
));
return $this->redirectToRoute('chill_person_address_list', [
'person_id' => $person->getId(),
]);
} else {
$this->addFlash('error', $this->get('translator')
->trans('Error! Address not created!'));
->trans('Error! Address not created!'));
}
}
return $this->render('ChillPersonBundle:Address:new.html.twig', array(
return $this->render('ChillPersonBundle:Address:new.html.twig', [
'person' => $person,
'form' => $form->createView()
));
'form' => $form->createView(),
]);
}
public function editAction($person_id, $address_id)
{
$person = $this->getDoctrine()->getManager()
->getRepository('ChillPersonBundle:Person')
->find($person_id);
->getRepository('ChillPersonBundle:Person')
->find($person_id);
if ($person === null) {
throw $this->createNotFoundException("Person with id $person_id not"
. " found ");
if (null === $person) {
throw $this->createNotFoundException("Person with id {$person_id} not"
. ' found ');
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_UPDATE',
$person,
"You are not allowed to edit this person."
'You are not allowed to edit this person.'
);
$address = $this->findAddressById($person, $address_id);
$form = $this->createEditForm($person, $address);
return $this->render('ChillPersonBundle:Address:edit.html.twig', array(
'person' => $person,
'address' => $address,
'form' => $form->createView()
));
return $this->render('ChillPersonBundle:Address:edit.html.twig', [
'person' => $person,
'address' => $address,
'form' => $form->createView(),
]);
}
public function listAction($person_id)
{
$person = $this->getDoctrine()->getManager()
->getRepository('ChillPersonBundle:Person')
->find($person_id);
if (null === $person) {
throw $this->createNotFoundException("Person with id {$person_id} not"
. ' found ');
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_SEE',
$person,
'You are not allowed to edit this person.'
);
return $this->render('ChillPersonBundle:Address:list.html.twig', [
'person' => $person,
]);
}
public function newAction($person_id)
{
$person = $this->getDoctrine()->getManager()
->getRepository('ChillPersonBundle:Person')
->find($person_id);
if (null === $person) {
throw $this->createNotFoundException("Person with id {$person_id} not"
. ' found ');
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_UPDATE',
$person,
'You are not allowed to edit this person.'
);
$address = new Address();
$form = $this->createCreateForm($person, $address);
return $this->render('ChillPersonBundle:Address:new.html.twig', [
'person' => $person,
'form' => $form->createView(),
]);
}
public function updateAction($person_id, $address_id, Request $request)
@@ -195,15 +181,15 @@ class PersonAddressController extends AbstractController
->getRepository('ChillPersonBundle:Person')
->find($person_id);
if ($person === null) {
throw $this->createNotFoundException("Person with id $person_id not"
. " found ");
if (null === $person) {
throw $this->createNotFoundException("Person with id {$person_id} not"
. ' found ');
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_UPDATE',
$person,
"You are not allowed to edit this person."
'You are not allowed to edit this person.'
);
$address = $this->findAddressById($person, $address_id);
@@ -211,7 +197,7 @@ class PersonAddressController extends AbstractController
$form = $this->createEditForm($person, $address);
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {
if ($form->isSubmitted() && $form->isValid()) {
$validatePersonErrors = $this->validatePerson($person);
if (count($validatePersonErrors) !== 0) {
@@ -220,92 +206,87 @@ class PersonAddressController extends AbstractController
}
} elseif ($form->isValid()) {
$this->getDoctrine()->getManager()
->flush();
->flush();
$this->addFlash('success', $this->get('translator')->trans(
"The address has been successfully updated"
'The address has been successfully updated'
));
return $this->redirectToRoute('chill_person_address_list', array(
'person_id' => $person->getId()
));
return $this->redirectToRoute('chill_person_address_list', [
'person_id' => $person->getId(),
]);
} else {
$this->addFlash('error', $this->get('translator')
->trans('Error when updating the period'));
}
}
return $this->render('ChillPersonBundle:Address:edit.html.twig', array(
'person' => $person,
'address' => $address,
'form' => $form->createView()
));
return $this->render('ChillPersonBundle:Address:edit.html.twig', [
'person' => $person,
'address' => $address,
'form' => $form->createView(),
]);
}
/**
* @param Person $person
* @param Address $address
* @return \Symfony\Component\Form\Form
*/
protected function createEditForm(Person $person, Address $address)
{
$form = $this->createForm(AddressType::class, $address, array(
'method' => 'POST',
'action' => $this->generateUrl('chill_person_address_update', array(
'person_id' => $person->getId(),
'address_id' => $address->getId()
)),
'has_no_address' => true
));
$form->add('submit', SubmitType::class, array(
'label' => 'Submit'
));
return $form;
}
/**
*
* @param Person $person
* @param Address $address
* @return \Symfony\Component\Form\Form
*/
protected function createCreateForm(Person $person, Address $address)
{
$form = $this->createForm(AddressType::class, $address, array(
'method' => 'POST',
'action' => $this->generateUrl('chill_person_address_create', array(
'person_id' => $person->getId()
)),
'has_no_address' => true
));
$form = $this->createForm(AddressType::class, $address, [
'method' => 'POST',
'action' => $this->generateUrl('chill_person_address_create', [
'person_id' => $person->getId(),
]),
'has_no_address' => true,
]);
$form->add('submit', SubmitType::class, array(
'label' => 'Submit'
));
$form->add('submit', SubmitType::class, [
'label' => 'Submit',
]);
return $form;
}
/**
* @return \Symfony\Component\Form\Form
*/
protected function createEditForm(Person $person, Address $address)
{
$form = $this->createForm(AddressType::class, $address, [
'method' => 'POST',
'action' => $this->generateUrl('chill_person_address_update', [
'person_id' => $person->getId(),
'address_id' => $address->getId(),
]),
'has_no_address' => true,
]);
$form->add('submit', SubmitType::class, [
'label' => 'Submit',
]);
return $form;
}
/**
*
* @param Person $person
* @param int $address_id
* @return Address
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the address id does not exists or is not associated with given person
*
* @return Address
*/
protected function findAddressById(Person $person, $address_id)
{
// filtering address
$criteria = Criteria::create()
->where(Criteria::expr()->eq('id', $address_id))
->setMaxResults(1);
->where(Criteria::expr()->eq('id', $address_id))
->setMaxResults(1);
$addresses = $person->getAddresses()->matching($criteria);
if (count($addresses) === 0) {
throw $this->createNotFoundException("Address with id $address_id "
. "matching person $person_id not found ");
throw $this->createNotFoundException("Address with id {$address_id} "
. "matching person {$person_id} not found ");
}
return $addresses->first();
@@ -313,16 +294,17 @@ class PersonAddressController extends AbstractController
/**
* @param Chill\PersonBundle\Entity\Person $person
*
* @return \Symfony\Component\Validator\ConstraintViolationListInterface
*/
private function validatePerson(Person $person)
{
$errors = $this->validator
->validate($person, null, array('Default'));
->validate($person, null, ['Default']);
$errors_addresses_consistent = $this->validator
->validate($person, null, array('addresses_consistent'));
->validate($person, null, ['addresses_consistent']);
foreach($errors_addresses_consistent as $error) {
foreach ($errors_addresses_consistent as $error) {
$errors->add($error);
}

View File

@@ -1,63 +1,42 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Psr\Log\LoggerInterface;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Form\PersonType;
use Chill\PersonBundle\Form\CreationPersonType;
use Chill\PersonBundle\Form\PersonType;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Search\SimilarPersonMatcher;
use DateTime;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Search\SimilarPersonMatcher;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\MainBundle\Search\SearchProvider;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Class PersonController
*
* @package Chill\PersonBundle\Controller
*/
use function count;
class PersonController extends AbstractController
{
/**
*
* @var SimilarPersonMatcher
* @var ConfigPersonAltNamesHelper
*/
protected $similarPersonMatcher;
/**
*
* @var TranslatorInterface
*/
protected $translator;
protected $configPersonAltNameHelper;
/**
* @var EventDispatcherInterface
@@ -65,28 +44,31 @@ class PersonController extends AbstractController
protected $eventDispatcher;
/**
*
* @var PersonRepository;
*/
protected $personRepository;
/**
*
* @var ConfigPersonAltNamesHelper
* @var SimilarPersonMatcher
*/
protected $configPersonAltNameHelper;
protected $similarPersonMatcher;
/**
* @var TranslatorInterface
*/
protected $translator;
/**
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/**
* @var ValidatorInterface
*/
private $validator;
public function __construct(
public function __construct(
SimilarPersonMatcher $similarPersonMatcher,
TranslatorInterface $translator,
EventDispatcherInterface $eventDispatcher,
@@ -102,147 +84,292 @@ class PersonController extends AbstractController
$this->personRepository = $personRepository;
$this->logger = $logger;
$this->validator = $validator;
}
public function getCFGroup()
{
$cFGroup = null;
$em = $this->getDoctrine()->getManager();
$cFDefaultGroup = $em->getRepository("ChillCustomFieldsBundle:CustomFieldsDefaultGroup")
->findOneByEntity("Chill\PersonBundle\Entity\Person");
if($cFDefaultGroup) {
$cFGroup = $cFDefaultGroup->getCustomFieldsGroup();
}
return $cFGroup;
}
public function viewAction($person_id)
public function createAction(Request $request)
{
$person = $this->_getPerson($person_id);
if ($request->getMethod() !== 'POST') {
$r = new Response('You must send something to create a person !');
$r->setStatusCode(400);
if ($person === null) {
throw $this->createNotFoundException("Person with id $person_id not"
. " found on this server");
return $r;
}
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person,
"You are not allowed to see this person.");
$form = $this->createForm(CreationPersonType::class, null, [
'form_status' => CreationPersonType::FORM_REVIEWED,
]);
$event = new PrivacyEvent($person);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
$form->handleRequest($request);
return $this->render('ChillPersonBundle:Person:view.html.twig',
array(
"person" => $person,
"cFGroup" => $this->getCFGroup(),
"alt_names" => $this->configPersonAltNameHelper->getChoices(),
$person = $this->_bindCreationForm($form);
$errors = $this->_validatePersonAndAccompanyingPeriod($person);
$this->denyAccessUnlessGranted(
'CHILL_PERSON_CREATE',
$person,
'You are not allowed to create this person'
);
if ($errors->count() === 0) {
$em = $this->getDoctrine()->getManager();
$em->persist($person);
$em->flush();
return $this->redirect($this->generateUrl(
'chill_person_general_edit',
['person_id' => $person->getId()]
));
}
$text = "this should not happen if you reviewed your submission\n";
foreach ($errors as $error) {
$text .= $error->getMessage() . "\n";
}
$r = new Response($text);
$r->setStatusCode(400);
return $r;
}
public function editAction($person_id)
{
$person = $this->_getPerson($person_id);
if ($person === null) {
if (null === $person) {
return $this->createNotFoundException();
}
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
'You are not allowed to edit this person');
$form = $this->createForm(PersonType::class, $person,
array(
"action" => $this->generateUrl('chill_person_general_update',
array("person_id" => $person_id)),
"cFGroup" => $this->getCFGroup()
)
$this->denyAccessUnlessGranted(
'CHILL_PERSON_UPDATE',
$person,
'You are not allowed to edit this person'
);
return $this->render('ChillPersonBundle:Person:edit.html.twig',
array('person' => $person, 'form' => $form->createView()));
$form = $this->createForm(
PersonType::class,
$person,
[
'action' => $this->generateUrl(
'chill_person_general_update',
['person_id' => $person_id]
),
'cFGroup' => $this->getCFGroup(),
]
);
return $this->render(
'ChillPersonBundle:Person:edit.html.twig',
['person' => $person, 'form' => $form->createView()]
);
}
public function updateAction($person_id, Request $request)
public function getCFGroup()
{
$person = $this->_getPerson($person_id);
$cFGroup = null;
if ($person === null) {
return $this->createNotFoundException();
$em = $this->getDoctrine()->getManager();
$cFDefaultGroup = $em->getRepository('ChillCustomFieldsBundle:CustomFieldsDefaultGroup')
->findOneByEntity('Chill\\PersonBundle\\Entity\\Person');
if ($cFDefaultGroup) {
$cFGroup = $cFDefaultGroup->getCustomFieldsGroup();
}
$this->denyAccessUnlessGranted('CHILL_PERSON_UPDATE', $person,
'You are not allowed to edit this person');
$form = $this->createForm(PersonType::class, $person,
array("cFGroup" => $this->getCFGroup()));
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
if ( ! $form->isValid() ) {
$this->get('session')
->getFlashBag()->add('error', $this->translator
->trans('This form contains errors'));
return $this->render('ChillPersonBundle:Person:edit.html.twig',
array('person' => $person,
'form' => $form->createView()));
}
$this->get('session')->getFlashBag()
->add('success',
$this->get('translator')
->trans('The person data has been updated')
);
$em = $this->getDoctrine()->getManager();
$em->flush();
$url = $this->generateUrl('chill_person_view', array(
'person_id' => $person->getId()
));
return $this->redirect($url);
}
return $cFGroup;
}
public function newAction()
{
// this is a dummy default center.
$defaultCenter = $this->get('security.token_storage')
->getToken()
->getUser()
->getGroupCenters()[0]
->getCenter();
->getToken()
->getUser()
->getGroupCenters()[0]
->getCenter();
$person = (new Person(new \DateTime('now')))
->setCenter($defaultCenter);
$person = (new Person(new DateTime('now')))
->setCenter($defaultCenter);
$form = $this->createForm(
CreationPersonType::class,
$person,
array(
'action' => $this->generateUrl('chill_person_review'),
'form_status' => CreationPersonType::FORM_NOT_REVIEWED
));
CreationPersonType::class,
$person,
[
'action' => $this->generateUrl('chill_person_review'),
'form_status' => CreationPersonType::FORM_NOT_REVIEWED,
]
);
return $this->_renderNewForm($form);
}
private function _renderNewForm($form)
public function reviewAction(Request $request)
{
return $this->render('ChillPersonBundle:Person:create.html.twig',
array(
'form' => $form->createView()
));
if ($request->getMethod() !== 'POST') {
$r = new Response('You must send something to review the creation of a new Person');
$r->setStatusCode(400);
return $r;
}
$form = $this->createForm(
//CreationPersonType::NAME,
CreationPersonType::class,
new Person(),
[
'action' => $this->generateUrl('chill_person_create'),
'form_status' => CreationPersonType::FORM_BEING_REVIEWED,
]
);
$form->handleRequest($request);
$person = $this->_bindCreationForm($form);
$errors = $this->_validatePersonAndAccompanyingPeriod($person);
$this->logger->info(sprintf('Person created with %d errors ', count($errors)));
if ($errors->count() > 0) {
$this->logger->info('The created person has errors');
$flashBag = $this->get('session')->getFlashBag();
$translator = $this->get('translator');
$flashBag->add('error', $translator->trans('The person data are not valid'));
foreach ($errors as $error) {
$flashBag->add('info', $error->getMessage());
}
$form = $this->createForm(
CreationPersonType::NAME,
$person,
[
'action' => $this->generateUrl('chill_person_review'),
'form_status' => CreationPersonType::FORM_NOT_REVIEWED,
]
);
$form->handleRequest($request);
return $this->_renderNewForm($form);
}
$this->logger->info('Person created without errors');
$alternatePersons = $this->similarPersonMatcher
->matchPerson($person);
if (count($alternatePersons) === 0) {
return $this->forward('ChillPersonBundle:Person:create');
}
$this->get('session')->getFlashBag()->add(
'info',
$this->get('translator')->trans(
'%nb% person with similar name. Please verify that this is a new person',
['%nb%' => count($alternatePersons)]
)
);
return $this->render(
'ChillPersonBundle:Person:create_review.html.twig',
[
'person' => $person,
'alternatePersons' => $alternatePersons,
'firstName' => $form['firstName']->getData(),
'lastName' => $form['lastName']->getData(),
'birthdate' => $form['birthdate']->getData(),
'gender' => $form['gender']->getData(),
'creation_date' => $form['creation_date']->getData(),
'form' => $form->createView(), ]
);
}
public function updateAction($person_id, Request $request)
{
$person = $this->_getPerson($person_id);
if (null === $person) {
return $this->createNotFoundException();
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_UPDATE',
$person,
'You are not allowed to edit this person'
);
$form = $this->createForm(
PersonType::class,
$person,
['cFGroup' => $this->getCFGroup()]
);
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
if (!$form->isValid()) {
$this->get('session')
->getFlashBag()->add('error', $this->translator
->trans('This form contains errors'));
return $this->render(
'ChillPersonBundle:Person:edit.html.twig',
['person' => $person,
'form' => $form->createView(), ]
);
}
$this->get('session')->getFlashBag()
->add(
'success',
$this->get('translator')
->trans('The person data has been updated')
);
$em = $this->getDoctrine()->getManager();
$em->flush();
$url = $this->generateUrl('chill_person_view', [
'person_id' => $person->getId(),
]);
return $this->redirect($url);
}
}
public function viewAction($person_id)
{
$person = $this->_getPerson($person_id);
if (null === $person) {
throw $this->createNotFoundException("Person with id {$person_id} not"
. ' found on this server');
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_SEE',
$person,
'You are not allowed to see this person.'
);
$event = new PrivacyEvent($person);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->render(
'ChillPersonBundle:Person:view.html.twig',
[
'person' => $person,
'cFGroup' => $this->getCFGroup(),
'alt_names' => $this->configPersonAltNameHelper->getChoices(),
]
);
}
/**
*
* @param type $form
*
* @return \Chill\PersonBundle\Entity\Person
*/
private function _bindCreationForm($form)
@@ -268,14 +395,34 @@ class PersonController extends AbstractController
}
/**
* easy getting a person by his id.
*
* @param \Chill\PersonBundle\Entity\Person $person
* @param mixed $id
*
* @return \Chill\PersonBundle\Entity\Person
*/
private function _getPerson($id)
{
return $this->personRepository->find($id);
}
private function _renderNewForm($form)
{
return $this->render(
'ChillPersonBundle:Person:create.html.twig',
[
'form' => $form->createView(),
]
);
}
/**
* @return \Symfony\Component\Validator\ConstraintViolationListInterface
*/
private function _validatePersonAndAccompanyingPeriod(Person $person)
{
$errors = $this->validator
->validate($person, null, array('creation'));
->validate($person, null, ['creation']);
//validate accompanying periods
$periods = $person->getAccompanyingPeriods();
@@ -285,139 +432,11 @@ class PersonController extends AbstractController
->validate($period);
//group errors :
foreach($period_errors as $error) {
foreach ($period_errors as $error) {
$errors->add($error);
}
}
return $errors;
}
public function reviewAction(Request $request)
{
if ($request->getMethod() !== 'POST') {
$r = new Response("You must send something to review the creation of a new Person");
$r->setStatusCode(400);
return $r;
}
$form = $this->createForm(
//CreationPersonType::NAME,
CreationPersonType::class,
new Person(),
array(
'action' => $this->generateUrl('chill_person_create'),
'form_status' => CreationPersonType::FORM_BEING_REVIEWED
));
$form->handleRequest($request);
$person = $this->_bindCreationForm($form);
$errors = $this->_validatePersonAndAccompanyingPeriod($person);
$this->logger->info(sprintf('Person created with %d errors ', count($errors)));
if ($errors->count() > 0) {
$this->logger->info('The created person has errors');
$flashBag = $this->get('session')->getFlashBag();
$translator = $this->get('translator');
$flashBag->add('error', $translator->trans('The person data are not valid'));
foreach($errors as $error) {
$flashBag->add('info', $error->getMessage());
}
$form = $this->createForm(
CreationPersonType::NAME,
$person,
array(
'action' => $this->generateUrl('chill_person_review'),
'form_status' => CreationPersonType::FORM_NOT_REVIEWED
));
$form->handleRequest($request);
return $this->_renderNewForm($form);
} else {
$this->logger->info('Person created without errors');
}
$alternatePersons = $this->similarPersonMatcher
->matchPerson($person);
if (count($alternatePersons) === 0) {
return $this->forward('ChillPersonBundle:Person:create');
}
$this->get('session')->getFlashBag()->add('info',
$this->get('translator')->trans(
'%nb% person with similar name. Please verify that this is a new person',
array('%nb%' => count($alternatePersons)))
);
return $this->render('ChillPersonBundle:Person:create_review.html.twig',
array(
'person' => $person,
'alternatePersons' => $alternatePersons,
'firstName' => $form['firstName']->getData(),
'lastName' => $form['lastName']->getData(),
'birthdate' => $form['birthdate']->getData(),
'gender' => $form['gender']->getData(),
'creation_date' => $form['creation_date']->getData(),
'form' => $form->createView()));
}
public function createAction(Request $request)
{
if ($request->getMethod() !== 'POST') {
$r = new Response('You must send something to create a person !');
$r->setStatusCode(400);
return $r;
}
$form = $this->createForm(CreationPersonType::class, null, array(
'form_status' => CreationPersonType::FORM_REVIEWED
));
$form->handleRequest($request);
$person = $this->_bindCreationForm($form);
$errors = $this->_validatePersonAndAccompanyingPeriod($person);
$this->denyAccessUnlessGranted('CHILL_PERSON_CREATE', $person,
'You are not allowed to create this person');
if ($errors->count() === 0) {
$em = $this->getDoctrine()->getManager();
$em->persist($person);
$em->flush();
return $this->redirect($this->generateUrl('chill_person_general_edit',
array('person_id' => $person->getId())));
} else {
$text = "this should not happen if you reviewed your submission\n";
foreach ($errors as $error) {
$text .= $error->getMessage()."\n";
}
$r = new Response($text);
$r->setStatusCode(400);
return $r;
}
}
/**
* easy getting a person by his id
* @return \Chill\PersonBundle\Entity\Person
*/
private function _getPerson($id)
{
$person = $this->personRepository->find($id);
return $person;
}
}

View File

@@ -1,9 +1,22 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\ActivityBundle\Entity\Activity;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\EventBundle\Entity\Participation;
use Chill\PersonBundle\Actions\Remove\PersonMove;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonNotDuplicate;
use Chill\PersonBundle\Form\PersonConfimDuplicateType;
@@ -11,18 +24,32 @@ use Chill\PersonBundle\Form\PersonFindManuallyDuplicateType;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Search\SimilarPersonMatcher;
use Chill\TaskBundle\Entity\SingleTask;
use http\Exception\InvalidArgumentException;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\ActivityBundle\Entity\Activity;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\EventBundle\Entity\Participation;
use Chill\TaskBundle\Entity\SingleTask;
use function count;
class PersonDuplicateController extends Controller
{
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $eventDispatcher;
/**
* @var \Chill\PersonBundle\Actions\Remove\PersonMove
*/
private $personMove;
/**
* @var \Chill\PersonBundle\Repository\PersonRepository
*/
private $personRepository;
/**
* @var \Chill\PersonBundle\Search\SimilarPersonMatcher
*/
@@ -33,27 +60,12 @@ class PersonDuplicateController extends Controller
*/
private $translator;
/**
* @var \Chill\PersonBundle\Repository\PersonRepository
*/
private $personRepository;
/**
* @var \Chill\PersonBundle\Actions\Remove\PersonMove
*/
private $personMove;
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $eventDispatcher;
public function __construct(
SimilarPersonMatcher $similarPersonMatcher,
TranslatorInterface $translator,
PersonRepository $personRepository,
PersonMove $personMove,
EventDispatcherInterface $eventDispatcher
SimilarPersonMatcher $similarPersonMatcher,
TranslatorInterface $translator,
PersonRepository $personRepository,
PersonMove $personMove,
EventDispatcherInterface $eventDispatcher
) {
$this->similarPersonMatcher = $similarPersonMatcher;
$this->translator = $translator;
@@ -62,30 +74,6 @@ class PersonDuplicateController extends Controller
$this->eventDispatcher = $eventDispatcher;
}
public function viewAction($person_id)
{
$person = $this->_getPerson($person_id);
if ($person === null) {
throw $this->createNotFoundException("Person with id $person_id not"
. " found on this server");
}
$this->denyAccessUnlessGranted('CHILL_PERSON_DUPLICATE', $person,
"You are not allowed to see this person.");
$duplicatePersons = $this->similarPersonMatcher->
matchPerson($person, 0.5, SimilarPersonMatcher::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL);
$notDuplicatePersons = $this->getDoctrine()->getRepository(PersonNotDuplicate::class)
->findNotDuplicatePerson($person);
return $this->render('ChillPersonBundle:PersonDuplicate:view.html.twig', [
'person' => $person,
'duplicatePersons' => $duplicatePersons,
'notDuplicatePersons' => $notDuplicatePersons,
]);
}
public function confirmAction($person1_id, $person2_id, Request $request)
{
if ($person1_id === $person2_id) {
@@ -97,18 +85,21 @@ class PersonDuplicateController extends Controller
$person1->counters = $this->_getCounters($person1_id);
$person2->counters = $this->_getCounters($person2_id);
if ($person1 === null) {
throw $this->createNotFoundException("Person with id $person1_id not"
. " found on this server");
if (null === $person1) {
throw $this->createNotFoundException("Person with id {$person1_id} not"
. ' found on this server');
}
$this->denyAccessUnlessGranted('CHILL_PERSON_DUPLICATE', $person1,
"You are not allowed to see this person.");
$this->denyAccessUnlessGranted(
'CHILL_PERSON_DUPLICATE',
$person1,
'You are not allowed to see this person.'
);
if ($person2 === null) {
throw $this->createNotFoundException("Person with id $person2_id not"
. " found on this server");
if (null === $person2) {
throw $this->createNotFoundException("Person with id {$person2_id} not"
. ' found on this server');
}
$form = $this->createForm(PersonConfimDuplicateType::class);
@@ -116,10 +107,10 @@ class PersonDuplicateController extends Controller
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$event = new PrivacyEvent($person1, array(
$event = new PrivacyEvent($person1, [
'element_class' => Person::class,
'action' => 'move'
));
'action' => 'move',
]);
$event->addPerson($person2);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
@@ -128,7 +119,8 @@ class PersonDuplicateController extends Controller
$connection = $this->getDoctrine()->getConnection();
$connection->beginTransaction();
foreach($sqls as $sql) {
foreach ($sqls as $sql) {
$connection->executeQuery($sql);
}
$connection->commit();
@@ -143,15 +135,68 @@ class PersonDuplicateController extends Controller
]);
}
public function findManuallyDuplicateAction($person_id, Request $request)
{
$person = $this->_getPerson($person_id);
if (null === $person) {
throw $this->createNotFoundException("Person with id {$person_id} not"
. ' found on this server');
}
$this->denyAccessUnlessGranted(
'CHILL_PERSON_DUPLICATE',
$person,
'You are not allowed to see this person.'
);
$form = $this->createForm(PersonFindManuallyDuplicateType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$person2 = $form->get('person')->getData();
if (null === $person2) {
throw $this->createNotFoundException("Person with id {$person2->getId}() not"
. ' found on this server');
}
$direction = $form->get('direction')->getData();
if ('starting' === $direction) {
$params = [
'person1_id' => $person->getId(),
'person2_id' => $person2->getId(),
];
} else {
$params = [
'person1_id' => $person2->getId(),
'person2_id' => $person->getId(),
];
}
return $this->redirectToRoute('chill_person_duplicate_confirm', $params);
}
return $this->render('ChillPersonBundle:PersonDuplicate:find_manually.html.twig', [
'person' => $person,
'form' => $form->createView(),
]);
}
public function notDuplicateAction($person1_id, $person2_id)
{
[$person1, $person2] = $this->_getPersonsByPriority($person1_id, $person2_id);
$this->denyAccessUnlessGranted('CHILL_PERSON_DUPLICATE', $person1,
"You are not allowed to see this person.");
$this->denyAccessUnlessGranted(
'CHILL_PERSON_DUPLICATE',
$person1,
'You are not allowed to see this person.'
);
$personNotDuplicate = $this->getDoctrine()->getRepository(PersonNotDuplicate::class)
->findOneBy(['person1' => $person1, 'person2' => $person2]);
->findOneBy(['person1' => $person1, 'person2' => $person2]);
if (!$personNotDuplicate instanceof PersonNotDuplicate) {
$personNotDuplicate = new PersonNotDuplicate();
@@ -170,11 +215,14 @@ class PersonDuplicateController extends Controller
{
[$person1, $person2] = $this->_getPersonsByPriority($person1_id, $person2_id);
$this->denyAccessUnlessGranted('CHILL_PERSON_DUPLICATE', $person1,
"You are not allowed to see this person.");
$this->denyAccessUnlessGranted(
'CHILL_PERSON_DUPLICATE',
$person1,
'You are not allowed to see this person.'
);
$personNotDuplicate = $this->getDoctrine()->getRepository(PersonNotDuplicate::class)
->findOneBy(['person1' => $person1, 'person2' => $person2]);
->findOneBy(['person1' => $person1, 'person2' => $person2]);
if ($personNotDuplicate instanceof PersonNotDuplicate) {
$this->getDoctrine()->getManager()->remove($personNotDuplicate);
@@ -184,54 +232,57 @@ class PersonDuplicateController extends Controller
return $this->redirectToRoute('chill_person_duplicate_view', ['person_id' => $person1->getId()]);
}
public function findManuallyDuplicateAction($person_id, Request $request)
public function viewAction($person_id)
{
$person = $this->_getPerson($person_id);
if ($person === null) {
throw $this->createNotFoundException("Person with id $person_id not"
. " found on this server");
if (null === $person) {
throw $this->createNotFoundException("Person with id {$person_id} not"
. ' found on this server');
}
$this->denyAccessUnlessGranted('CHILL_PERSON_DUPLICATE', $person,
"You are not allowed to see this person.");
$this->denyAccessUnlessGranted(
'CHILL_PERSON_DUPLICATE',
$person,
'You are not allowed to see this person.'
);
$form = $this->createForm(PersonFindManuallyDuplicateType::class);
$duplicatePersons = $this->similarPersonMatcher->
matchPerson($person, 0.5, SimilarPersonMatcher::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL);
$form->handleRequest($request);
$notDuplicatePersons = $this->getDoctrine()->getRepository(PersonNotDuplicate::class)
->findNotDuplicatePerson($person);
if ($form->isSubmitted() && $form->isValid()) {
$person2 = $form->get('person')->getData();
if ($person2 === null) {
throw $this->createNotFoundException("Person with id $person2->getId() not"
. " found on this server");
}
$direction = $form->get('direction')->getData();
if ($direction === 'starting') {
$params = [
'person1_id' => $person->getId(),
'person2_id' => $person2->getId(),
];
} else {
$params = [
'person1_id' => $person2->getId(),
'person2_id' => $person->getId(),
];
}
return $this->redirectToRoute('chill_person_duplicate_confirm', $params);
}
return $this->render('ChillPersonBundle:PersonDuplicate:find_manually.html.twig', [
return $this->render('ChillPersonBundle:PersonDuplicate:view.html.twig', [
'person' => $person,
'form' => $form->createView(),
'duplicatePersons' => $duplicatePersons,
'notDuplicatePersons' => $notDuplicatePersons,
]);
}
private function _getCounters($id): ?array
{
$em = $this->getDoctrine()->getManager();
$nb_activity = $em->getRepository(Activity::class)->findBy(['person' => $id]);
$nb_document = $em->getRepository(PersonDocument::class)->findBy(['person' => $id]);
$nb_event = $em->getRepository(Participation::class)->findBy(['person' => $id]);
$nb_task = $em->getRepository(SingleTask::class)->countByParameters(['person' => $id]);
$person = $em->getRepository(Person::class)->findOneBy(['id' => $id]);
return [
'nb_activity' => count($nb_activity),
'nb_document' => count($nb_document),
'nb_event' => count($nb_event),
'nb_task' => $nb_task,
'nb_addresses' => count($person->getAddresses()),
];
}
/**
* easy getting a person by his id
* easy getting a person by his id.
*
* @param mixed $id
*/
private function _getPerson($id): ?Person
{
@@ -252,35 +303,16 @@ class PersonDuplicateController extends Controller
$person2 = $this->_getPerson($person2_id);
}
if ($person1 === null) {
throw $this->createNotFoundException("Person with id $person1_id not"
. " found on this server");
if (null === $person1) {
throw $this->createNotFoundException("Person with id {$person1_id} not"
. ' found on this server');
}
if ($person2 === null) {
throw $this->createNotFoundException("Person with id $person2_id not"
. " found on this server");
if (null === $person2) {
throw $this->createNotFoundException("Person with id {$person2_id} not"
. ' found on this server');
}
return [$person1, $person2];
}
private function _getCounters($id): ?array
{
$em = $this->getDoctrine()->getManager();
$nb_activity = $em->getRepository(Activity::class)->findBy(['person'=>$id]);
$nb_document = $em->getRepository(PersonDocument::class)->findBy(['person'=>$id]);
$nb_event = $em->getRepository(Participation::class)->findBy(['person'=>$id]);
$nb_task = $em->getRepository(SingleTask::class)->countByParameters(['person'=>$id]);
$person = $em->getRepository(Person::class)->findOneBy(['id'=>$id]);
return [
'nb_activity' => count($nb_activity),
'nb_document' => count($nb_document),
'nb_event' => count($nb_event),
'nb_task' => $nb_task,
'nb_addresses' => count($person->getAddresses())
];
}
}

View File

@@ -1,63 +1,45 @@
<?php
/*
* Copyright (C) 2015 Champs-Libres Coopérative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Timeline\TimelineBuilder;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Chill\MainBundle\Timeline\TimelineBuilder;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
/**
* Class TimelinePersonController
*
* @package Chill\PersonBundle\Controller
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class TimelinePersonController extends AbstractController
{
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
*
* @var TimelineBuilder
*/
protected $timelineBuilder;
/**
*
* @var PaginatorFactory
*/
protected $paginatorFactory;
/**
* @var TimelineBuilder
*/
protected $timelineBuilder;
/**
* TimelinePersonController constructor.
*
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(
EventDispatcherInterface $eventDispatcher,
@@ -68,42 +50,42 @@ class TimelinePersonController extends AbstractController
$this->timelineBuilder = $timelineBuilder;
$this->paginatorFactory = $paginatorFactory;
}
public function personAction(Request $request, $person_id)
{
$person = $this->getDoctrine()
->getRepository('ChillPersonBundle:Person')
->find($person_id);
->getRepository('ChillPersonBundle:Person')
->find($person_id);
if ($person === NULL) {
if (null === $person) {
throw $this->createNotFoundException();
}
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person);
$nbItems = $this->timelineBuilder->countItems('person',
[ 'person' => $person ]
);
$nbItems = $this->timelineBuilder->countItems(
'person',
['person' => $person]
);
$paginator = $this->paginatorFactory->create($nbItems);
$event = new PrivacyEvent($person, array('action' => 'timeline'));
$event = new PrivacyEvent($person, ['action' => 'timeline']);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->render('ChillPersonBundle:Timeline:index.html.twig', array
(
return $this->render(
'ChillPersonBundle:Timeline:index.html.twig',
[
'timeline' => $this->timelineBuilder->getTimelineHTML(
'person',
array('person' => $person),
'person',
['person' => $person],
$paginator->getCurrentPage()->getFirstItemNumber(),
$paginator->getItemsPerPage()
),
),
'person' => $person,
'nb_items' => $nbItems,
'paginator' => $paginator
)
'paginator' => $paginator,
]
);
}
}

View File

@@ -1,89 +1,72 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <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/>.
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DataFixtures\ORM;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
/**
* Load closing motives into database
*
* @author Julien Fastré <julien arobase fastre point info>
* Load closing motives into database.
*/
class LoadAccompanyingPeriodClosingMotive extends AbstractFixture
implements OrderedFixtureInterface
class LoadAccompanyingPeriodClosingMotive extends AbstractFixture implements OrderedFixtureInterface
{
public function getOrder() {
public static $closingMotives = [
'nothing_to_do' => [
'name' => [
'fr' => 'Plus rien à faire',
'en' => 'Nothing to do',
'nl' => 'nieks meer te doen',
],
],
'did_not_come_back' => [
'name' => [
'fr' => "N'est plus revenu",
'en' => "Did'nt come back",
'nl' => 'Niet teruggekomen',
],
],
'no_more_money' => [
'active' => false,
'name' => [
'fr' => "Plus d'argent",
'en' => 'No more money',
'nl' => 'Geen geld',
],
],
];
public static $references = [];
public function getOrder()
{
return 9500;
}
public static $closingMotives = array(
'nothing_to_do' => array(
'name' => array(
'fr' => 'Plus rien à faire',
'en' => 'Nothing to do',
'nl' => 'nieks meer te doen'
)
),
'did_not_come_back' => array(
'name' => array(
'fr' => "N'est plus revenu",
'en' => "Did'nt come back",
'nl' => "Niet teruggekomen"
)
),
'no_more_money' => array(
'active' => false,
'name' => array(
'fr' => "Plus d'argent",
'en' => "No more money",
'nl' => "Geen geld"
)
)
);
public static $references = array();
public function load(ObjectManager $manager)
public function load(ObjectManager $manager)
{
foreach (static::$closingMotives as $ref => $new) {
$motive = new ClosingMotive();
$motive->setName($new['name'])
->setActive((isset($new['active']) ? $new['active'] : true))
;
->setActive(($new['active'] ?? true));
$manager->persist($motive);
$this->addReference($ref, $motive);
echo "Adding ClosingMotive $ref\n";
echo "Adding ClosingMotive {$ref}\n";
}
$manager->flush();
}
}

View File

@@ -1,78 +1,65 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DataFixtures\ORM;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldText;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldTitle;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Chill\CustomFieldsBundle\Entity\CustomFieldsDefaultGroup;
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Person;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use RuntimeException;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldTitle;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldText;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice;
use Chill\CustomFieldsBundle\Entity\CustomFieldsDefaultGroup;
use Chill\PersonBundle\Entity\Person;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class LoadCustomFields extends AbstractFixture implements OrderedFixtureInterface,
ContainerAwareInterface
class LoadCustomFields extends AbstractFixture implements
ContainerAwareInterface,
OrderedFixtureInterface
{
/**
*
* @var ContainerInterface
*/
private $container;
/**
*
* @var CustomField
*/
private $customFieldText;
/**
*
* @var CustomField
*/
private $customFieldChoice;
/**
* @var CustomField
*/
private $customFieldText;
/**
* @var TranslatableStringHelper
*/
private $translatableStringHelper;
/**
* @var TranslatorInterface
*/
private $translator;
/**
* LoadCustomFields constructor.
*
* @param TranslatableStringHelper $translatableStringHelper
* @param TranslatorInterface $translator
*/
public function __construct(
TranslatableStringHelper $translatableStringHelper,
@@ -81,21 +68,12 @@ class LoadCustomFields extends AbstractFixture implements OrderedFixtureInterfac
$this->translatableStringHelper = $translatableStringHelper;
$this->translator = $translator;
}
//put your code here
public function getOrder()
{
return 10003;
}
public function setContainer(ContainerInterface $container = null)
{
if ($container === null) {
throw new \RuntimeException("The given container should not be null");
}
$this->container = $container;
}
public function load(ObjectManager $manager)
{
@@ -103,45 +81,16 @@ class LoadCustomFields extends AbstractFixture implements OrderedFixtureInterfac
$this->loadData($manager);
$manager->flush();
}
private function loadData(ObjectManager $manager)
public function setContainer(?ContainerInterface $container = null)
{
$personIds = $this->container->get('doctrine.orm.entity_manager')
->createQuery("SELECT person.id FROM ChillPersonBundle:Person person")
->getScalarResult();
// get possible values for cfGroup
$choices = array_map(
function($a) { return $a["slug"]; },
$this->customFieldChoice->getOptions()["choices"]
);
// create faker
$faker = \Faker\Factory::create('fr_FR');
// select a set of people and add data
foreach ($personIds as $id) {
// add info on 1 person on 2
if (rand(0,1) === 1) {
/* @var $person Person */
$person = $manager->getRepository(Person::class)->find($id);
$person->setCFData(array(
"remarques" => $this->createCustomFieldText()
->serialize($faker->text(rand(150, 250)), $this->customFieldText),
"document-d-identite" => $this->createCustomFieldChoice()
->serialize(array($choices[array_rand($choices)]), $this->customFieldChoice)
));
}
if (null === $container) {
throw new RuntimeException('The given container should not be null');
}
$this->container = $container;
}
private function createCustomFieldText()
{
return new CustomFieldText(
$this->container->get('request_stack'),
$this->container->get('templating'),
$this->translatableStringHelper
);
}
private function createCustomFieldChoice()
{
return new CustomFieldChoice(
@@ -150,80 +99,115 @@ class LoadCustomFields extends AbstractFixture implements OrderedFixtureInterfac
$this->translatableStringHelper
);
}
private function createCustomFieldText()
{
return new CustomFieldText(
$this->container->get('request_stack'),
$this->container->get('templating'),
$this->translatableStringHelper
);
}
private function loadData(ObjectManager $manager)
{
$personIds = $this->container->get('doctrine.orm.entity_manager')
->createQuery('SELECT person.id FROM ChillPersonBundle:Person person')
->getScalarResult();
// get possible values for cfGroup
$choices = array_map(
static function ($a) {
return $a['slug'];
},
$this->customFieldChoice->getOptions()['choices']
);
// create faker
$faker = \Faker\Factory::create('fr_FR');
// select a set of people and add data
foreach ($personIds as $id) {
// add info on 1 person on 2
if (mt_rand(0, 1) === 1) {
/** @var Person $person */
$person = $manager->getRepository(Person::class)->find($id);
$person->setCFData([
'remarques' => $this->createCustomFieldText()
->serialize($faker->text(mt_rand(150, 250)), $this->customFieldText),
'document-d-identite' => $this->createCustomFieldChoice()
->serialize([$choices[array_rand($choices)]], $this->customFieldChoice),
]);
}
}
}
private function loadFields(ObjectManager $manager)
{
$cfGroup = (new CustomFieldsGroup())
->setEntity(Person::class)
->setName(array("fr" => "Données"))
;
->setName(['fr' => 'Données']);
$manager->persist($cfGroup);
// make this group default for Person::class
$manager->persist(
(new CustomFieldsDefaultGroup())
->setCustomFieldsGroup($cfGroup)
->setEntity(Person::class)
);
);
// create title field
$customField0 = (new CustomField())
->setActive(true)
->setName(array("fr" => "Données personnalisées"))
->setSlug("personal-data")
->setName(['fr' => 'Données personnalisées'])
->setSlug('personal-data')
->setOrdering(10)
->setType('title')
->setOptions(array(CustomFieldTitle::TYPE => CustomFieldTitle::TYPE_TITLE))
->setCustomFieldsGroup($cfGroup)
;
->setOptions([CustomFieldTitle::TYPE => CustomFieldTitle::TYPE_TITLE])
->setCustomFieldsGroup($cfGroup);
$manager->persist($customField0);
// create text field
$this->customFieldText = (new CustomField())
->setActive(true)
->setName(array("fr" => "Remarques"))
->setSlug("remarques")
->setName(['fr' => 'Remarques'])
->setSlug('remarques')
->setOrdering(20)
->setType('text')
->setOptions(array('maxLength' => 5000))
->setCustomFieldsGroup($cfGroup)
;
->setOptions(['maxLength' => 5000])
->setCustomFieldsGroup($cfGroup);
$manager->persist($this->customFieldText);
// create choice field
$this->customFieldChoice = (new CustomField())
->setActive(true)
->setName(array("fr" => "Document d'identité"))
->setSlug("document-d-identite")
->setName(['fr' => "Document d'identité"])
->setSlug('document-d-identite')
->setOrdering(30)
->setType('choice')
->setCustomFieldsGroup($cfGroup)
->setOptions(array(
"multiple" => true,
"other" => false,
"expanded" => true,
"active" => true,
"slug" => "document-d-identite",
"choices" => array(
array(
"name" => array("fr" => "Carte d'identité"),
"active" => true,
"slug" => "carte-d-identite"
),
array(
"name" => array("fr" => "Passeport"),
"active" => true,
"slug" => "passeport"
),
array(
"name" => array("fr" => "Titre de séjour"),
"active" => true,
"slug" => "passeport"
)
)
))
;
->setOptions([
'multiple' => true,
'other' => false,
'expanded' => true,
'active' => true,
'slug' => 'document-d-identite',
'choices' => [
[
'name' => ['fr' => "Carte d'identité"],
'active' => true,
'slug' => 'carte-d-identite',
],
[
'name' => ['fr' => 'Passeport'],
'active' => true,
'slug' => 'passeport',
],
[
'name' => ['fr' => 'Titre de séjour'],
'active' => true,
'slug' => 'passeport',
],
],
]);
$manager->persist($this->customFieldChoice);
}
}

View File

@@ -1,66 +1,56 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DataFixtures\ORM;
use Chill\PersonBundle\Entity\MaritalStatus;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use Chill\PersonBundle\Entity\MaritalStatus;
/**
* Load marital status into database
*
* @author Marc Ducobu <marc@champs-libres.coop>
* Load marital status into database.
*/
class LoadMaritalStatus extends AbstractFixture implements OrderedFixtureInterface
{
private $maritalStatuses = [
['id' => 'single', 'name' =>['en' => 'single', 'fr' => 'célibataire']],
['id' => 'married', 'name' =>['en' => 'married', 'fr' => 'marié(e)']],
['id' => 'widow', 'name' =>['en' => 'widow', 'fr' => 'veuf veuve ']],
['id' => 'separat', 'name' =>['en' => 'separated', 'fr' => 'séparé(e)']],
['id' => 'divorce', 'name' =>['en' => 'divorced', 'fr' => 'divorcé(e)']],
['id' => 'legalco', 'name' =>['en' => 'legal cohabitant', 'fr' => 'cohabitant(e) légal(e)']],
['id' => 'unknown', 'name' =>['en' => 'unknown', 'fr' => 'indéterminé']]
['id' => 'single', 'name' => ['en' => 'single', 'fr' => 'célibataire']],
['id' => 'married', 'name' => ['en' => 'married', 'fr' => 'marié(e)']],
['id' => 'widow', 'name' => ['en' => 'widow', 'fr' => 'veuf veuve ']],
['id' => 'separat', 'name' => ['en' => 'separated', 'fr' => 'séparé(e)']],
['id' => 'divorce', 'name' => ['en' => 'divorced', 'fr' => 'divorcé(e)']],
['id' => 'legalco', 'name' => ['en' => 'legal cohabitant', 'fr' => 'cohabitant(e) légal(e)']],
['id' => 'unknown', 'name' => ['en' => 'unknown', 'fr' => 'indéterminé']],
];
public function getOrder()
{
return 9999;
}
public function load(ObjectManager $manager)
{
echo "loading maritalStatuses... \n";
foreach ($this->maritalStatuses as $ms) {
echo $ms['name']['en'].' ';
echo $ms['name']['en'] . ' ';
$new_ms = new MaritalStatus();
$new_ms->setId($ms['id']);
$new_ms->setName($ms['name']);
$this->addReference('ms_'.$ms['id'], $new_ms);
$this->addReference('ms_' . $ms['id'], $new_ms);
$manager->persist($new_ms);
}
$manager->flush();
}
}

View File

@@ -1,292 +1,71 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DataFixtures\ORM;
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
use Chill\MainBundle\Entity\Address;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use DateTime;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use Chill\PersonBundle\Entity\Person;
use Exception;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
use Chill\MainBundle\Entity\Address;
use function call_user_func;
use function is_array;
/**
* Load people into database
*
* @author Julien Fastré <julien arobase fastre point info>
* @author Marc Ducobu <marc@champs-libres.coop>
* Load people into database.
*/
class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{
class LoadPeople extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
protected $faker;
public function __construct()
{
$this->faker = \Faker\Factory::create('fr_FR');
}
public function prepare()
{
//prepare days, month, years
$y = 1950;
do {
$this->years[] = $y;
$y = $y +1;
} while ($y >= 1990);
$m = 1;
do {
$this->month[] = $m;
$m = $m +1;
} while ($m >= 12);
$d = 1;
do {
$this->day[] = $d;
$d = $d + 1;
} while ($d <= 28);
}
public function getOrder()
{
return 10000;
}
public function load(ObjectManager $manager)
{
$this->loadRandPeople($manager);
$this->loadExpectedPeople($manager);
$manager->flush();
}
public function loadExpectedPeople(ObjectManager $manager)
{
echo "loading expected people...\n";
foreach ($this->peoples as $person) {
$this->addAPerson($this->fillWithDefault($person), $manager);
}
}
public function loadRandPeople(ObjectManager $manager)
{
echo "loading rand people...\n";
$this->prepare();
$chooseLastNameOrTri = array('tri', 'tri', 'name', 'tri');
$i = 0;
do {
$i++;
$sex = $this->genders[array_rand($this->genders)];
if ($chooseLastNameOrTri[array_rand($chooseLastNameOrTri)] === 'tri' ) {
$length = rand(2, 3);
$lastName = '';
for ($j = 0; $j <= $length; $j++) {
$lastName .= $this->lastNamesTrigrams[array_rand($this->lastNamesTrigrams)];
}
$lastName = ucfirst($lastName);
} else {
$lastName = $this->lastNames[array_rand($this->lastNames)];
}
if ($sex === Person::MALE_GENDER) {
$firstName = $this->firstNamesMale[array_rand($this->firstNamesMale)];
} else {
$firstName = $this->firstNamesFemale[array_rand($this->firstNamesFemale)];
}
// add an address on 80% of the created people
if (rand(0,100) < 80) {
$address = $this->getRandomAddress();
// on 30% of those person, add multiple addresses
if (rand(0,10) < 4) {
$address = array(
$address,
$this->getRandomAddress()
);
}
} else {
$address = null;
}
$person = array(
'FirstName' => $firstName,
'LastName' => $lastName,
'Gender' => $sex,
'Nationality' => (rand(0,100) > 50) ? NULL: 'BE',
'center' => (rand(0,1) == 0) ? 'centerA': 'centerB',
'Address' => $address,
'maritalStatus' => $this->maritalStatusRef[array_rand($this->maritalStatusRef)]
);
$this->addAPerson($this->fillWithDefault($person), $manager);
} while ($i <= 100);
}
/**
* fill a person array with default value
*
* @param string[] $specific
*/
private function fillWithDefault(array $specific)
{
return array_merge(array(
'Birthdate' => "1960-10-12",
'PlaceOfBirth' => "Ottignies Louvain-La-Neuve",
'Gender' => Person::MALE_GENDER,
'Email' => "Email d'un ami: roger@tt.com",
'CountryOfBirth' => 'BE',
'Nationality' => 'BE',
'CFData' => array(),
'Address' => null
), $specific);
}
/**
* create a new person from array data
*
* @param array $person
* @param ObjectManager $manager
* @throws \Exception
*/
private function addAPerson(array $person, ObjectManager $manager)
{
$p = new Person();
foreach ($person as $key => $value) {
switch ($key) {
case 'CountryOfBirth':
case 'Nationality':
$value = $this->getCountry($value);
break;
case 'Birthdate':
$value = new \DateTime($value);
break;
case 'center':
case 'maritalStatus':
$value = $this->getReference($value);
break;
case 'accompanyingPeriods':
$this->addAccompanyingPeriods($p, $value, $manager);
break;
}
//try to add the data using the setSomething function,
// if not possible, fallback to addSomething function
if (method_exists($p, 'set'.$key)) {
call_user_func(array($p, 'set'.$key), $value);
} elseif (method_exists($p, 'add'.$key)) {
// if we have a "addSomething", we may have multiple items to add
// so, we set the value in an array if it is not an array, and
// will call the function addSomething multiple times
if (!is_array($value)) {
$value = array($value);
}
foreach($value as $v) {
if ($v !== NULL) {
call_user_func(array($p, 'add'.$key), $v);
}
}
}
}
private $day = [];
$manager->persist($p);
echo "add person'".$p->__toString()."'\n";
}
/**
* Creata a random address
*
* @return Address
*/
private function getRandomAddress()
{
return (new Address())
->setStreetAddress1($this->faker->streetAddress)
->setStreetAddress2(
rand(0,9) > 5 ? $this->faker->streetAddress : ''
)
->setPostcode($this->getReference(
LoadPostalCodes::$refs[array_rand(LoadPostalCodes::$refs)]
))
->setValidFrom($this->faker->dateTimeBetween('-5 years'))
;
}
private function getCountry($countryCode)
{
if ($countryCode === NULL) {
return NULL;
}
return $this->container->get('doctrine.orm.entity_manager')
->getRepository('ChillMainBundle:Country')
->findOneByCountryCode($countryCode);
}
private $firstNamesFemale = ['Svedana', 'Sevlatina', 'Irène', 'Marcelle',
'Corentine', 'Alfonsine', 'Caroline', 'Solange', 'Gostine', 'Fatoumata', 'Nicole',
'Groseille', 'Chana', 'Oxana', 'Ivana', 'Julie', 'Tina', 'Adèle', ];
private $firstNamesMale = ['Jean', 'Mohamed', 'Alfred', 'Robert', 'Justin', 'Brian',
'Compère', 'Jean-de-Dieu', 'Charles', 'Pierre', 'Luc', 'Mathieu', 'Alain', 'Etienne', 'Eric',
'Corentin', 'Gaston', 'Spirou', 'Fantasio', 'Mahmadou', 'Mohamidou', 'Vursuv', 'Youssef', ];
private $genders = [Person::MALE_GENDER, Person::FEMALE_GENDER];
private $lastNames = ['Diallo', 'Bah', 'Gaillot', 'Martin'];
private $lastNamesTrigrams = ['fas', 'tré', 'hu', 'blart', 'van', 'der', 'lin', 'den',
'ta', 'mi', 'net', 'gna', 'bol', 'sac', 'ré', 'jo', 'du', 'pont', 'cas', 'tor', 'rob', 'al',
'ma', 'gone', 'car', 'fu', 'ka', 'lot', 'no', 'va', 'du', 'bu', 'su', 'jau', 'tte', 'sir',
'lo', 'to', 'cho', 'car', 'mo', 'zu', 'qi', 'mu', ];
private $maritalStatusRef = ['ms_single', 'ms_married', 'ms_widow', 'ms_separat',
'ms_divorce', 'ms_legalco', 'ms_unknown'];
private $firstNamesMale = array("Jean", "Mohamed", "Alfred", "Robert", "Justin", "Brian",
"Compère", "Jean-de-Dieu", "Charles", "Pierre", "Luc", "Mathieu", "Alain", "Etienne", "Eric",
"Corentin", "Gaston", "Spirou", "Fantasio", "Mahmadou", "Mohamidou", "Vursuv", "Youssef" );
private $firstNamesFemale = array("Svedana", "Sevlatina", "Irène", "Marcelle",
"Corentine", "Alfonsine", "Caroline", "Solange", "Gostine", "Fatoumata", "Nicole",
"Groseille", "Chana", "Oxana", "Ivana", "Julie", "Tina", "Adèle" );
private $lastNames = array("Diallo", "Bah", "Gaillot", "Martin");
private $lastNamesTrigrams = array("fas", "tré", "hu", 'blart', 'van', 'der', 'lin', 'den',
'ta', 'mi', 'net', 'gna', 'bol', 'sac', 'ré', 'jo', 'du', 'pont', 'cas', 'tor', 'rob', 'al',
'ma', 'gone', 'car',"fu", "ka", "lot", "no", "va", "du", "bu", "su", "jau", "tte", 'sir',
"lo", 'to', "cho", "car", 'mo','zu', 'qi', 'mu');
private $genders = array(Person::MALE_GENDER, Person::FEMALE_GENDER);
private $years = array();
private $month = array();
private $day = array();
private $peoples = array(
array(
'LastName' => "Depardieu",
'FirstName' => "Gérard",
'Birthdate' => "1948-12-27",
'PlaceOfBirth' => "Châteauroux",
'ms_divorce', 'ms_legalco', 'ms_unknown', ];
private $month = [];
private $peoples = [
[
'LastName' => 'Depardieu',
'FirstName' => 'Gérard',
'Birthdate' => '1948-12-27',
'PlaceOfBirth' => 'Châteauroux',
'Gender' => Person::MALE_GENDER,
'CountryOfBirth' => 'FR',
'Nationality' => 'RU',
@@ -297,87 +76,302 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
'from' => '2015-02-01',
'to' => '2015-10-30',
'remark' => 'oops',
],[
], [
'from' => '2017-06-01',
'to' => '2018-03-30',
'remark' => 'argg',
],[
], [
'from' => '2019-01-01',
'to' => '2019-12-31',
'remark' => 'blob',
]
]
),
array(
],
],
],
[
//to have a person with same firstname as Gérard Depardieu
'LastName' => "Depardieu",
'FirstName' => "Jean",
'Birthdate' => "1960-10-12",
'LastName' => 'Depardieu',
'FirstName' => 'Jean',
'Birthdate' => '1960-10-12',
'CountryOfBirth' => 'FR',
'Nationality' => 'FR',
'center' => 'centerA',
'maritalStatus' => 'ms_divorce'
),
array(
'maritalStatus' => 'ms_divorce',
],
[
//to have a person with same birthdate of Gérard Depardieu
'LastName' => 'Van Snick',
'FirstName' => 'Bart',
'Birthdate' => '1948-12-27',
'center' => 'centerA',
'maritalStatus' => 'ms_legalco'
),
array(
'maritalStatus' => 'ms_legalco',
],
[
//to have a woman with Depardieu as FirstName
'LastName' => 'Depardieu',
'FirstName' => 'Charline',
'Gender' => Person::FEMALE_GENDER,
'center' => 'centerA',
'maritalStatus' => 'ms_legalco'
),
array(
'maritalStatus' => 'ms_legalco',
],
[
//to have a special character in lastName
'LastName' => 'Manço',
'FirstName' => 'Étienne',
'center' => 'centerA',
'maritalStatus' => 'ms_unknown'
),
array(
'maritalStatus' => 'ms_unknown',
],
[
//to have true duplicate person
'LastName' => "Depardieu",
'FirstName' => "Jean",
'Birthdate' => "1960-10-12",
'LastName' => 'Depardieu',
'FirstName' => 'Jean',
'Birthdate' => '1960-10-12',
'CountryOfBirth' => 'FR',
'Nationality' => 'FR',
'center' => 'centerA',
'maritalStatus' => 'ms_divorce'
),
array(
'maritalStatus' => 'ms_divorce',
],
[
//to have false duplicate person
'LastName' => "Depardieu",
'FirstName' => "Jeanne",
'Birthdate' => "1966-11-13",
'LastName' => 'Depardieu',
'FirstName' => 'Jeanne',
'Birthdate' => '1966-11-13',
'CountryOfBirth' => 'FR',
'Nationality' => 'FR',
'center' => 'centerA',
'maritalStatus' => 'ms_legalco'
),
);
'maritalStatus' => 'ms_legalco',
],
];
private $years = [];
public function __construct()
{
$this->faker = \Faker\Factory::create('fr_FR');
}
public function getOrder()
{
return 10000;
}
public function load(ObjectManager $manager)
{
$this->loadRandPeople($manager);
$this->loadExpectedPeople($manager);
$manager->flush();
}
public function loadExpectedPeople(ObjectManager $manager)
{
echo "loading expected people...\n";
foreach ($this->peoples as $person) {
$this->addAPerson($this->fillWithDefault($person), $manager);
}
}
public function loadRandPeople(ObjectManager $manager)
{
echo "loading rand people...\n";
$this->prepare();
$chooseLastNameOrTri = ['tri', 'tri', 'name', 'tri'];
$i = 0;
do {
++$i;
$sex = $this->genders[array_rand($this->genders)];
if ('tri' === $chooseLastNameOrTri[array_rand($chooseLastNameOrTri)]) {
$length = mt_rand(2, 3);
$lastName = '';
for ($j = 0; $j <= $length; ++$j) {
$lastName .= $this->lastNamesTrigrams[array_rand($this->lastNamesTrigrams)];
}
$lastName = ucfirst($lastName);
} else {
$lastName = $this->lastNames[array_rand($this->lastNames)];
}
if (Person::MALE_GENDER === $sex) {
$firstName = $this->firstNamesMale[array_rand($this->firstNamesMale)];
} else {
$firstName = $this->firstNamesFemale[array_rand($this->firstNamesFemale)];
}
// add an address on 80% of the created people
if (mt_rand(0, 100) < 80) {
$address = $this->getRandomAddress();
// on 30% of those person, add multiple addresses
if (mt_rand(0, 10) < 4) {
$address = [
$address,
$this->getRandomAddress(),
];
}
} else {
$address = null;
}
$person = [
'FirstName' => $firstName,
'LastName' => $lastName,
'Gender' => $sex,
'Nationality' => (mt_rand(0, 100) > 50) ? null : 'BE',
'center' => (mt_rand(0, 1) === 0) ? 'centerA' : 'centerB',
'Address' => $address,
'maritalStatus' => $this->maritalStatusRef[array_rand($this->maritalStatusRef)],
];
$this->addAPerson($this->fillWithDefault($person), $manager);
} while (100 >= $i);
}
public function prepare()
{
//prepare days, month, years
$y = 1950;
do {
$this->years[] = $y;
$y = $y + 1;
} while (1990 <= $y);
$m = 1;
do {
$this->month[] = $m;
$m = $m + 1;
} while (12 <= $m);
$d = 1;
do {
$this->day[] = $d;
$d = $d + 1;
} while (28 >= $d);
}
private function addAccompanyingPeriods(Person $person, array $periods, ObjectManager $manager)
{
foreach ($periods as $period) {
echo "adding new past Accompanying Period..\n";
/** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = new AccompanyingPeriod(new \DateTime($period['from']));
$accompanyingPeriod = new AccompanyingPeriod(new DateTime($period['from']));
$accompanyingPeriod
->setClosingDate(new \DateTime($period['to']))
->setRemark($period['remark'])
;
->setClosingDate(new DateTime($period['to']))
->setRemark($period['remark']);
$person->addAccompanyingPeriod($accompanyingPeriod);
}
}
/**
* create a new person from array data.
*
* @throws Exception
*/
private function addAPerson(array $person, ObjectManager $manager)
{
$p = new Person();
foreach ($person as $key => $value) {
switch ($key) {
case 'CountryOfBirth':
case 'Nationality':
$value = $this->getCountry($value);
break;
case 'Birthdate':
$value = new DateTime($value);
break;
case 'center':
case 'maritalStatus':
$value = $this->getReference($value);
break;
case 'accompanyingPeriods':
$this->addAccompanyingPeriods($p, $value, $manager);
break;
}
//try to add the data using the setSomething function,
// if not possible, fallback to addSomething function
if (method_exists($p, 'set' . $key)) {
call_user_func([$p, 'set' . $key], $value);
} elseif (method_exists($p, 'add' . $key)) {
// if we have a "addSomething", we may have multiple items to add
// so, we set the value in an array if it is not an array, and
// will call the function addSomething multiple times
if (!is_array($value)) {
$value = [$value];
}
foreach ($value as $v) {
if (null !== $v) {
call_user_func([$p, 'add' . $key], $v);
}
}
}
}
$manager->persist($p);
echo "add person'" . $p->__toString() . "'\n";
}
/**
* fill a person array with default value.
*
* @param string[] $specific
*/
private function fillWithDefault(array $specific)
{
return array_merge([
'Birthdate' => '1960-10-12',
'PlaceOfBirth' => 'Ottignies Louvain-La-Neuve',
'Gender' => Person::MALE_GENDER,
'Email' => "Email d'un ami: roger@tt.com",
'CountryOfBirth' => 'BE',
'Nationality' => 'BE',
'CFData' => [],
'Address' => null,
], $specific);
}
private function getCountry($countryCode)
{
if (null === $countryCode) {
return null;
}
return $this->container->get('doctrine.orm.entity_manager')
->getRepository('ChillMainBundle:Country')
->findOneByCountryCode($countryCode);
}
/**
* Creata a random address.
*
* @return Address
*/
private function getRandomAddress()
{
return (new Address())
->setStreetAddress1($this->faker->streetAddress)
->setStreetAddress2(
mt_rand(0, 9) > 5 ? $this->faker->streetAddress : ''
)
->setPostcode($this->getReference(
LoadPostalCodes::$refs[array_rand(LoadPostalCodes::$refs)]
))
->setValidFrom($this->faker->dateTimeBetween('-5 years'));
}
}

View File

@@ -1,36 +1,28 @@
<?php
/*
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
use Chill\MainBundle\Entity\RoleScope;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
/**
* Add a role CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE for all groups except administrative,
* and a role CHILL_PERSON_SEE for administrative
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* and a role CHILL_PERSON_SEE for administrative.
*/
class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface
{
@@ -39,12 +31,11 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface
return 9600;
}
public function load(ObjectManager $manager)
{
foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) {
$permissionsGroup = $this->getReference($permissionsGroupRef);
//create permission group
switch ($permissionsGroup->getName()) {
case 'social':
@@ -52,13 +43,13 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface
printf("Adding CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE to %s permission group \n", $permissionsGroup->getName());
$roleScopeUpdate = (new RoleScope())
->setRole('CHILL_PERSON_UPDATE')
->setScope(null);
->setRole('CHILL_PERSON_UPDATE')
->setScope(null);
$permissionsGroup->addRoleScope($roleScopeUpdate);
$roleScopeCreate = (new RoleScope())
->setRole('CHILL_PERSON_CREATE')
->setScope(null);
->setRole('CHILL_PERSON_CREATE')
->setScope(null);
$permissionsGroup->addRoleScope($roleScopeCreate);
$roleScopeDuplicate = (new RoleScope())
@@ -67,33 +58,32 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface
$permissionsGroup->addRoleScope($roleScopeDuplicate);
$roleScopeList = (new RoleScope())
->setRole(PersonVoter::LISTS)
->setScope(null);
->setRole(PersonVoter::LISTS)
->setScope(null);
$permissionsGroup->addRoleScope($roleScopeList);
$roleScopeStats = (new RoleScope())
->setRole(PersonVoter::STATS)
->setScope(null);
->setRole(PersonVoter::STATS)
->setScope(null);
$permissionsGroup->addRoleScope($roleScopeStats);
$manager->persist($roleScopeUpdate);
$manager->persist($roleScopeCreate);
$manager->persist($roleScopeDuplicate);
break;
case 'administrative':
printf("Adding CHILL_PERSON_SEE to %s permission group \n", $permissionsGroup->getName());
$roleScopeSee = (new RoleScope())
->setRole('CHILL_PERSON_SEE')
->setScope(null);
->setRole('CHILL_PERSON_SEE')
->setScope(null);
$permissionsGroup->addRoleScope($roleScopeSee);
$manager->persist($roleScopeSee);
break;
}
}
$manager->flush();
}
}

View File

@@ -1,66 +1,64 @@
<?php
/*
* Copyright (C) 2014-2016 Julien Fastré <julien.fastre@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Chill\MainBundle\DependencyInjection\MissingBundleException;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\MainBundle\Security\Authorization\ChillExportVoter;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Exception;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use function array_key_exists;
/**
* Class ChillPersonExtension
* Loads and manages your bundle configuration
* Loads and manages your bundle configuration.
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
* @package Chill\PersonBundle\DependencyInjection
*/
class ChillPersonExtension extends Extension implements PrependExtensionInterface
{
/**
* {@inheritDoc}
* @param array $configs
* @param ContainerBuilder $container
* @throws \Exception
* {@inheritdoc}
*
* @throws Exception
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// set configuration for validation
$container->setParameter('chill_person.validation.birtdate_not_before',
$config['validation']['birthdate_not_after']);
$container->setParameter(
'chill_person.validation.birtdate_not_before',
$config['validation']['birthdate_not_after']
);
$this->handlePersonFieldsParameters($container, $config['person_fields']);
$this->handleAccompanyingPeriodsFieldsParameters($container, $config['accompanying_periods_fields']);
$container->setParameter('chill_person.allow_multiple_simultaneous_accompanying_periods',
$config['allow_multiple_simultaneous_accompanying_periods']);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
$container->setParameter(
'chill_person.allow_multiple_simultaneous_accompanying_periods',
$config['allow_multiple_simultaneous_accompanying_periods']
);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
$loader->load('services.yaml');
$loader->load('services/widgets.yaml');
$loader->load('services/exports.yaml');
@@ -75,84 +73,22 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/repository.yaml');
$loader->load('services/templating.yaml');
$loader->load('services/alt_names.yaml');
// load service advanced search only if configure
if ($config['search']['search_by_phone'] != 'never') {
if ('never' !== $config['search']['search_by_phone']) {
$loader->load('services/search_by_phone.yaml');
$container->setParameter('chill_person.search.search_by_phone',
$config['search']['search_by_phone']);
$container->setParameter(
'chill_person.search.search_by_phone',
$config['search']['search_by_phone']
);
}
if ($container->getParameter('chill_person.accompanying_period') !== 'hidden') {
$loader->load('services/exports_accompanying_period.yaml');
}
}
/**
* @param ContainerBuilder $container
* @param $config
*/
private function handlePersonFieldsParameters(ContainerBuilder $container, $config)
{
if (array_key_exists('enabled', $config)) {
unset($config['enabled']);
}
$container->setParameter('chill_person.person_fields', $config);
foreach ($config as $key => $value) {
switch($key) {
case 'accompanying_period':
$container->setParameter('chill_person.accompanying_period', $value);
break;
default:
$container->setParameter('chill_person.person_fields.'.$key, $value);
break;
}
}
}
/**
* @param ContainerBuilder $container
* @param $config
*/
private function handleAccompanyingPeriodsFieldsParameters(ContainerBuilder $container, $config)
{
$container->setParameter('chill_person.accompanying_period_fields', $config);
foreach ($config as $key => $value) {
switch($key) {
case 'enabled':
break;
default:
$container->setParameter('chill_person.accompanying_period_fields.'.$key, $value);
break;
}
}
}
/**
* @param ContainerBuilder $container
* @throws MissingBundleException
*/
private function declarePersonAsCustomizable (ContainerBuilder $container)
{
$bundles = $container->getParameter('kernel.bundles');
if (!isset($bundles['ChillCustomFieldsBundle'])) {
throw new MissingBundleException('ChillCustomFieldsBundle');
}
$container->prependExtensionConfig('chill_custom_fields',
array('customizables_entities' =>
array(
array('class' => 'Chill\PersonBundle\Entity\Person', 'name' => 'PersonEntity')
)
)
);
}
/**
* @param ContainerBuilder $container
* @throws MissingBundleException
*/
public function prepend(ContainerBuilder $container)
@@ -161,106 +97,35 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$this->prependHomepageWidget($container);
$this->prependDoctrineDQL($container);
$this->prependCruds($container);
//add person_fields parameter as global
$chillPersonConfig = $container->getExtensionConfig($this->getAlias());
$config = $this->processConfiguration(new Configuration(), $chillPersonConfig);
$twigConfig = array(
'globals' => array(
'chill_person' => array(
'fields' => $config['person_fields']
),
$twigConfig = [
'globals' => [
'chill_person' => [
'fields' => $config['person_fields'],
],
'chill_accompanying_periods' => [
'fields' => $config['accompanying_periods_fields']
]
),
'form_themes' => array('ChillPersonBundle:Export:ListPersonFormFields.html.twig')
);
'fields' => $config['accompanying_periods_fields'],
],
],
'form_themes' => ['ChillPersonBundle:Export:ListPersonFormFields.html.twig'],
];
$container->prependExtensionConfig('twig', $twigConfig);
$this-> declarePersonAsCustomizable($container);
$this->declarePersonAsCustomizable($container);
//declare routes for person bundle
$container->prependExtensionConfig('chill_main', array(
'routing' => array(
'resources' => array(
'@ChillPersonBundle/config/routes.yaml'
)
)
));
$container->prependExtensionConfig('chill_main', [
'routing' => [
'resources' => [
'@ChillPersonBundle/config/routes.yaml',
],
],
]);
}
/**
* Add a widget "add a person" on the homepage, automatically
*
* @param \Chill\PersonBundle\DependencyInjection\containerBuilder $container
*/
protected function prependHomepageWidget(containerBuilder $container)
{
$container->prependExtensionConfig('chill_main', array(
'widgets' => array(
'homepage' => array(
array(
'widget_alias' => 'add_person',
'order' => 2
)
)
)
));
}
/**
* Add role hierarchy.
*
* @param ContainerBuilder $container
*/
protected function prependRoleHierarchy(ContainerBuilder $container)
{
$container->prependExtensionConfig('security', array(
'role_hierarchy' => array(
'CHILL_PERSON_UPDATE' => array('CHILL_PERSON_SEE'),
'CHILL_PERSON_CREATE' => array('CHILL_PERSON_SEE'),
PersonVoter::LISTS => [ ChillExportVoter::EXPORT ],
PersonVoter::STATS => [ ChillExportVoter::EXPORT ]
)
));
}
/**
* Add DQL function linked with person
*
* @param ContainerBuilder $container
*/
protected function prependDoctrineDQL(ContainerBuilder $container)
{
//add DQL function to ORM (default entity_manager)
$container->prependExtensionConfig('doctrine', array(
'orm' => array(
'dql' => array(
'string_functions' => array(
'GET_PERSON_ADDRESS_ADDRESS_ID' => AddressPart\AddressPartAddressId::class,
'GET_PERSON_ADDRESS_STREET_ADDRESS_1' => AddressPart\AddressPartStreetAddress1::class,
'GET_PERSON_ADDRESS_STREET_ADDRESS_2' => AddressPart\AddressPartStreetAddress2::class,
'GET_PERSON_ADDRESS_VALID_FROM' => AddressPart\AddressPartValidFrom::class,
'GET_PERSON_ADDRESS_POSTCODE_LABEL' => AddressPart\AddressPartPostCodeLabel::class,
'GET_PERSON_ADDRESS_POSTCODE_CODE' => AddressPart\AddressPartPostCodeCode::class,
'GET_PERSON_ADDRESS_POSTCODE_ID' => AddressPart\AddressPartPostCodeId::class,
'GET_PERSON_ADDRESS_COUNTRY_NAME' => AddressPart\AddressPartCountryName::class,
'GET_PERSON_ADDRESS_COUNTRY_CODE' => AddressPart\AddressPartCountryCode::class,
'GET_PERSON_ADDRESS_COUNTRY_ID' => AddressPart\AddressPartCountryId::class,
),
'numeric_functions' => [
'GET_PERSON_ADDRESS_ISNOADDRESS' => AddressPart\AddressPartIsNoAddress::class,
]
)
)
));
}
/**
* @param ContainerBuilder $container
*/
protected function prependCruds(ContainerBuilder $container)
{
$container->prependExtensionConfig('chill_main', [
@@ -274,17 +139,17 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
'actions' => [
'index' => [
'template' => '@ChillPerson/ClosingMotive/index.html.twig',
'role' => 'ROLE_ADMIN'
'role' => 'ROLE_ADMIN',
],
'new' => [
'new' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillPerson/ClosingMotive/new.html.twig',
],
'edit' => [
'edit' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillPerson/ClosingMotive/edit.html.twig',
]
]
],
],
],
[
'class' => \Chill\PersonBundle\Entity\MaritalStatus::class,
@@ -297,17 +162,147 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
'role' => 'ROLE_ADMIN',
'template' => '@ChillPerson/MaritalStatus/index.html.twig',
],
'new' => [
'new' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillPerson/MaritalStatus/new.html.twig',
],
'edit' => [
'edit' => [
'role' => 'ROLE_ADMIN',
'template' => '@ChillPerson/MaritalStatus/edit.html.twig',
]
]
]
]
],
],
],
],
]);
}
/**
* Add DQL function linked with person.
*/
protected function prependDoctrineDQL(ContainerBuilder $container)
{
//add DQL function to ORM (default entity_manager)
$container->prependExtensionConfig('doctrine', [
'orm' => [
'dql' => [
'string_functions' => [
'GET_PERSON_ADDRESS_ADDRESS_ID' => AddressPart\AddressPartAddressId::class,
'GET_PERSON_ADDRESS_STREET_ADDRESS_1' => AddressPart\AddressPartStreetAddress1::class,
'GET_PERSON_ADDRESS_STREET_ADDRESS_2' => AddressPart\AddressPartStreetAddress2::class,
'GET_PERSON_ADDRESS_VALID_FROM' => AddressPart\AddressPartValidFrom::class,
'GET_PERSON_ADDRESS_POSTCODE_LABEL' => AddressPart\AddressPartPostCodeLabel::class,
'GET_PERSON_ADDRESS_POSTCODE_CODE' => AddressPart\AddressPartPostCodeCode::class,
'GET_PERSON_ADDRESS_POSTCODE_ID' => AddressPart\AddressPartPostCodeId::class,
'GET_PERSON_ADDRESS_COUNTRY_NAME' => AddressPart\AddressPartCountryName::class,
'GET_PERSON_ADDRESS_COUNTRY_CODE' => AddressPart\AddressPartCountryCode::class,
'GET_PERSON_ADDRESS_COUNTRY_ID' => AddressPart\AddressPartCountryId::class,
],
'numeric_functions' => [
'GET_PERSON_ADDRESS_ISNOADDRESS' => AddressPart\AddressPartIsNoAddress::class,
],
],
],
]);
}
/**
* Add a widget "add a person" on the homepage, automatically.
*
* @param \Chill\PersonBundle\DependencyInjection\containerBuilder $container
*/
protected function prependHomepageWidget(containerBuilder $container)
{
$container->prependExtensionConfig('chill_main', [
'widgets' => [
'homepage' => [
[
'widget_alias' => 'add_person',
'order' => 2,
],
],
],
]);
}
/**
* Add role hierarchy.
*/
protected function prependRoleHierarchy(ContainerBuilder $container)
{
$container->prependExtensionConfig('security', [
'role_hierarchy' => [
'CHILL_PERSON_UPDATE' => ['CHILL_PERSON_SEE'],
'CHILL_PERSON_CREATE' => ['CHILL_PERSON_SEE'],
PersonVoter::LISTS => [ChillExportVoter::EXPORT],
PersonVoter::STATS => [ChillExportVoter::EXPORT],
],
]);
}
/**
* @throws MissingBundleException
*/
private function declarePersonAsCustomizable(ContainerBuilder $container)
{
$bundles = $container->getParameter('kernel.bundles');
if (!isset($bundles['ChillCustomFieldsBundle'])) {
throw new MissingBundleException('ChillCustomFieldsBundle');
}
$container->prependExtensionConfig(
'chill_custom_fields',
['customizables_entities' => [
['class' => 'Chill\PersonBundle\Entity\Person', 'name' => 'PersonEntity'],
],
]
);
}
/**
* @param $config
*/
private function handleAccompanyingPeriodsFieldsParameters(ContainerBuilder $container, $config)
{
$container->setParameter('chill_person.accompanying_period_fields', $config);
foreach ($config as $key => $value) {
switch ($key) {
case 'enabled':
break;
default:
$container->setParameter('chill_person.accompanying_period_fields.' . $key, $value);
break;
}
}
}
/**
* @param $config
*/
private function handlePersonFieldsParameters(ContainerBuilder $container, $config)
{
if (array_key_exists('enabled', $config)) {
unset($config['enabled']);
}
$container->setParameter('chill_person.person_fields', $config);
foreach ($config as $key => $value) {
switch ($key) {
case 'accompanying_period':
$container->setParameter('chill_person.accompanying_period', $value);
break;
default:
$container->setParameter('chill_person.person_fields.' . $key, $value);
break;
}
}
}
}

View File

@@ -1,29 +1,26 @@
<?php
/*
* Copyright (C) 2018 Champs-Libres <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/>.
*/
namespace Chill\PersonBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Remove services which add AccompanyingPeriod to timeline if
* accompanying_periods are set to `hidden`
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use function in_array;
/**
* Remove services which add AccompanyingPeriod to timeline if
* accompanying_periods are set to `hidden`.
*/
class AccompanyingPeriodTimelineCompilerPass implements CompilerPassInterface
{
@@ -33,34 +30,33 @@ class AccompanyingPeriodTimelineCompilerPass implements CompilerPassInterface
if ($container->getParameter('chill_person.accompanying_period') !== 'hidden') {
return;
}
$definitions = [
'chill.person.timeline.accompanying_period_opening',
'chill.person.timeline.accompanying_period_closing'
'chill.person.timeline.accompanying_period_closing',
];
foreach($definitions as $definition) {
foreach ($definitions as $definition) {
$container
->removeDefinition($definition)
;
->removeDefinition($definition);
}
$definition = $container->getDefinition('chill.main.timeline_builder');
// we have to remove all methods call, and re-add them if not linked
// we have to remove all methods call, and re-add them if not linked
// to this service
$calls = $definition->getMethodCalls();
foreach($calls as list($method, $arguments)) {
if ($method !== 'addProvider') {
foreach ($calls as [$method, $arguments]) {
if ('addProvider' !== $method) {
continue;
}
$definition->removeMethodCall('addProvider');
if (FALSE === \in_array($arguments[1], $definitions)) {
if (false === in_array($arguments[1], $definitions, true)) {
$definition->addMethodCall($method, $arguments);
}
}
}
}
}

View File

@@ -1,23 +1,36 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\DependencyInjection;
use DateInterval;
use Exception;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
* This is the class that validates and merges configuration from your app/config files.
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
private $validationBirthdateNotAfterInfos = 'The period before today during which'
. ' any birthdate is not allowed. The birthdate is expressed as ISO8601 : '
. 'https://en.wikipedia.org/wiki/ISO_8601#Durations';
private $validationBirthdateNotAfterInfos = "The period before today during which"
. " any birthdate is not allowed. The birthdate is expressed as ISO8601 : "
. "https://en.wikipedia.org/wiki/ISO_8601#Durations";
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getConfigTreeBuilder()
{
@@ -25,123 +38,125 @@ class Configuration implements ConfigurationInterface
$rootNode = $treeBuilder->getRootNode('cl_chill_person');
$rootNode
->canBeDisabled()
->children()
->arrayNode('search')
->canBeDisabled()
->children()
->enumNode('search_by_phone')
->values(['always', 'on-domain', 'never'])
->defaultValue('on-domain')
->info('enable search by phone. \'always\' show the result '
->canBeDisabled()
->children()
->arrayNode('search')
->canBeDisabled()
->children()
->enumNode('search_by_phone')
->values(['always', 'on-domain', 'never'])
->defaultValue('on-domain')
->info('enable search by phone. \'always\' show the result '
. 'on every result. \'on-domain\' will show the result '
. 'only if the domain is given in the search box. '
. '\'never\' disable this feature')
->end()
->end() //children for 'search', parent = array node 'search'
->end() // array 'search', parent = children of root
->arrayNode('validation')
->canBeDisabled()
->children()
->scalarNode('birthdate_not_after')
->info($this->validationBirthdateNotAfterInfos)
->defaultValue('P1D')
->validate()
->ifTrue(function($period) {
try {
$interval = new \DateInterval($period);
} catch (\Exception $ex) {
return true;
}
return false;
})
->thenInvalid('Invalid period for birthdate validation : "%s" '
->end()
->end() //children for 'search', parent = array node 'search'
->end() // array 'search', parent = children of root
->arrayNode('validation')
->canBeDisabled()
->children()
->scalarNode('birthdate_not_after')
->info($this->validationBirthdateNotAfterInfos)
->defaultValue('P1D')
->validate()
->ifTrue(static function ($period) {
try {
$interval = new DateInterval($period);
} catch (Exception $ex) {
return true;
}
return false;
})
->thenInvalid('Invalid period for birthdate validation : "%s" '
. 'The parameter should match duration as defined by ISO8601 : '
. 'https://en.wikipedia.org/wiki/ISO_8601#Durations')
->end() // birthdate_not_after, parent = children of validation
->end() // children for 'validation', parent = validation
->end() //validation, parent = children of root
->end() // children of root, parent = root
->arrayNode('person_fields')
->canBeDisabled()
->children()
->append($this->addFieldNode('place_of_birth'))
->append($this->addFieldNode('email'))
->append($this->addFieldNode('phonenumber'))
->append($this->addFieldNode('mobilenumber'))
->append($this->addFieldNode('contact_info'))
->append($this->addFieldNode('nationality'))
->append($this->addFieldNode('country_of_birth'))
->append($this->addFieldNode('marital_status'))
->append($this->addFieldNode('spoken_languages'))
->append($this->addFieldNode('address'))
->append($this->addFieldNode('accompanying_period'))
->append($this->addFieldNode('memo'))
->arrayNode('alt_names')
->defaultValue([])
->arrayPrototype()
->children()
->scalarNode('key')
->isRequired()->cannotBeEmpty()
->end()
->arrayNode('labels')
->children()
->scalarNode('lang')->isRequired()->cannotBeEmpty()
->example('fr')
->end()
->scalarNode('label')->isRequired()->cannotBeEmpty()
->example('Nom de jeune fille')
->end()
->end()
->end()
->end()
->end()
->end()
->end() //children for 'person_fields', parent = array 'person_fields'
->end() // person_fields, parent = children of root
->arrayNode('accompanying_periods_fields')
->canBeDisabled()
->children()
->append($this->addFieldNode('user'))
->append($this->addFieldNode('createdBy'))
->append($this->addFieldNode('step'))
->append($this->addFieldNode('origin'))
->append($this->addFieldNode('intensity'))
->append($this->addFieldNode('scopes'))
->append($this->addFieldNode('requestor'))
->append($this->addFieldNode('anonymous'))
->append($this->addFieldNode('emergency'))
->append($this->addFieldNode('confidential'))
->end() //children for 'accompanying_person_fields', parent = array 'person_fields'
->end() // paccompanying_person_fields, parent = children of root
->booleanNode('allow_multiple_simultaneous_accompanying_periods')
->info('Can we have more than one simultaneous accompanying period in the same time. Default false.')
->defaultValue(false)
->end()
->end() // children of 'root', parent = root
;
->end() // birthdate_not_after, parent = children of validation
->end() // children for 'validation', parent = validation
->end() //validation, parent = children of root
->end() // children of root, parent = root
->arrayNode('person_fields')
->canBeDisabled()
->children()
->append($this->addFieldNode('place_of_birth'))
->append($this->addFieldNode('email'))
->append($this->addFieldNode('phonenumber'))
->append($this->addFieldNode('mobilenumber'))
->append($this->addFieldNode('contact_info'))
->append($this->addFieldNode('nationality'))
->append($this->addFieldNode('country_of_birth'))
->append($this->addFieldNode('marital_status'))
->append($this->addFieldNode('spoken_languages'))
->append($this->addFieldNode('address'))
->append($this->addFieldNode('accompanying_period'))
->append($this->addFieldNode('memo'))
->arrayNode('alt_names')
->defaultValue([])
->arrayPrototype()
->children()
->scalarNode('key')
->isRequired()->cannotBeEmpty()
->end()
->arrayNode('labels')
->children()
->scalarNode('lang')->isRequired()->cannotBeEmpty()
->example('fr')
->end()
->scalarNode('label')->isRequired()->cannotBeEmpty()
->example('Nom de jeune fille')
->end()
->end()
->end()
->end()
->end()
->end()
->end() //children for 'person_fields', parent = array 'person_fields'
->end() // person_fields, parent = children of root
->arrayNode('accompanying_periods_fields')
->canBeDisabled()
->children()
->append($this->addFieldNode('user'))
->append($this->addFieldNode('createdBy'))
->append($this->addFieldNode('step'))
->append($this->addFieldNode('origin'))
->append($this->addFieldNode('intensity'))
->append($this->addFieldNode('scopes'))
->append($this->addFieldNode('requestor'))
->append($this->addFieldNode('anonymous'))
->append($this->addFieldNode('emergency'))
->append($this->addFieldNode('confidential'))
->end() //children for 'accompanying_person_fields', parent = array 'person_fields'
->end() // paccompanying_person_fields, parent = children of root
->booleanNode('allow_multiple_simultaneous_accompanying_periods')
->info('Can we have more than one simultaneous accompanying period in the same time. Default false.')
->defaultValue(false)
->end()
->end() // children of 'root', parent = root
;
return $treeBuilder;
}
private function addFieldNode($key)
{
$tree = new TreeBuilder($key,'enum');
$tree = new TreeBuilder($key, 'enum');
$node = $tree->getRootNode($key);
switch($key) {
switch ($key) {
case 'accompanying_period':
$info = "If the accompanying periods are shown";
$info = 'If the accompanying periods are shown';
break;
default:
$info = "If the field $key must be shown";
$info = "If the field {$key} must be shown";
break;
}
$node
->values(array('hidden', 'visible'))
->values(['hidden', 'visible'])
->defaultValue('visible')
->info($info)
->end();

View File

@@ -1,20 +1,16 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
@@ -23,70 +19,64 @@ use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
*
* USAGE GET_ADDRESS_<part>(person.id, :date, 'postcode') where part
* should be replace by the part of the address.
*
*
* This function return the current address part at the given date, for the
* given person (identified by his id)
*
* The aim of this function is to be used within reports
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* The aim of this function is to be used within reports
*/
abstract class AddressPart extends FunctionNode
{
public $fields = array(
'address_id',
'streetaddress1',
'streetaddress2',
'validfrom',
'postcode_label',
'postcode_code',
'postcode_id',
'country_name',
'country_code',
'country_id'
);
public $fields = [
'address_id',
'streetaddress1',
'streetaddress2',
'validfrom',
'postcode_label',
'postcode_code',
'postcode_id',
'country_name',
'country_code',
'country_id',
];
/**
*
* @var \Doctrine\ORM\Query\AST\Node
*/
private $pid;
/**
*
* @var \Doctrine\ORM\Query\AST\Node
*/
private $date;
/**
*
* @var \Doctrine\ORM\Query\AST\Node
*/
private $part;
/**
* return the part of the address
*
* Should be one value of the "public" amongst
* 'address_id', 'streetaddress1',
* 'streetaddress2', 'validfrom', 'postcode_label', 'postcode_code',
* @var \Doctrine\ORM\Query\AST\Node
*/
private $pid;
/**
* return the part of the address.
*
* Should be one value of the "public" amongst
* 'address_id', 'streetaddress1',
* 'streetaddress2', 'validfrom', 'postcode_label', 'postcode_code',
* 'postcode_id', 'country_name', 'country_code', 'country_id', 'isnoaddress'
*
*
* @return string
*/
abstract public function getPart();
public function getSql(SqlWalker $sqlWalker)
{
return sprintf(
'get_last_address_%s(%s, %s)',
'get_last_address_%s(%s, %s)',
$this->getPart(),
$this->pid->dispatch($sqlWalker),
$this->date->dispatch($sqlWalker)
);
);
}
public function parse(Parser $parser)

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartAddressId extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartCountryCode extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartCountryId extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartCountryName extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartIsNoAddress extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartPostCodeCode extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartPostCodeId extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartPostCodeLabel extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartStreetAddress1 extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartStreetAddress2 extends AddressPart
{
public function getPart()

View File

@@ -1,29 +1,20 @@
<?php
/*
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AddressPartValidFrom extends AddressPart
{
public function getPart()

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +1,51 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* ClosingMotive give an explanation why we closed the Accompanying period
* ClosingMotive give an explanation why we closed the Accompanying period.
*
* @ORM\Entity(
* repositoryClass="Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepository")
* repositoryClass="Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepository")
* @ORM\Table(name="chill_person_accompanying_period_closingmotive")
*/
class ClosingMotive
{
/**
* @var integer
* @var bool
*
* @ORM\Column(type="boolean")
*/
private $active = true;
/**
* Child Accompanying periods.
*
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive",
* mappedBy="parent")
*/
private $children;
/**
* @var int
*
* @ORM\Id
* @ORM\Column(name="id", type="integer")
@@ -50,41 +59,23 @@ class ClosingMotive
* @ORM\Column(type="json")
*/
private $name;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $active = true;
/**
* @var self
*
* @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive",
* inversedBy="children")
*/
private $parent = null;
/**
* Child Accompanying periods
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive",
* mappedBy="parent")
*/
private $children;
/**
* @var float
*
* @ORM\Column(type="float")
*/
private $ordering = 0.0;
/**
* @var self
*
* @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive",
* inversedBy="children")
*/
private $parent;
/**
* ClosingMotive constructor.
*/
@@ -92,11 +83,28 @@ class ClosingMotive
{
$this->children = new ArrayCollection();
}
public function addChildren(ClosingMotive $child): ClosingMotive
{
if ($this->children->contains($child)) {
return $this;
}
$this->children->add($child);
$child->setParent($this);
return $this;
}
public function getChildren(): Collection
{
return $this->children;
}
/**
* Get id
* Get id.
*
* @return integer
* @return int
*/
public function getId()
{
@@ -104,7 +112,87 @@ class ClosingMotive
}
/**
* Set name
* Get name.
*
* @return array
*/
public function getName()
{
return $this->name;
}
public function getOrdering(): float
{
return $this->ordering;
}
/**
* @return ClosingMotive
*/
public function getParent()
{
return $this->parent;
}
public function hasParent(): bool
{
return null !== $this->parent;
}
public function isActive(): bool
{
return $this->active;
}
public function isChild(): bool
{
return null !== $this->parent;
}
public function isLeaf(): bool
{
return $this->children->count() === 0;
}
public function isParent(): bool
{
return $this->children->count() > 0;
}
public function removeChildren(ClosingMotive $child): ClosingMotive
{
if ($this->children->removeElement($child)) {
$child->setParent(null);
}
return $this;
}
/**
* @return $this
*/
public function setActive(bool $active)
{
$this->active = $active;
if (false === $this->active) {
foreach ($this->getChildren() as $child) {
$child->setActive(false);
}
}
return $this;
}
public function setChildren(Collection $children): ClosingMotive
{
$this->children = $children;
return $this;
}
/**
* Set name.
*
* @param array $name
*
@@ -118,161 +206,23 @@ class ClosingMotive
}
/**
* Get name
*
* @return array
*/
public function getName()
{
return $this->name;
}
/**
* @return bool
*/
public function isActive(): bool
{
return $this->active;
}
/**
* @param bool $active
* @return $this
*/
public function setActive(bool $active)
{
$this->active = $active;
if ($this->active === FALSE) {
foreach ($this->getChildren() as $child) {
$child->setActive(FALSE);
}
}
return $this;
}
/**
* @return ClosingMotive
*/
public function getParent()
{
return $this->parent;
}
/**
* @return Collection
*/
public function getChildren(): Collection
{
return $this->children;
}
/**
* @param ClosingMotive|null $parent
* @return ClosingMotive
*/
public function setParent(?ClosingMotive $parent): ClosingMotive
{
$this->parent = $parent;
if (NULL !== $parent) {
//$parent->addChildren($this);
}
return $this;
}
/**
* @param Collection $children
* @return ClosingMotive
*/
public function setChildren(Collection $children): ClosingMotive
{
$this->children = $children;
return $this;
}
/**
* @param ClosingMotive $child
* @return ClosingMotive
*/
public function addChildren(ClosingMotive $child): ClosingMotive
{
if ($this->children->contains($child)) {
return $this;
}
$this->children->add($child);
$child->setParent($this);
return $this;
}
/**
* @param ClosingMotive $child
* @return ClosingMotive
*/
public function removeChildren(ClosingMotive $child): ClosingMotive
{
if ($this->children->removeElement($child)) {
$child->setParent(null);
}
return $this;
}
/**
* @return float
*/
public function getOrdering(): float
{
return $this->ordering;
}
/**
* @param float $ordering
* @return $this
*/
public function setOrdering(float $ordering)
{
$this->ordering = $ordering;
return $this;
}
/**
* @return bool
*/
public function isChild(): bool
{
return $this->parent !== null;
}
/**
* @return bool
*/
public function isParent(): bool
{
return $this->children->count() > 0;
}
/**
* @return bool
*/
public function isLeaf(): bool
{
return $this->children->count() === 0;
}
/**
* @return bool
*/
public function hasParent(): bool
{
return $this->parent !== null;
}
public function setParent(?ClosingMotive $parent): ClosingMotive
{
$this->parent = $parent;
if (null !== $parent) {
//$parent->addChildren($this);
}
return $this;
}
}

View File

@@ -1,30 +1,22 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Repository\AccompanyingPeriod\CommentRepository;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Repository\AccompanyingPeriod\CommentRepository;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
/**
@@ -33,21 +25,24 @@ use Doctrine\ORM\Mapping as ORM;
*/
class Comment
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod",
* inversedBy="comments")
* inversedBy="comments")
* @ORM\JoinColumn(nullable=false)
*/
private $accompanyingPeriod;
/**
* @ORM\Column(type="text")
*/
private $content;
/**
* @ORM\Column(type="datetime")
*/
private $createdAt;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=false)
@@ -55,9 +50,11 @@ class Comment
private $creator;
/**
* @ORM\Column(type="datetime")
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $createdAt;
private $id;
/**
* @ORM\Column(type="datetime")
@@ -70,19 +67,39 @@ class Comment
*/
private $updatedBy;
/**
* @ORM\Column(type="text")
*/
private $content;
public function getAccompanyingPeriod(): ?AccompanyingPeriod
{
return $this->accompanyingPeriod;
}
public function getContent(): ?string
{
return $this->content;
}
public function getCreatedAt(): ?DateTimeInterface
{
return $this->createdAt;
}
public function getCreator(): ?User
{
return $this->creator;
}
public function getId(): ?int
{
return $this->id;
}
public function getAccompanyingPeriod(): ?AccompanyingPeriod
public function getUpdatedAt(): ?DateTimeInterface
{
return $this->accompanyingPeriod;
return $this->updatedAt;
}
public function getUpdatedBy(): ?User
{
return $this->updatedBy;
}
public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self
@@ -92,9 +109,18 @@ class Comment
return $this;
}
public function getCreator(): ?User
public function setContent(string $content): self
{
return $this->creator;
$this->content = $content;
return $this;
}
public function setCreatedAt(DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function setCreator(?User $creator): self
@@ -104,51 +130,17 @@ class Comment
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
public function setUpdatedAt(DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getUpdatedBy(): ?User
{
return $this->updatedBy;
}
public function setUpdatedBy(?User $updatedBy): self
{
$this->updatedBy = $updatedBy;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
}

View File

@@ -1,28 +1,19 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\OriginRepository;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
/**
@@ -58,6 +49,11 @@ class Origin
return $this->label;
}
public function getNoActiveAfter(): ?DateTimeImmutable
{
return $this->noActiveAfter;
}
public function setLabel(string $label): self
{
$this->label = $label;
@@ -65,12 +61,7 @@ class Origin
return $this;
}
public function getNoActiveAfter(): ?\DateTimeImmutable
{
return $this->noActiveAfter;
}
public function setNoActiveAfter(?\DateTimeImmutable $noActiveAfter): self
public function setNoActiveAfter(?DateTimeImmutable $noActiveAfter): self
{
$this->noActiveAfter = $noActiveAfter;

View File

@@ -1,31 +1,21 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Repository\AccompanyingPeriod\ResourceRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\AccompanyingPeriod\ResourceRepository;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\ORM\Mapping as ORM;
@@ -35,13 +25,6 @@ use Doctrine\ORM\Mapping as ORM;
*/
class Resource
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod",
@@ -52,10 +35,17 @@ class Resource
private $accompanyingPeriod;
/**
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
* @ORM\ManyToOne(targetEntity=Comment::class)
* @ORM\JoinColumn(nullable=true)
*/
private $thirdParty;
private $comment;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=Person::class)
@@ -64,19 +54,42 @@ class Resource
private $person;
/**
* @ORM\ManyToOne(targetEntity=Comment::class)
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
* @ORM\JoinColumn(nullable=true)
*/
private $comment;
private $thirdParty;
public function getAccompanyingPeriod(): ?AccompanyingPeriod
{
return $this->accompanyingPeriod;
}
public function getComment(): ?Comment
{
return $this->comment;
}
public function getId(): ?int
{
return $this->id;
}
public function getAccompanyingPeriod(): ?AccompanyingPeriod
public function getPerson(): ?Person
{
return $this->accompanyingPeriod;
return $this->person;
}
/**
* @return Person|ThirdParty
*/
public function getResource()
{
return $this->person ?? $this->thirdParty;
}
public function getThirdParty(): ?ThirdParty
{
return $this->thirdParty;
}
public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self
@@ -86,23 +99,13 @@ class Resource
return $this;
}
public function getThirdParty(): ?ThirdParty
public function setComment(?Comment $comment): self
{
return $this->thirdParty;
}
public function setThirdParty(?ThirdParty $thirdParty): self
{
$this->thirdParty = $thirdParty;
$this->comment = $comment;
return $this;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
@@ -110,23 +113,10 @@ class Resource
return $this;
}
public function getComment(): ?Comment
public function setThirdParty(?ThirdParty $thirdParty): self
{
return $this->comment;
}
public function setComment(?Comment $comment): self
{
$this->comment = $comment;
$this->thirdParty = $thirdParty;
return $this;
}
/**
* @return Person|ThirdParty
*/
public function getResource()
{
return $this->person ?? $this->thirdParty;
}
}

View File

@@ -1,124 +1,114 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity;
use Chill\PersonBundle\Repository\AccompanyingPeriodParticipationRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* AccompanyingPeriodParticipation Class
* AccompanyingPeriodParticipation Class.
*
* @package Chill\PersonBundle\Entity
* @ORM\Entity(repositoryClass=AccompanyingPeriodParticipationRepository::class)
* @ORM\Table(name="chill_person_accompanying_period_participation")
*/
class AccompanyingPeriodParticipation
{
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class, inversedBy="participations", cascade={"persist"})
* @ORM\JoinColumn(name="accompanyingperiod_id", referencedColumnName="id", nullable=false)
*/
private $accompanyingPeriod;
/**
* @ORM\Column(type="date", nullable=true)
*/
private $endDate;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="accompanyingPeriodParticipations")
* @ORM\JoinColumn(name="person_id", referencedColumnName="id", nullable=false)
*/
private $person;
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class, inversedBy="participations", cascade={"persist"})
* @ORM\JoinColumn(name="accompanyingperiod_id", referencedColumnName="id", nullable=false)
*/
private $accompanyingPeriod;
/**
* @ORM\Column(type="date", nullable=false)
*/
private $startDate;
/**
* @ORM\Column(type="date", nullable=true)
*/
private $endDate = null;
public function __construct(AccompanyingPeriod $accompanyingPeriod, Person $person)
{
$this->startDate = new \DateTimeImmutable('now');
$this->startDate = new DateTimeImmutable('now');
$this->accompanyingPeriod = $accompanyingPeriod;
$this->person = $person;
}
public function getId(): ?int
{
return $this->id;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
public function getAccompanyingPeriod(): ?AccompanyingPeriod
{
return $this->accompanyingPeriod;
}
public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self
{
$this->accompanyingPeriod = $accompanyingPeriod;
return $this;
}
public function getStartDate(): ?\DateTimeInterface
{
return $this->startDate;
}
/*
* public function setStartDate(\DateTimeInterface $startDate): self { $this->startDate = $startDate; return $this; }
*/
public function getEndDate(): ?\DateTimeInterface
public function getEndDate(): ?DateTimeInterface
{
return $this->endDate;
}
public function setEndDate(?\DateTimeInterface $endDate): self
public function getId(): ?int
{
return $this->id;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function getStartDate(): ?DateTimeInterface
{
return $this->startDate;
}
public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self
{
$this->accompanyingPeriod = $accompanyingPeriod;
return $this;
}
public function setEndDate(?DateTimeInterface $endDate): self
{
$this->endDate = $endDate;
return $this;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
}

View File

@@ -1,34 +1,24 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2019, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <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/>.
*/
namespace Chill\PersonBundle\Entity;
use Chill\PersonBundle\Entity\Person;
/**
* Interface which applies to entities which are associated to a single person
*
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity;
/**
* Interface which applies to entities which are associated to a single person.
*/
interface HasPerson
{
public function setPerson(Person $person = null): HasPerson;
public function getPerson(): ?Person;
public function setPerson(?Person $person = null): HasPerson;
}

View File

@@ -1,40 +1,33 @@
<?php
/*
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* MaritalStatus
* MaritalStatus.
*
* @ORM\Entity()
* @ORM\Entity
* @ORM\Table(name="chill_person_marital_status")
* @ORM\HasLifecycleCallbacks()
* @ORM\HasLifecycleCallbacks
*/
class MaritalStatus
{
/**
* @var string
*
* @ORM\Id()
* @ORM\Id
* @ORM\Column(type="string", length=7)
*/
private $id;
@@ -46,9 +39,9 @@ class MaritalStatus
private $name;
/**
* Get id
* Get id.
*
* @return string
* @return string
*/
public function getId()
{
@@ -56,37 +49,40 @@ class MaritalStatus
}
/**
* Set id
*
* Get name.
*
* @return string array
*/
public function getName()
{
return $this->name;
}
/**
* Set id.
*
* @param string $id
*
* @return MaritalStatus
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Set name
* Set name.
*
* @param string array $name
*
* @return MaritalStatus
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string array
*/
public function getName()
{
return $this->name;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,22 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* PersonAltName
* PersonAltName.
*
* @ORM\Table(name="chill_person_alt_name")
* @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonAltNameRepository")
@@ -13,7 +24,7 @@ use Doctrine\ORM\Mapping as ORM;
class PersonAltName
{
/**
* @var integer
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
@@ -34,18 +45,17 @@ class PersonAltName
* @ORM\Column(name="label", type="text")
*/
private $label;
/**
* @var Person
*
* @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\Person",
* inversedBy="altNames"
* targetEntity="Chill\PersonBundle\Entity\Person",
* inversedBy="altNames"
* )
*/
private $person;
/**
* Get id.
*
@@ -56,6 +66,31 @@ class PersonAltName
return $this->id;
}
/**
* Get key.
*
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* Get label.
*
* @return string
*/
public function getLabel()
{
return $this->label;
}
public function getPerson(): Person
{
return $this->person;
}
/**
* Set key.
*
@@ -70,16 +105,6 @@ class PersonAltName
return $this;
}
/**
* Get key.
*
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* Set label.
*
@@ -95,31 +120,12 @@ class PersonAltName
}
/**
* Get label.
*
* @return string
*/
public function getLabel()
{
return $this->label;
}
/**
* @return Person
*/
public function getPerson(): Person
{
return $this->person;
}
/**
* @param Person|null $person
* @return $this
*/
public function setPerson(?Person $person = null)
{
$this->person = $person;
return $this;
}
}

View File

@@ -1,12 +1,24 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Chill\MainBundle\Entity\User;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
/**
* PersonNotDuplicate
* PersonNotDuplicate.
*
* @ORM\Table(name="chill_person_not_duplicate")
* @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonNotDuplicateRepository")
@@ -14,8 +26,15 @@ use Chill\MainBundle\Entity\User;
class PersonNotDuplicate
{
/**
* The person's id
* @var integer
* @var DateTime
* @ORM\Column(type="datetime")
*/
private $date;
/**
* The person's id.
*
* @var int
*
* @ORM\Id
* @ORM\Column(name="id", type="integer")
@@ -37,12 +56,6 @@ class PersonNotDuplicate
*/
private $person2;
/**
* @var \DateTime
* @ORM\Column(type="datetime")
*/
private $date;
/**
* @var User
*
@@ -52,37 +65,7 @@ class PersonNotDuplicate
public function __construct()
{
$this->date = new \DateTime();
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getPerson1()
{
return $this->person1;
}
public function setPerson1(Person $person1)
{
$this->person1 = $person1;
}
public function getPerson2()
{
return $this->person2;
}
public function setPerson2(Person $person2)
{
$this->person2 = $person2;
$this->date = new DateTime();
}
public function getDate()
@@ -90,9 +73,19 @@ class PersonNotDuplicate
return $this->date;
}
public function setDate(\DateTime $date)
public function getId()
{
$this->date = $date;
return $this->id;
}
public function getPerson1()
{
return $this->person1;
}
public function getPerson2()
{
return $this->person2;
}
public function getUser()
@@ -100,6 +93,26 @@ class PersonNotDuplicate
return $this->user;
}
public function setDate(DateTime $date)
{
$this->date = $date;
}
public function setId($id)
{
$this->id = $id;
}
public function setPerson1(Person $person1)
{
$this->person1 = $person1;
}
public function setPerson2(Person $person2)
{
$this->person2 = $person2;
}
public function setUser(User $user)
{
$this->user = $user;

View File

@@ -1,18 +1,39 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Entity;
use Chill\PersonBundle\Entity\Person;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
/**
* Person Phones
* Person Phones.
*
* @ORM\Entity()
* @ORM\Entity
* @ORM\Table(name="chill_person_phone")
*/
class PersonPhone
{
/**
* @ORM\Column(type="datetime", nullable=false)
*/
private DateTime $date;
/**
* @ORM\Column(type="text", nullable=true)
*/
private ?string $description = null;
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
@@ -22,35 +43,35 @@ class PersonPhone
/**
* @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\Person",
* inversedBy="otherPhoneNumbers"
* targetEntity="Chill\PersonBundle\Entity\Person",
* inversedBy="otherPhoneNumbers"
* )
*/
private Person $person;
/**
* @ORM\Column(type="text", length=40, nullable=true)
*/
private ?string $type;
/**
* @ORM\Column(type="text", length=40, nullable=false)
*/
private string $phonenumber = '';
/**
* @ORM\Column(type="text", nullable=true)
* @ORM\Column(type="text", length=40, nullable=true)
*/
private ?string $description = null;
/**
* @ORM\Column(type="datetime", nullable=false)
*/
private \DateTime $date;
private ?string $type;
public function __construct()
{
$this->date = new \DateTime();
$this->date = new DateTime();
}
public function getDate(): DateTime
{
return $this->date;
}
public function getDescription(): ?string
{
return $this->description;
}
public function getId(): int
@@ -63,9 +84,9 @@ class PersonPhone
return $this->person;
}
public function setPerson(Person $person): void
public function getPhonenumber(): string
{
$this->person = $person;
return $this->phonenumber;
}
public function getType(): string
@@ -73,24 +94,14 @@ class PersonPhone
return $this->type;
}
public function setType(string $type): void
public function isEmpty(): bool
{
$this->type = $type;
return empty($this->getDescription()) && empty($this->getPhonenumber());
}
public function getPhonenumber(): string
public function setDate(DateTime $date): void
{
return $this->phonenumber;
}
public function setPhonenumber(string $phonenumber): void
{
$this->phonenumber = $phonenumber;
}
public function getDescription(): ?string
{
return $this->description;
$this->date = $date;
}
public function setDescription(?string $description): void
@@ -98,18 +109,18 @@ class PersonPhone
$this->description = $description;
}
public function getDate(): \DateTime
public function setPerson(Person $person): void
{
return $this->date;
$this->person = $person;
}
public function setDate(\DateTime $date): void
public function setPhonenumber(string $phonenumber): void
{
$this->date = $date;
$this->phonenumber = $phonenumber;
}
public function isEmpty(): bool
public function setType(string $type): void
{
return empty($this->getDescription()) && empty($this->getPhonenumber());
$this->type = $type;
}
}

View File

@@ -1,59 +1,49 @@
<?php
/*
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use function in_array;
/**
*
*
*/
class AbstractAccompanyingPeriodExportElement
{
/**
* Add the accompanying period alias to the query.
*
* @throws LogicException if the "person" alias is not present and attaching accompanying period is not possible
*/
protected function addJoinAccompanyingPeriod(QueryBuilder $query): void
{
if (false === $this->havingAccompanyingPeriodInJoin($query)) {
if (false === in_array('person', $query->getAllAliases(), true)) {
throw new LogicException("the alias 'person' does not exists in "
. 'query builder');
}
$query->join('person.accompanyingPeriods', 'accompanying_period');
}
}
/**
* Return true if "accompanying_period" alias is present in the query alises.
*
* @param QueryBuilder $query
* @return bool
*/
protected function havingAccompanyingPeriodInJoin(QueryBuilder $query): bool
{
$joins = $query->getDQLPart('join') ?? [];
return (\in_array('accompanying_period', $query->getAllAliases()));
}
/**
* Add the accompanying period alias to the query
*
* @param QueryBuilder $query
* @return void
* @throws \LogicException if the "person" alias is not present and attaching accompanying period is not possible
*/
protected function addJoinAccompanyingPeriod(QueryBuilder $query): void
{
if (FALSE === $this->havingAccompanyingPeriodInJoin($query)) {
if (FALSE === \in_array('person', $query->getAllAliases())) {
throw new \LogicException("the alias 'person' does not exists in "
. "query builder");
}
$query->join('person.accompanyingPeriods', 'accompanying_period');
}
return in_array('accompanying_period', $query->getAllAliases(), true);
}
}

View File

@@ -1,51 +1,39 @@
<?php
/*
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use DateTime;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Translation\TranslatorInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class AgeAggregator implements AggregatorInterface,
class AgeAggregator implements
AggregatorInterface,
ExportElementValidatedInterface
{
/**
*
* @var
* @var
*/
protected $translator;
public function __construct($translator)
{
$this->translator = $translator;
}
public function addRole()
{
return null;
@@ -65,48 +53,47 @@ class AgeAggregator implements AggregatorInterface,
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
{
$builder->add('date_age_calculation', DateType::class, array(
'label' => "Calculate age in relation to this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy'
));
}
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['date_age_calculation'] === null) {
$context->buildViolation("The date should not be empty")
->addViolation();
}
$builder->add('date_age_calculation', DateType::class, [
'label' => 'Calculate age in relation to this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]);
}
public function getLabels($key, array $values, $data)
{
return function($value) {
if ($value === '_header') {
return "Age";
return function ($value) {
if ('_header' === $value) {
return 'Age';
}
if ($value === NULL) {
return $this->translator->trans("without data");
if (null === $value) {
return $this->translator->trans('without data');
}
return $value;
};
}
public function getQueryKeys($data)
{
return array(
'person_age'
);
return [
'person_age',
];
}
public function getTitle()
{
return "Aggregate by age";
return 'Aggregate by age';
}
public function validateForm($data, ExecutionContextInterface $context)
{
if (null === $data['date_age_calculation']) {
$context->buildViolation('The date should not be empty')
->addViolation();
}
}
}

View File

@@ -1,106 +1,70 @@
<?php
/*
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\MainBundle\Util\CountriesInfo;
use Symfony\Component\Security\Core\Role\Role;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Util\CountriesInfo;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CountryOfBirthAggregator implements AggregatorInterface,
class CountryOfBirthAggregator implements
AggregatorInterface,
ExportElementValidatedInterface
{
/**
*
* @var EntityRepository
*/
protected $countriesRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
*
* @var TranslatorInterface
*/
protected $translator;
public function __construct(EntityRepository $countriesRepository,
TranslatableStringHelper $translatableStringHelper,
TranslatorInterface $translator)
{
public function __construct(
EntityRepository $countriesRepository,
TranslatableStringHelper $translatableStringHelper,
TranslatorInterface $translator
) {
$this->countriesRepository = $countriesRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->translator = $translator;
}
public function applyOn()
public function addRole()
{
return 'person';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('group_by_level', ChoiceType::class, array(
'choices' => array(
'Group by continents' => 'continent',
'Group by country' => 'country'
),
'expanded' => true,
'multiple' => false
));
}
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['group_by_level'] === null) {
$context->buildViolation("You should select an option")
->addViolation();
}
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
// add a clause in select part
if ($data['group_by_level'] === 'country') {
if ('country' === $data['group_by_level']) {
$qb->addSelect('countryOfBirth.countryCode as country_of_birth_aggregator');
} elseif ($data['group_by_level'] === 'continent') {
} elseif ('continent' === $data['group_by_level']) {
$clause = 'CASE '
. 'WHEN countryOfBirth.countryCode IN(:cob_africa_codes) THEN \'AF\' '
. 'WHEN countryOfBirth.countryCode IN(:cob_asia_codes) THEN \'AS\' '
@@ -113,24 +77,24 @@ class CountryOfBirthAggregator implements AggregatorInterface,
. 'END as country_of_birth_aggregator ';
$qb->addSelect($clause);
$params =
array(
[
'cob_africa_codes' => CountriesInfo::getCountriesCodeByContinent('AF'),
'cob_asia_codes' => CountriesInfo::getCountriesCodeByContinent('AS'),
'cob_europe_codes' => CountriesInfo::getCountriesCodeByContinent('EU'),
'cob_north_america_codes' => CountriesInfo::getCountriesCodeByContinent('NA'),
'cob_south_america_codes' => CountriesInfo::getCountriesCodeByContinent('SA'),
'cob_oceania_codes' => CountriesInfo::getCountriesCodeByContinent('OC'),
'cob_antartica_codes' => CountriesInfo::getCountriesCodeByContinent('AN')
);
'cob_antartica_codes' => CountriesInfo::getCountriesCodeByContinent('AN'),
];
foreach ($params as $k => $v) {
$qb->setParameter($k, $v);
}
} else {
throw new \LogicException("The group_by_level '".$data['group_by_level']
." is not known.");
throw new LogicException("The group_by_level '" . $data['group_by_level']
. ' is not known.');
}
$qb->leftJoin('person.countryOfBirth', 'countryOfBirth');
// add group by
@@ -141,46 +105,45 @@ class CountryOfBirthAggregator implements AggregatorInterface,
} else {
$qb->groupBy('country_of_birth_aggregator');
}
}
public function getTitle()
public function applyOn()
{
return "Group people by country of birth";
return 'person';
}
public function getQueryKeys($data)
public function buildForm(FormBuilderInterface $builder)
{
return array('country_of_birth_aggregator');
}
public function addRole()
{
return NULL;
$builder->add('group_by_level', ChoiceType::class, [
'choices' => [
'Group by continents' => 'continent',
'Group by country' => 'country',
],
'expanded' => true,
'multiple' => false,
]);
}
public function getLabels($key, array $values, $data)
{
if ($data['group_by_level'] === 'country') {
if ('country' === $data['group_by_level']) {
$qb = $this->countriesRepository->createQueryBuilder('c');
$countries = $qb
->andWhere($qb->expr()->in('c.countryCode', ':countries'))
->setParameter('countries', $values)
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
->andWhere($qb->expr()->in('c.countryCode', ':countries'))
->setParameter('countries', $values)
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
// initialize array and add blank key for null values
$labels[''] = $this->translator->trans('without data');
$labels['_header'] = $this->translator->trans('Country of birth');
foreach($countries as $row) {
foreach ($countries as $row) {
$labels[$row['c_countryCode']] = $this->translatableStringHelper->localize($row['c_name']);
}
} elseif ($data['group_by_level'] === 'continent') {
$labels = array(
} elseif ('continent' === $data['group_by_level']) {
$labels = [
'EU' => $this->translator->trans('Europe'),
'AS' => $this->translator->trans('Asia'),
'AN' => $this->translator->trans('Antartica'),
@@ -188,15 +151,31 @@ class CountryOfBirthAggregator implements AggregatorInterface,
'SA' => $this->translator->trans('South America'),
'NA' => $this->translator->trans('North America'),
'OC' => $this->translator->trans('Oceania'),
'' => $this->translator->trans('without data'),
'_header' => $this->translator->trans('Continent of birth')
);
'' => $this->translator->trans('without data'),
'_header' => $this->translator->trans('Continent of birth'),
];
}
return function($value) use ($labels) {
return static function ($value) use ($labels) {
return $labels[$value];
};
}
public function getQueryKeys($data)
{
return ['country_of_birth_aggregator'];
}
public function getTitle()
{
return 'Group people by country of birth';
}
public function validateForm($data, ExecutionContextInterface $context)
{
if (null === $data['group_by_level']) {
$context->buildViolation('You should select an option')
->addViolation();
}
}
}

View File

@@ -1,103 +1,87 @@
<?php
/*
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Translation\TranslatorInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class GenderAggregator implements AggregatorInterface
{
/**
*
* @var TranslatorInterface
*/
protected $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('person.gender as gender');
$qb->addGroupBy('gender');
}
public function applyOn()
{
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('person.gender as gender');
$qb->addGroupBy('gender');
}
public function getTitle()
{
return "Group people by gender";
}
public function getQueryKeys($data)
{
return array('gender');
}
public function getLabels($key, array $values, $data)
{
return function($value) {
{
return function ($value) {
switch ($value) {
case Person::FEMALE_GENDER :
case Person::FEMALE_GENDER:
return $this->translator->trans('woman');
case Person::MALE_GENDER :
case Person::MALE_GENDER:
return $this->translator->trans('man');
case Person::BOTH_GENDER:
return $this->translator->trans('both');
case null:
return $this->translator->trans('Not given');
case '_header' :
case '_header':
return $this->translator->trans('Gender');
default:
throw new \LogicException(sprintf("The value %s is not valid", $value));
throw new LogicException(sprintf('The value %s is not valid', $value));
}
};
}
public function addRole()
public function getQueryKeys($data)
{
return NULL;
return ['gender'];
}
public function getTitle()
{
return 'Group people by gender';
}
}

View File

@@ -1,105 +1,70 @@
<?php
/*
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Aggregator;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\MainBundle\Util\CountriesInfo;
use Symfony\Component\Security\Core\Role\Role;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Util\CountriesInfo;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class NationalityAggregator implements AggregatorInterface,
class NationalityAggregator implements
AggregatorInterface,
ExportElementValidatedInterface
{
/**
*
* @var EntityRepository
*/
protected $countriesRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
*
* @var TranslatorInterface
*/
protected $translator;
public function __construct(EntityRepository $countriesRepository,
TranslatableStringHelper $translatableStringHelper,
TranslatorInterface $translator)
{
public function __construct(
EntityRepository $countriesRepository,
TranslatableStringHelper $translatableStringHelper,
TranslatorInterface $translator
) {
$this->countriesRepository = $countriesRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->translator = $translator;
}
public function applyOn()
public function addRole()
{
return 'person';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('group_by_level', ChoiceType::class, array(
'choices' => array(
'Group by continents' => 'continent',
'Group by country' => 'country'
),
'expanded' => true,
'multiple' => false
));
}
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['group_by_level'] === null) {
$context->buildViolation("You should select an option")
->addViolation();
}
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
// add a clause in select part
if ($data['group_by_level'] === 'country') {
if ('country' === $data['group_by_level']) {
$qb->addSelect('nationality.countryCode as nationality_aggregator');
} elseif ($data['group_by_level'] === 'continent') {
} elseif ('continent' === $data['group_by_level']) {
$clause = 'CASE '
. 'WHEN nationality.countryCode IN(:africa_codes) THEN \'AF\' '
. 'WHEN nationality.countryCode IN(:asia_codes) THEN \'AS\' '
@@ -112,24 +77,24 @@ class NationalityAggregator implements AggregatorInterface,
. 'END as nationality_aggregator ';
$qb->addSelect($clause);
$params =
array(
[
'africa_codes' => CountriesInfo::getCountriesCodeByContinent('AF'),
'asia_codes' => CountriesInfo::getCountriesCodeByContinent('AS'),
'europe_codes' => CountriesInfo::getCountriesCodeByContinent('EU'),
'north_america_codes' => CountriesInfo::getCountriesCodeByContinent('NA'),
'south_america_codes' => CountriesInfo::getCountriesCodeByContinent('SA'),
'oceania_codes' => CountriesInfo::getCountriesCodeByContinent('OC'),
'antartica_codes' => CountriesInfo::getCountriesCodeByContinent('AN')
);
'antartica_codes' => CountriesInfo::getCountriesCodeByContinent('AN'),
];
foreach ($params as $k => $v) {
$qb->setParameter($k, $v);
}
} else {
throw new \LogicException("The group_by_level '".$data['group_by_level']
." is not known.");
throw new LogicException("The group_by_level '" . $data['group_by_level']
. ' is not known.');
}
$qb->leftJoin('person.nationality', 'nationality');
// add group by
@@ -140,46 +105,45 @@ class NationalityAggregator implements AggregatorInterface,
} else {
$qb->groupBy('nationality_aggregator');
}
}
public function getTitle()
public function applyOn()
{
return "Group people by nationality";
return 'person';
}
public function getQueryKeys($data)
public function buildForm(FormBuilderInterface $builder)
{
return array('nationality_aggregator');
}
public function addRole()
{
return NULL;
$builder->add('group_by_level', ChoiceType::class, [
'choices' => [
'Group by continents' => 'continent',
'Group by country' => 'country',
],
'expanded' => true,
'multiple' => false,
]);
}
public function getLabels($key, array $values, $data)
{
if ($data['group_by_level'] === 'country') {
if ('country' === $data['group_by_level']) {
$qb = $this->countriesRepository->createQueryBuilder('c');
$countries = $qb
->andWhere($qb->expr()->in('c.countryCode', ':countries'))
->setParameter('countries', $values)
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
->andWhere($qb->expr()->in('c.countryCode', ':countries'))
->setParameter('countries', $values)
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR);
// initialize array and add blank key for null values
$labels[''] = $this->translator->trans('without data');
$labels['_header'] = $this->translator->trans('Nationality');
foreach($countries as $row) {
foreach ($countries as $row) {
$labels[$row['c_countryCode']] = $this->translatableStringHelper->localize($row['c_name']);
}
} elseif ($data['group_by_level'] === 'continent') {
$labels = array(
} elseif ('continent' === $data['group_by_level']) {
$labels = [
'EU' => $this->translator->trans('Europe'),
'AS' => $this->translator->trans('Asia'),
'AN' => $this->translator->trans('Antartica'),
@@ -187,15 +151,31 @@ class NationalityAggregator implements AggregatorInterface,
'SA' => $this->translator->trans('South America'),
'NA' => $this->translator->trans('North America'),
'OC' => $this->translator->trans('Oceania'),
'' => $this->translator->trans('without data'),
'_header' => $this->translator->trans('Continent')
);
'' => $this->translator->trans('without data'),
'_header' => $this->translator->trans('Continent'),
];
}
return function($value) use ($labels) {
return static function ($value) use ($labels) {
return $labels[$value];
};
}
public function getQueryKeys($data)
{
return ['nationality_aggregator'];
}
public function getTitle()
{
return 'Group people by nationality';
}
public function validateForm($data, ExecutionContextInterface $context)
{
if (null === $data['group_by_level']) {
$context->buildViolation('You should select an option')
->addViolation();
}
}
}

View File

@@ -1,32 +1,24 @@
<?php
/*
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export;
/**
* This class declare constants used for the export framework.
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
abstract class Declarations
{
CONST PERSON_TYPE = 'person';
CONST PERSON_IMPLIED_IN = 'person_implied_in';
public const PERSON_IMPLIED_IN = 'person_implied_in';
public const PERSON_TYPE = 'person';
}

View File

@@ -1,136 +1,119 @@
<?php
/*
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\ExportInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\Query;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Role\Role;
use Chill\PersonBundle\Export\Declarations;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CountPerson implements ExportInterface
{
/**
*
* @var EntityManagerInterface
*/
protected $entityManager;
public function __construct(
EntityManagerInterface $em
)
{
EntityManagerInterface $em
) {
$this->entityManager = $em;
}
/**
*
*/
public function getType()
public function buildForm(FormBuilderInterface $builder)
{
return Declarations::PERSON_TYPE;
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription()
{
return "Count peoples by various parameters.";
return 'Count peoples by various parameters.';
}
public function getTitle()
public function getLabels($key, array $values, $data)
{
return "Count peoples";
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
}
public function requiredRole()
public function getQueryKeys($data)
{
return new Role(PersonVoter::STATS);
return ['export_result'];
}
/**
* Initiate the query
*
* @param QueryBuilder $qb
* @return QueryBuilder
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
{
$centers = array_map(function($el) { return $el['center']; }, $acl);
$qb = $this->entityManager->createQueryBuilder();
$qb->select('COUNT(person.id) AS export_result')
->from('ChillPersonBundle:Person', 'person')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
;
return $qb;
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getQueryKeys($data)
{
return array('export_result');
}
public function getLabels($key, array $values, $data)
{
if ($key !== 'export_result') {
throw new \LogicException("the key $key is not used by this export");
}
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return function($value) use ($labels) {
return $labels[$value];
};
}
public function getAllowedFormattersTypes()
{
return array(FormatterInterface::TYPE_TABULAR);
}
public function buildForm(FormBuilderInterface $builder) {
public function getTitle()
{
return 'Count peoples';
}
public function getType()
{
return Declarations::PERSON_TYPE;
}
/**
* Initiate the query.
*
* @return QueryBuilder
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb = $this->entityManager->createQueryBuilder();
$qb->select('COUNT(person.id) AS export_result')
->from('ChillPersonBundle:Person', 'person')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
return $qb;
}
public function requiredRole()
{
return new Role(PersonVoter::STATS);
}
public function supportsModifiers()
{
return array(Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN);
return [Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN];
}
}

View File

@@ -1,75 +1,85 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\ListInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\Query;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Role\Role;
use Chill\PersonBundle\Export\Declarations;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Chill\CustomFieldsBundle\Service\CustomFieldProvider;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Exception;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\CustomFieldsBundle\Service\CustomFieldProvider;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice;
use function array_key_exists;
use function count;
use function in_array;
/**
* Render a list of peoples
*
* @author julien
* Render a list of peoples.
*/
class ListPerson implements ListInterface, ExportElementValidatedInterface
class ListPerson implements ExportElementValidatedInterface, ListInterface
{
/**
*
* @var EntityManagerInterface
*/
protected $entityManager;
/**
*
* @var TranslatorInterface
*/
protected $translator;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
*
* @var CustomFieldProvider
*/
protected $customFieldProvider;
protected $fields = array(
/**
* @var EntityManagerInterface
*/
protected $entityManager;
protected $fields = [
'id', 'firstName', 'lastName', 'birthdate',
'placeOfBirth', 'gender', 'memo', 'email', 'phonenumber',
'mobilenumber', 'contactInfo', 'countryOfBirth', 'nationality',
'address_street_address_1', 'address_street_address_2',
'address_valid_from', 'address_postcode_label', 'address_postcode_code',
'address_country_name', 'address_country_code', 'address_isnoaddress'
);
'address_country_name', 'address_country_code', 'address_isnoaddress',
];
/**
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
* @var TranslatorInterface
*/
protected $translator;
private $slugs = [];
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper,
CustomFieldProvider $customFieldProvider
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper,
CustomFieldProvider $customFieldProvider
) {
$this->entityManager = $em;
$this->translator = $translator;
@@ -78,122 +88,81 @@ class ListPerson implements ListInterface, ExportElementValidatedInterface
}
/**
* {@inheritDoc}
*
* @param FormBuilderInterface $builder
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder)
{
$choices = array_combine($this->fields, $this->fields);
foreach ($this->getCustomFields() as $cf) {
$choices
[$this->translatableStringHelper->localize($cf->getName())]
$choices[$this->translatableStringHelper->localize($cf->getName())]
=
$cf->getSlug();
}
// Add a checkbox to select fields
$builder->add('fields', ChoiceType::class, array(
$builder->add('fields', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
'choices' => $choices,
'label' => 'Fields to include in export',
'choice_attr' => function($val, $key, $index) {
'label' => 'Fields to include in export',
'choice_attr' => static function ($val, $key, $index) {
// add a 'data-display-target' for address fields
if (substr($val, 0, 8) === 'address_') {
return ['data-display-target' => 'address_date'];
} else {
return [];
}
return [];
},
'constraints' => [new Callback(array(
'callback' => function($selected, ExecutionContextInterface $context) {
'constraints' => [new Callback([
'callback' => static function ($selected, ExecutionContextInterface $context) {
if (count($selected) === 0) {
$context->buildViolation('You must select at least one element')
->atPath('fields')
->addViolation();
}
}
))]
));
},
])],
]);
// add a date field for addresses
$builder->add('address_date', DateType::class, array(
'label' => "Address valid at this date",
'data' => new \DateTime(),
'attr' => array( 'class' => 'datepicker'),
'widget'=> 'single_text',
$builder->add('address_date', DateType::class, [
'label' => 'Address valid at this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
'required' => false,
'block_name' => 'list_export_form_address_date'
));
}
public function validateForm($data, ExecutionContextInterface $context)
{
// get the field starting with address_
$addressFields = array_filter(function($el) {
return substr($el, 0, 8) === 'address_';
}, $this->fields);
// check if there is one field starting with address in data
if (count(array_intersect($data['fields'], $addressFields)) > 0) {
// if a field address is checked, the date must not be empty
if (empty($data['address_date'])) {
$context
->buildViolation("You must set this date if an address is checked")
->atPath('address_date')
->addViolation();
}
}
'block_name' => 'list_export_form_address_date',
]);
}
/**
* Get custom fields associated with person
*
* @return CustomField[]
*/
private function getCustomFields()
{
return $this->entityManager
->createQuery("SELECT cf "
. "FROM ChillCustomFieldsBundle:CustomField cf "
. "JOIN cf.customFieldGroup g "
. "WHERE cf.type != :title AND g.entity LIKE :entity")
->setParameters(array(
'title' => 'title',
'entity' => \addcslashes(Person::class, "\\")
))
->getResult();
}
/**
* {@inheritDoc}
* {@inheritdoc}
*
* @return type
*/
public function getAllowedFormattersTypes()
{
return array(FormatterInterface::TYPE_LIST);
return [FormatterInterface::TYPE_LIST];
}
/**
* {@inheritDoc}
* {@inheritdoc}
*
* @return string
*/
public function getDescription()
{
return "Create a list of people according to various filters.";
return 'Create a list of people according to various filters.';
}
/**
* {@inheritDoc}
* {@inheritdoc}
*
* @param type $key
* @param array $values
* @param type $data
*
* @return type
*/
public function getLabels($key, array $values, $data)
@@ -202,27 +171,30 @@ class ListPerson implements ListInterface, ExportElementValidatedInterface
case 'birthdate':
// for birthdate, we have to transform the string into a date
// to format the date correctly.
return function($value) {
if ($value === '_header') { return 'birthdate'; }
if (empty($value))
{
return "";
return static function ($value) {
if ('_header' === $value) {
return 'birthdate';
}
$date = \DateTime::createFromFormat('Y-m-d', $value);
if (empty($value)) {
return '';
}
$date = DateTime::createFromFormat('Y-m-d', $value);
// check that the creation could occurs.
if ($date === false) {
throw new \Exception(sprintf("The value %s could "
. "not be converted to %s", $value, \DateTime::class));
if (false === $date) {
throw new Exception(sprintf('The value %s could '
. 'not be converted to %s', $value, DateTime::class));
}
return $date->format('d-m-Y');
};
case 'gender' :
case 'gender':
// for gender, we have to translate men/women statement
return function($value) {
if ($value === '_header') { return 'gender'; }
return function ($value) {
if ('_header' === $value) {
return 'gender';
}
return $this->translator->trans($value);
};
@@ -232,157 +204,87 @@ class ListPerson implements ListInterface, ExportElementValidatedInterface
->getRepository('ChillMainBundle:Country');
// load all countries in a single query
$countryRepository->findBy(array('countryCode' => $values));
$countryRepository->findBy(['countryCode' => $values]);
return function($value) use ($key, $countryRepository) {
if ($value === '_header') { return \strtolower($key); }
return function ($value) use ($key, $countryRepository) {
if ('_header' === $value) {
return strtolower($key);
}
if ($value === NULL) {
if (null === $value) {
return $this->translator->trans('no data');
}
$country = $countryRepository->find($value);
return $this->translatableStringHelper->localize(
$country->getName());
$country->getName()
);
};
case 'address_country_name':
return function($value) use ($key) {
if ($value === '_header') { return \strtolower($key); }
return function ($value) use ($key) {
if ('_header' === $value) {
return strtolower($key);
}
if ($value === NULL) {
if (null === $value) {
return '';
}
return $this->translatableStringHelper->localize(json_decode($value, true));
};
case 'address_isnoaddress':
return function($value) use ($key) {
if ($value === '_header') { return 'address.address_homeless'; }
return static function ($value) {
if ('_header' === $value) {
return 'address.address_homeless';
}
if ($value) {
return 'X';
} else {
return '';
}
return '';
};
default:
// for fields which are associated with person
if (in_array($key, $this->fields)) {
return function($value) use ($key) {
if ($value === '_header') { return \strtolower($key); }
if (in_array($key, $this->fields, true)) {
return static function ($value) use ($key) {
if ('_header' === $value) {
return strtolower($key);
}
return $value;
};
};
} else {
return $this->getLabelForCustomField($key, $values, $data);
}
}
}
private function getLabelForCustomField($key, array $values, $data)
{
// for fields which are custom fields
/* @var $cf CustomField */
$cf = $this->entityManager
->getRepository(CustomField::class)
->findOneBy(array('slug' => $this->DQLToSlug($key)));
$cfType = $this->customFieldProvider->getCustomFieldByType($cf->getType());
$defaultFunction = function($value) use ($cf) {
if ($value === '_header') {
return $this->translatableStringHelper->localize($cf->getName());
}
return $this->customFieldProvider
->getCustomFieldByType($cf->getType())
->render(json_decode($value, true), $cf, 'csv');
};
if ($cfType instanceof CustomFieldChoice and $cfType->isMultiple($cf)) {
return function($value) use ($cf, $cfType, $key) {
$slugChoice = $this->extractInfosFromSlug($key)['additionnalInfos']['choiceSlug'];
$decoded = \json_decode($value, true);
if ($value === '_header') {
$label = $cfType->getChoices($cf)[$slugChoice];
return $this->translatableStringHelper->localize($cf->getName())
.' | '.$label;
}
if ($slugChoice === '_other' and $cfType->isChecked($cf, $choiceSlug, $decoded)) {
return $cfType->extractOtherValue($cf, $decoded);
} else {
return $cfType->isChecked($cf, $slugChoice, $decoded);
}
};
} else {
return $defaultFunction;
}
}
/**
* {@inheritDoc}
* {@inheritdoc}
*
* @param type $data
*
* @return type
*/
public function getQueryKeys($data)
{
$fields = array();
$fields = [];
foreach ($data['fields'] as $key) {
if (in_array($key, $this->fields)) {
if (in_array($key, $this->fields, true)) {
$fields[] = $key;
}
}
// add the key from slugs and return
return \array_merge($fields, \array_keys($this->slugs));
return array_merge($fields, array_keys($this->slugs));
}
/**
* clean a slug to be usable by DQL
*
* @param string $slugsanitize
* @param string $type the type of the customfield, if required (currently only for choices)
* @return string
*/
private function slugToDQL($slug, $type = "default", array $additionalInfos = [])
{
$uid = 'slug_'.\uniqid();
$this->slugs[$uid] = [
'slug' => $slug,
'type' => $type,
'additionnalInfos' => $additionalInfos
];
return $uid;
}
private function DQLToSlug($cleanedSlug)
{
return $this->slugs[$cleanedSlug]['slug'];
}
/**
*
* @param type $cleanedSlug
* @return an array with keys = 'slug', 'type', 'additionnalInfo'
*/
private function extractInfosFromSlug($slug)
{
return $this->slugs[$slug];
}
/**
* {@inheritDoc}
*
* {@inheritdoc}
*/
public function getResult($query, $data)
{
@@ -390,18 +292,17 @@ class ListPerson implements ListInterface, ExportElementValidatedInterface
}
/**
* {@inheritDoc}
* {@inheritdoc}
*
* @return string
*/
public function getTitle()
{
return "List peoples";
return 'List peoples';
}
/**
* {@inheritDoc}
*
* {@inheritdoc}
*/
public function getType()
{
@@ -409,27 +310,29 @@ class ListPerson implements ListInterface, ExportElementValidatedInterface
}
/**
* {@inheritDoc}
*
* {@inheritdoc}
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(function($el) { return $el['center']; }, $acl);
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
// throw an error if any fields are present
if (!\array_key_exists('fields', $data)) {
throw new \Doctrine\DBAL\Exception\InvalidArgumentException("any fields "
. "have been checked");
if (!array_key_exists('fields', $data)) {
throw new \Doctrine\DBAL\Exception\InvalidArgumentException('any fields '
. 'have been checked');
}
$qb = $this->entityManager->createQueryBuilder();
foreach ($this->fields as $f) {
if (in_array($f, $data['fields'])) {
if (in_array($f, $data['fields'], true)) {
switch ($f) {
case 'countryOfBirth':
case 'nationality':
$qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f));
break;
case 'address_street_address_1':
case 'address_street_address_2':
@@ -439,14 +342,16 @@ class ListPerson implements ListInterface, ExportElementValidatedInterface
case 'address_country_name':
case 'address_country_code':
case 'address_isnoaddress':
$qb->addSelect(sprintf(
'GET_PERSON_ADDRESS_%s(person.id, :address_date) AS %s',
// get the part after address_
strtoupper(substr($f, 8)),
$f));
$f
));
$qb->setParameter('address_date', $data['address_date']);
break;
default:
$qb->addSelect(sprintf('person.%s as %s', $f, $f));
}
@@ -455,37 +360,43 @@ class ListPerson implements ListInterface, ExportElementValidatedInterface
foreach ($this->getCustomFields() as $cf) {
$cfType = $this->customFieldProvider->getCustomFieldByType($cf->getType());
if ($cfType instanceof CustomFieldChoice and $cfType->isMultiple($cf)) {
foreach($cfType->getChoices($cf) as $choiceSlug => $label) {
$slug = $this->slugToDQL($cf->getSlug(), 'choice', [ 'choiceSlug' => $choiceSlug ]);
if ($cfType instanceof CustomFieldChoice && $cfType->isMultiple($cf)) {
foreach ($cfType->getChoices($cf) as $choiceSlug => $label) {
$slug = $this->slugToDQL($cf->getSlug(), 'choice', ['choiceSlug' => $choiceSlug]);
$qb->addSelect(
sprintf('GET_JSON_FIELD_BY_KEY(person.cFData, :slug%s) AS %s',
$slug, $slug));
sprintf(
'GET_JSON_FIELD_BY_KEY(person.cFData, :slug%s) AS %s',
$slug,
$slug
)
);
$qb->setParameter(sprintf('slug%s', $slug), $cf->getSlug());
}
} else {
$slug = $this->slugToDQL($cf->getSlug());
$qb->addSelect(
sprintf('GET_JSON_FIELD_BY_KEY(person.cFData, :slug%s) AS %s',
$slug, $slug));
sprintf(
'GET_JSON_FIELD_BY_KEY(person.cFData, :slug%s) AS %s',
$slug,
$slug
)
);
$qb->setParameter(sprintf('slug%s', $slug), $cf->getSlug());
}
}
$qb
->from('ChillPersonBundle:Person', 'person')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
;
->from('ChillPersonBundle:Person', 'person')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
return $qb;
}
/**
*
* {@inheritDoc}
* {@inheritdoc}
*/
public function requiredRole()
{
@@ -493,11 +404,125 @@ class ListPerson implements ListInterface, ExportElementValidatedInterface
}
/**
*
* {@inheritDoc}
* {@inheritdoc}
*/
public function supportsModifiers()
{
return array(Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN);
return [Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN];
}
public function validateForm($data, ExecutionContextInterface $context)
{
// get the field starting with address_
$addressFields = array_filter(static function ($el) {
return substr($el, 0, 8) === 'address_';
}, $this->fields);
// check if there is one field starting with address in data
if (count(array_intersect($data['fields'], $addressFields)) > 0) {
// if a field address is checked, the date must not be empty
if (empty($data['address_date'])) {
$context
->buildViolation('You must set this date if an address is checked')
->atPath('address_date')
->addViolation();
}
}
}
private function DQLToSlug($cleanedSlug)
{
return $this->slugs[$cleanedSlug]['slug'];
}
/**
* @param mixed $slug
*
* @return an array with keys = 'slug', 'type', 'additionnalInfo'
*/
private function extractInfosFromSlug($slug)
{
return $this->slugs[$slug];
}
/**
* Get custom fields associated with person.
*
* @return CustomField[]
*/
private function getCustomFields()
{
return $this->entityManager
->createQuery('SELECT cf '
. 'FROM ChillCustomFieldsBundle:CustomField cf '
. 'JOIN cf.customFieldGroup g '
. 'WHERE cf.type != :title AND g.entity LIKE :entity')
->setParameters([
'title' => 'title',
'entity' => addcslashes(Person::class, '\\'),
])
->getResult();
}
private function getLabelForCustomField($key, array $values, $data)
{
// for fields which are custom fields
/** @var CustomField $cf */
$cf = $this->entityManager
->getRepository(CustomField::class)
->findOneBy(['slug' => $this->DQLToSlug($key)]);
$cfType = $this->customFieldProvider->getCustomFieldByType($cf->getType());
$defaultFunction = function ($value) use ($cf) {
if ('_header' === $value) {
return $this->translatableStringHelper->localize($cf->getName());
}
return $this->customFieldProvider
->getCustomFieldByType($cf->getType())
->render(json_decode($value, true), $cf, 'csv');
};
if ($cfType instanceof CustomFieldChoice && $cfType->isMultiple($cf)) {
return function ($value) use ($cf, $cfType, $key) {
$slugChoice = $this->extractInfosFromSlug($key)['additionnalInfos']['choiceSlug'];
$decoded = json_decode($value, true);
if ('_header' === $value) {
$label = $cfType->getChoices($cf)[$slugChoice];
return $this->translatableStringHelper->localize($cf->getName())
. ' | ' . $label;
}
if ('_other' === $slugChoice && $cfType->isChecked($cf, $choiceSlug, $decoded)) {
return $cfType->extractOtherValue($cf, $decoded);
}
return $cfType->isChecked($cf, $slugChoice, $decoded);
};
} else {
return $defaultFunction;
}
}
/**
* clean a slug to be usable by DQL.
*
* @param string $type the type of the customfield, if required (currently only for choices)
* @param mixed $slug
*
* @return string
*/
private function slugToDQL($slug, $type = 'default', array $additionalInfos = [])
{
$uid = 'slug_' . uniqid();
$this->slugs[$uid] = [
'slug' => $slug,
'type' => $type,
'additionnalInfos' => $additionalInfos,
];
return $uid;
}
}

View File

@@ -1,11 +1,20 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\DirectExportInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\EntityManagerInterface;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -21,25 +30,14 @@ use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Render a list of duplicate peoples
* Render a list of duplicate peoples.
*/
class ListPersonDuplicate implements DirectExportInterface, ExportElementValidatedInterface
{
/**
*
* @var EntityManagerInterface
* @var float
*/
protected $entityManager;
/**
* @var \Symfony\Component\Translation\TranslatorInterface
*/
private $translator;
/**
* @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface
*/
private $router;
private const PRECISION_DEFAULT_VALUE = 0.7;
/**
* @var string
@@ -47,47 +45,35 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
protected $baseUrl;
/**
* @var float
* @var EntityManagerInterface
*/
private const PRECISION_DEFAULT_VALUE = 0.7;
protected $entityManager;
/**
* @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface
*/
private $router;
/**
* @var \Symfony\Component\Translation\TranslatorInterface
*/
private $translator;
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator,
UrlGeneratorInterface $router,
$routeParameters
EntityManagerInterface $em,
TranslatorInterface $translator,
UrlGeneratorInterface $router,
$routeParameters
) {
$this->entityManager = $em;
$this->translator = $translator;
$this->router = $router;
$this->baseUrl = $routeParameters['scheme'].
'://'.$routeParameters['host'];
$this->baseUrl = $routeParameters['scheme'] .
'://' . $routeParameters['host'];
}
/**
* {@inheritDoc}
*
* @return string
*/
public function getTitle()
{
return "List duplicates";
}
/**
* {@inheritDoc}
*
* @return string
*/
public function getDescription()
{
return "Create a list of duplicate people.";
}
/**
* {@inheritDoc}
*
* @param FormBuilderInterface $builder
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder)
{
@@ -97,27 +83,23 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
]);
}
public function validateForm($data, ExecutionContextInterface $context)
{
}
public function generate(array $acl, array $data = []): Response
{
$values = [];
$values[] = $this->getHeaders();
$result = $this->getResult($data);
foreach ($result as $row) {
$values[] = [
$row['id1'],
$row['firstname1'],
$row['lastname1'],
$this->baseUrl.$this->router->generate('chill_person_view', ['person_id' => $row['id1']]),
$this->baseUrl . $this->router->generate('chill_person_view', ['person_id' => $row['id1']]),
$row['id2'],
$row['firstname2'],
$row['lastname2'],
$this->baseUrl.$this->router->generate('chill_person_view', ['person_id' => $row['id2']]),
$this->baseUrl . $this->router->generate('chill_person_view', ['person_id' => $row['id2']]),
];
}
@@ -125,15 +107,15 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
$spreadsheet->getActiveSheet()->fromArray($values);
// Make links clickable
for ($i = 1; $i <= $spreadsheet->getActiveSheet()->getHighestDataRow(); $i++) {
$spreadsheet->getActiveSheet()->getCell('D'.$i)->getHyperlink()
->setUrl($spreadsheet->getActiveSheet()->getCell('D'.$i)->getValue());
$spreadsheet->getActiveSheet()->getCell('H'.$i)->getHyperlink()
->setUrl($spreadsheet->getActiveSheet()->getCell('H'.$i)->getValue());
for ($i = 1; $spreadsheet->getActiveSheet()->getHighestDataRow() >= $i; ++$i) {
$spreadsheet->getActiveSheet()->getCell('D' . $i)->getHyperlink()
->setUrl($spreadsheet->getActiveSheet()->getCell('D' . $i)->getValue());
$spreadsheet->getActiveSheet()->getCell('H' . $i)->getHyperlink()
->setUrl($spreadsheet->getActiveSheet()->getCell('H' . $i)->getValue());
}
$writer = new Xlsx($spreadsheet);
$temp_file = sys_get_temp_dir().'/'.uniqid('export_').'.xlsx';
$temp_file = sys_get_temp_dir() . '/' . uniqid('export_') . '.xlsx';
$writer->save($temp_file);
$response = new BinaryFileResponse($temp_file);
@@ -143,6 +125,49 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
return $response;
}
/**
* {@inheritdoc}
*
* @return string
*/
public function getDescription()
{
return 'Create a list of duplicate people.';
}
/**
* {@inheritdoc}
*
* @return string
*/
public function getTitle()
{
return 'List duplicates';
}
public function requiredRole(): Role
{
return new Role(PersonVoter::DUPLICATE);
}
public function validateForm($data, ExecutionContextInterface $context)
{
}
protected function getHeaders(): array
{
return [
$this->translator->trans('Departure folder number'),
$this->translator->trans('Last name'),
$this->translator->trans('First name'),
$this->translator->trans('Link'),
$this->translator->trans('Arrival folder number'),
$this->translator->trans('Last name'),
$this->translator->trans('First name'),
$this->translator->trans('Link'),
];
}
protected function getResult($data = [])
{
$precision = $data['precision'] ?? self::PRECISION_DEFAULT_VALUE;
@@ -153,23 +178,23 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
SIMILARITY(p.fullnamecanonical, p2.fullnamecanonical) AS "similarity nom + prenom",
SIMILARITY(p.lastname, p2.lastname) AS "similarity nom",
SIMILARITY(p.firstname, p2.firstname) AS "similarity prenom"
FROM chill_person_person AS p
JOIN chill_person_person AS p2
ON p.id != p2.id
AND (SIMILARITY(p.fullnamecanonical, p2.fullnamecanonical) > :precision
FROM chill_person_person AS p
JOIN chill_person_person AS p2
ON p.id != p2.id
AND (SIMILARITY(p.fullnamecanonical, p2.fullnamecanonical) > :precision
AND p.id < p2.id)
OR (UNACCENT(LOWER(p.firstname)) = UNACCENT(LOWER(p2.lastname))
AND UNACCENT(LOWER(p.lastname)) = UNACCENT(LOWER(p2.firstname)))
JOIN centers AS p1center
JOIN centers AS p1center
ON p1center.id = p.center_id
JOIN centers AS p2center
JOIN centers AS p2center
ON p2center.id = p2.center_id
WHERE NOT EXISTS (
SELECT id
FROM chill_person_not_duplicate as pnd
WHERE (pnd.person1_id = p.id
SELECT id
FROM chill_person_not_duplicate as pnd
WHERE (pnd.person1_id = p.id
AND pnd.person2_id = p2.id)
OR (pnd.person2_id = p.id
OR (pnd.person2_id = p.id
AND pnd.person1_id = p2.id)
)
ORDER BY p.fullnamecanonical, p.id, p2.id';
@@ -180,23 +205,4 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat
return $statement->fetchAll();
}
protected function getHeaders(): array
{
return [
$this->translator->trans('Departure folder number'),
$this->translator->trans('Last name'),
$this->translator->trans('First name'),
$this->translator->trans('Link'),
$this->translator->trans('Arrival folder number'),
$this->translator->trans('Last name'),
$this->translator->trans('First name'),
$this->translator->trans('Link'),
];
}
public function requiredRole(): Role
{
return new Role(PersonVoter::DUPLICATE);
}
}

View File

@@ -1,33 +1,26 @@
<?php
/*
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Form\Type\ChillDateType;
use Doctrine\DBAL\Types\Type;
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
use DateTime;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
/**
*
*
*/
class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
{
public function addRole()
@@ -38,10 +31,11 @@ class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportEl
public function alterQuery(QueryBuilder $qb, $data)
{
$this->addJoinAccompanyingPeriod($qb);
$clause = $qb->expr()->andX(
$qb->expr()->lte('accompanying_period.closingDate', ':date_to'),
$qb->expr()->gte('accompanying_period.closingDate', ':date_from'));
$qb->expr()->gte('accompanying_period.closingDate', ':date_from')
);
$qb->andWhere($clause);
$qb->setParameter('date_from', $data['date_from'], Type::DATE);
@@ -55,31 +49,31 @@ class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportEl
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, array(
'label' => "Having an accompanying period closed after this date",
'data' => new \DateTime("-1 month"),
));
$builder->add('date_to', ChillDateType::class, array(
'label' => "Having an accompanying period closed before this date",
'data' => new \DateTime(),
));
$builder->add('date_from', ChillDateType::class, [
'label' => 'Having an accompanying period closed after this date',
'data' => new DateTime('-1 month'),
]);
$builder->add('date_to', ChillDateType::class, [
'label' => 'Having an accompanying period closed before this date',
'data' => new DateTime(),
]);
}
public function describeAction($data, $format = 'string')
{
return [
"Filtered by accompanying period: persons having an accompanying period"
. " closed between the %date_from% and %date_to%",
'Filtered by accompanying period: persons having an accompanying period'
. ' closed between the %date_from% and %date_to%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y')
]
'%date_to%' => $data['date_to']->format('d-m-Y'),
],
];
}
public function getTitle(): string
{
return "Filter by accompanying period: closed between two dates";
return 'Filter by accompanying period: closed between two dates';
}
}

View File

@@ -1,33 +1,26 @@
<?php
/*
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Form\Type\ChillDateType;
use Doctrine\DBAL\Types\Type;
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
use DateTime;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
/**
*
*
*/
class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
{
public function addRole()
@@ -38,18 +31,18 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i
public function alterQuery(QueryBuilder $qb, $data)
{
$this->addJoinAccompanyingPeriod($qb);
$clause = $qb->expr()->andX();
$clause->add(
$qb->expr()->lte('accompanying_period.openingDate', ':date_to')
);
);
$clause->add(
$qb->expr()->orX(
$qb->expr()->gte('accompanying_period.closingDate', ':date_from'),
$qb->expr()->isNull('accompanying_period.closingDate')
)
);
)
);
$qb->andWhere($clause);
$qb->setParameter('date_from', $data['date_from'], Type::DATE);
@@ -63,33 +56,33 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, array(
'label' => "Having an accompanying period opened after this date",
'data' => new \DateTime("-1 month"),
));
$builder->add('date_to', ChillDateType::class, array(
'label' => "Having an accompanying period ending before this date, or "
. "still opened at this date",
'data' => new \DateTime(),
));
$builder->add('date_from', ChillDateType::class, [
'label' => 'Having an accompanying period opened after this date',
'data' => new DateTime('-1 month'),
]);
$builder->add('date_to', ChillDateType::class, [
'label' => 'Having an accompanying period ending before this date, or '
. 'still opened at this date',
'data' => new DateTime(),
]);
}
public function describeAction($data, $format = 'string')
{
return [
"Filtered by accompanying period: persons having an accompanying period"
. " opened after the %date_from% and closed before the %date_to% (or still opened "
. "at the %date_to%)",
'Filtered by accompanying period: persons having an accompanying period'
. ' opened after the %date_from% and closed before the %date_to% (or still opened '
. 'at the %date_to%)',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y')
]
'%date_to%' => $data['date_to']->format('d-m-Y'),
],
];
}
public function getTitle(): string
{
return "Filter by accompanying period: active period";
return 'Filter by accompanying period: active period';
}
}

View File

@@ -1,33 +1,26 @@
<?php
/*
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Form\Type\ChillDateType;
use Doctrine\DBAL\Types\Type;
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
use DateTime;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
/**
*
*
*/
class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
{
public function addRole()
@@ -38,10 +31,11 @@ class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportEl
public function alterQuery(QueryBuilder $qb, $data)
{
$this->addJoinAccompanyingPeriod($qb);
$clause = $qb->expr()->andX(
$qb->expr()->lte('accompanying_period.openingDate', ':date_to'),
$qb->expr()->gte('accompanying_period.openingDate', ':date_from'));
$qb->expr()->gte('accompanying_period.openingDate', ':date_from')
);
$qb->andWhere($clause);
$qb->setParameter('date_from', $data['date_from'], Type::DATE);
@@ -55,31 +49,31 @@ class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportEl
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', ChillDateType::class, array(
'label' => "Having an accompanying period opened after this date",
'data' => new \DateTime("-1 month"),
));
$builder->add('date_to', ChillDateType::class, array(
'label' => "Having an accompanying period opened before this date",
'data' => new \DateTime(),
));
$builder->add('date_from', ChillDateType::class, [
'label' => 'Having an accompanying period opened after this date',
'data' => new DateTime('-1 month'),
]);
$builder->add('date_to', ChillDateType::class, [
'label' => 'Having an accompanying period opened before this date',
'data' => new DateTime(),
]);
}
public function describeAction($data, $format = 'string')
{
return [
"Filtered by accompanying period: persons having an accompanying period"
. " opened between the %date_from% and %date_to%",
'Filtered by accompanying period: persons having an accompanying period'
. ' opened between the %date_from% and %date_to%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y')
]
'%date_to%' => $data['date_to']->format('d-m-Y'),
],
];
}
public function getTitle(): string
{
return "Filter by accompanying period: starting between two dates";
return 'Filter by accompanying period: starting between two dates';
}
}

View File

@@ -1,43 +1,27 @@
<?php
/*
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Constraints;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Form\Type\Export\FilterType;
use Symfony\Component\Form\FormError;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class BirthdateFilter implements FilterInterface, ExportElementValidatedInterface
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
{
public function addRole()
{
return null;
@@ -46,15 +30,18 @@ class BirthdateFilter implements FilterInterface, ExportElementValidatedInterfac
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->between('person.birthdate', ':date_from',
':date_to');
$clause = $qb->expr()->between(
'person.birthdate',
':date_from',
':date_to'
);
if ($where instanceof Expr\Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('date_from', $data['date_from']);
$qb->setParameter('date_to', $data['date_to']);
@@ -67,59 +54,30 @@ class BirthdateFilter implements FilterInterface, ExportElementValidatedInterfac
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
{
$builder->add('date_from', DateType::class, array(
'label' => "Born after this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
$builder->add('date_from', DateType::class, [
'label' => 'Born after this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add('date_to', DateType::class, array(
'label' => "Born before this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
]);
$builder->add('date_to', DateType::class, [
'label' => 'Born before this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
));
}
public function validateForm($data, ExecutionContextInterface $context)
{
$date_from = $data['date_from'];
$date_to = $data['date_to'];
if ($date_from === null) {
$context->buildViolation('The "date from" should not be empty')
//->atPath('date_from')
->addViolation();
}
if ($date_to === null) {
$context->buildViolation('The "date to" should not be empty')
//->atPath('date_to')
->addViolation();
}
if (
($date_from !== null && $date_to !== null)
&&
$date_from >= $date_to
) {
$context->buildViolation('The date "date to" should be after the '
. 'date given in "date from" field')
->addViolation();
}
]);
}
public function describeAction($data, $format = 'string')
{
return array('Filtered by person\'s birtdate: '
. 'between %date_from% and %date_to%', array(
return ['Filtered by person\'s birtdate: '
. 'between %date_from% and %date_to%', [
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y')
));
'%date_to%' => $data['date_to']->format('d-m-Y'),
], ];
}
public function getTitle()
@@ -127,4 +85,30 @@ class BirthdateFilter implements FilterInterface, ExportElementValidatedInterfac
return 'Filter by person\'s birthdate';
}
public function validateForm($data, ExecutionContextInterface $context)
{
$date_from = $data['date_from'];
$date_to = $data['date_to'];
if (null === $date_from) {
$context->buildViolation('The "date from" should not be empty')
//->atPath('date_from')
->addViolation();
}
if (null === $date_to) {
$context->buildViolation('The "date to" should not be empty')
//->atPath('date_to')
->addViolation();
}
if (
(null !== $date_from && null !== $date_to)
&& $date_from >= $date_to
) {
$context->buildViolation('The date "date to" should be after the '
. 'date given in "date from" field')
->addViolation();
}
}
}

View File

@@ -1,90 +1,57 @@
<?php
/*
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class GenderFilter implements FilterInterface,
ExportElementValidatedInterface
use function count;
use function in_array;
use function is_array;
class GenderFilter implements
ExportElementValidatedInterface,
FilterInterface
{
/**
*
* @var TranslatorInterface
*/
protected $translator;
function __construct(TranslatorInterface $translator)
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function applyOn()
public function addRole()
{
return 'person';
}
/**
*
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_genders', ChoiceType::class, array(
'choices' => array(
'Woman' => Person::FEMALE_GENDER,
'Man' => Person::MALE_GENDER,
'Both' => Person::BOTH_GENDER,
'Not given' => 'null'
),
'multiple' => true,
'expanded' => true
));
}
public function validateForm($data, ExecutionContextInterface $context)
{
if (!is_array($data['accepted_genders']) || count($data['accepted_genders']) === 0 ) {
$context->buildViolation("You should select an option")
->addViolation();
}
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$isIn = $qb->expr()->in('person.gender', ':person_gender');
if (!\in_array('null', $data['accepted_genders'])) {
if (!in_array('null', $data['accepted_genders'], true)) {
$clause = $isIn;
} else {
$clause = $qb->expr()->orX($isIn, $qb->expr()->isNull('person.gender'));
@@ -97,15 +64,53 @@ class GenderFilter implements FilterInterface,
}
$qb->add('where', $where);
$qb->setParameter('person_gender', \array_filter(
$qb->setParameter('person_gender', array_filter(
$data['accepted_genders'],
function($el) {
return $el !== 'null';
}));
static function ($el) {
return 'null' !== $el;
}
));
}
public function applyOn()
{
return 'person';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_genders', ChoiceType::class, [
'choices' => [
'Woman' => Person::FEMALE_GENDER,
'Man' => Person::MALE_GENDER,
'Both' => Person::BOTH_GENDER,
'Not given' => 'null',
],
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string')
{
$genders = [];
foreach ($data['accepted_genders'] as $g) {
if ('null' === $g) {
$genders[] = $this->translator->trans('Not given');
} else {
$genders[] = $this->translator->trans($g);
}
}
return [
'Filtering by genders: only %genders%',
['%genders%' => implode(', ', $genders)],
];
}
/**
* A title which will be used in the label for the form
* A title which will be used in the label for the form.
*
* @return string
*/
@@ -114,26 +119,11 @@ class GenderFilter implements FilterInterface,
return 'Filter by person gender';
}
public function addRole()
public function validateForm($data, ExecutionContextInterface $context)
{
return NULL;
}
public function describeAction($data, $format = 'string')
{
$genders = [];
foreach ($data['accepted_genders'] as $g) {
if ('null' === $g) {
$genders[] = $this->translator->trans('Not given');
} else {
$genders[] = $this->translator->trans($g);
}
if (!is_array($data['accepted_genders']) || count($data['accepted_genders']) === 0) {
$context->buildViolation('You should select an option')
->addViolation();
}
return [
"Filtering by genders: only %genders%",
[ "%genders%" => \implode(", ", $genders)]
];
}
}

View File

@@ -1,45 +1,33 @@
<?php
/*
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Entity\Country;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\Select2CountryType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class NationalityFilter implements FilterInterface,
ExportElementValidatedInterface
class NationalityFilter implements
ExportElementValidatedInterface,
FilterInterface
{
/**
*
* @var TranslatableStringHelper
*/
private $translatableStringHelper;
@@ -49,25 +37,9 @@ class NationalityFilter implements FilterInterface,
$this->translatableStringHelper = $helper;
}
public function applyOn()
public function addRole()
{
return 'person';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('nationalities', Select2CountryType::class, array(
'placeholder' => 'Choose countries'
));
}
public function validateForm($data, ExecutionContextInterface $context)
{
if ($data['nationalities'] === null) {
$context->buildViolation("A nationality must be selected")
->addViolation();
}
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
@@ -82,7 +54,33 @@ class NationalityFilter implements FilterInterface,
}
$qb->add('where', $where);
$qb->setParameter('person_nationality', array($data['nationalities']));
$qb->setParameter('person_nationality', [$data['nationalities']]);
}
public function applyOn()
{
return 'person';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('nationalities', Select2CountryType::class, [
'placeholder' => 'Choose countries',
]);
}
public function describeAction($data, $format = 'string')
{
$countries = $data['nationalities'];
$names = array_map(function (Country $c) {
return $this->translatableStringHelper->localize($c->getName());
}, [$countries]);
return [
'Filtered by nationality : %nationalities%',
['%nationalities%' => implode(', ', $names)],
];
}
public function getTitle()
@@ -90,22 +88,11 @@ class NationalityFilter implements FilterInterface,
return "Filter by person's nationality";
}
public function addRole()
public function validateForm($data, ExecutionContextInterface $context)
{
return NULL;
}
public function describeAction($data, $format = 'string')
{
$countries = $data['nationalities'];
$names = array_map(function(Country $c) {
return $this->translatableStringHelper->localize($c->getName());
}, array($countries));
return array(
"Filtered by nationality : %nationalities%",
array('%nationalities%' => implode(", ", $names))
);
if (null === $data['nationalities']) {
$context->buildViolation('A nationality must be selected')
->addViolation();
}
}
}

View File

@@ -1,26 +1,31 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form;
use Chill\MainBundle\Entity\Center;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\MainBundle\Form\Type\UserPickerType;
use Symfony\Component\Security\Core\Role\Role;
use Chill\PersonBundle\Form\Type\ClosingMotivePickerType;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Role\Role;
/**
* Class AccompanyingPeriodType
*
* @package Chill\PersonBundle\Form
*/
class AccompanyingPeriodType extends AbstractType
{
/**
@@ -34,72 +39,65 @@ class AccompanyingPeriodType extends AbstractType
protected $config = [];
/**
*
* @param string[] $config configuration of visibility of some fields
*/
public function __construct(array $config)
{
$this->config = $config;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
//if the period_action is close, date opening should not be seen
if ($options['period_action'] !== 'close') {
if ('close' !== $options['period_action']) {
$builder
->add('openingDate', DateType::class, [
"required" => true,
'widget' => 'single_text',
'format' => 'dd-MM-yyyy'
])
;
'required' => true,
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]);
}
// closingDate should be seen only if
// period_action = close
// OR ( period_action = update AND accompanying period is already closed )
$accompanyingPeriod = $options['data'];
if (
($options['period_action'] === 'close')
OR
($options['period_action'] === 'create')
OR
($options['period_action'] === 'update' AND !$accompanyingPeriod->isOpen())
('close' === $options['period_action'])
|| ('create' === $options['period_action'])
|| ('update' === $options['period_action'] && !$accompanyingPeriod->isOpen())
) {
$builder->add('closingDate', DateType::class, [
'required' => true,
'widget' => 'single_text',
'format' => 'dd-MM-yyyy'
'format' => 'dd-MM-yyyy',
]);
$builder->add('closingMotive', ClosingMotivePickerType::class);
}
if ($this->config['user'] === 'visible') {
if ('visible' === $this->config['user']) {
$builder->add('user', UserPickerType::class, [
'center' => $options['center'],
'role' => new Role(PersonVoter::SEE),
'role' => new Role(PersonVoter::SEE),
]);
}
$builder->add('remark', ChillTextareaType::class, [
'required' => false
]);
$builder->add('remark', ChillTextareaType::class, [
'required' => false,
]);
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['action'] = $options['period_action'];
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'Chill\PersonBundle\Entity\AccompanyingPeriod'
'data_class' => 'Chill\PersonBundle\Entity\AccompanyingPeriod',
]);
$resolver
@@ -107,18 +105,7 @@ class AccompanyingPeriodType extends AbstractType
->addAllowedTypes('period_action', 'string')
->addAllowedValues('period_action', ['update', 'open', 'close', 'create'])
->setRequired('center')
->setAllowedTypes('center', Center::class)
;
}
/**
* @param FormView $view
* @param FormInterface $form
* @param array $options
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['action'] = $options['period_action'];
->setAllowedTypes('center', Center::class);
}
/**

View File

@@ -1,68 +1,127 @@
<?php
/*
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\ChoiceLoader;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Doctrine\ORM\EntityRepository;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\EntityRepository;
use RuntimeException;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use function call_user_func;
use function count;
use function in_array;
/**
* Class PersonChoiceLoader
*
* @package Chill\PersonBundle\Form\ChoiceLoader
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class PersonChoiceLoader implements ChoiceLoaderInterface
{
/**
* @var EntityRepository
*/
protected $personRepository;
/**
* @var array
*/
protected $lazyLoadedPersons = [];
/**
* @var array
*/
protected $centers = [];
/**
* @var array
*/
protected $lazyLoadedPersons = [];
/**
* @var EntityRepository
*/
protected $personRepository;
/**
* PersonChoiceLoader constructor.
*
* @param EntityRepository $personRepository
* @param array|null $centers
*/
public function __construct(
EntityRepository $personRepository,
array $centers = null
?array $centers = null
) {
$this->personRepository = $personRepository;
if (NULL !== $centers) {
if (null !== $centers) {
$this->centers = $centers;
}
}
/**
* @param null $value
*/
public function loadChoiceList($value = null): ChoiceListInterface
{
$list = new \Symfony\Component\Form\ChoiceList\ArrayChoiceList(
$this->lazyLoadedPersons,
static function (Person $p) use ($value) {
return call_user_func($value, $p);
}
);
return $list;
}
/**
* @param null $value
*
* @return array
*/
public function loadChoicesForValues(array $values, $value = null)
{
$choices = [];
foreach ($values as $value) {
if (empty($value)) {
continue;
}
$person = $this->personRepository->find($value);
if (
$this->hasCenterFilter()
&& !in_array($person->getCenter(), $this->centers, true)
) {
throw new RuntimeException('chosen a person not in correct center');
}
$choices[] = $person;
}
return $choices;
}
/**
* @param null $value
*
* @return array|string[]
*/
public function loadValuesForChoices(array $choices, $value = null)
{
$values = [];
foreach ($choices as $choice) {
if (null === $choice) {
$values[] = null;
continue;
}
$id = call_user_func($value, $choice);
$values[] = $id;
$this->lazyLoadedPersons[$id] = $choice;
}
return $values;
}
/**
* @return bool
*/
@@ -70,69 +129,4 @@ class PersonChoiceLoader implements ChoiceLoaderInterface
{
return count($this->centers) > 0;
}
/**
* @param null $value
* @return ChoiceListInterface
*/
public function loadChoiceList($value = null): ChoiceListInterface
{
$list = new \Symfony\Component\Form\ChoiceList\ArrayChoiceList(
$this->lazyLoadedPersons,
function(Person $p) use ($value) {
return \call_user_func($value, $p);
});
return $list;
}
/**
* @param array $values
* @param null $value
* @return array
*/
public function loadChoicesForValues(array $values, $value = null)
{
$choices = [];
foreach($values as $value) {
if (empty($value)) {
continue;
}
$person = $this->personRepository->find($value);
if ($this->hasCenterFilter() &&
!\in_array($person->getCenter(), $this->centers)) {
throw new \RuntimeException("chosen a person not in correct center");
}
$choices[] = $person;
}
return $choices;
}
/**
* @param array $choices
* @param null $value
* @return array|string[]
*/
public function loadValuesForChoices(array $choices, $value = null)
{
$values = [];
foreach ($choices as $choice) {
if (NULL === $choice) {
$values[] = null;
continue;
}
$id = \call_user_func($value, $choice);
$values[] = $id;
$this->lazyLoadedPersons[$id] = $choice;
}
return $values;
}
}

View File

@@ -1,76 +1,56 @@
<?php
/*
*
* Copyright (C) 2014-2020, 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/>.
*/
namespace Chill\PersonBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\PersonBundle\Form\Type\ClosingMotivePickerType;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
/**
* Class ClosingMotiveType
* Chill is a software for social workers.
*
* @package Chill\PersonBundle\Form
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\PersonBundle\Form\Type\ClosingMotivePickerType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ClosingMotiveType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TranslatableStringFormType::class, [
'label' => 'Nom'
'label' => 'Nom',
])
->add('active', CheckboxType::class, [
'label' => 'Actif ?',
'required' => false
'required' => false,
])
->add('ordering', NumberType::class, [
'label' => 'Ordre d\'apparition',
'required' => true,
'scale' => 5
'scale' => 5,
])
->add('parent', ClosingMotivePickerType::class, [
'label' => 'Parent',
'required' => false,
'placeholder' => 'closing_motive.any parent',
'multiple' => false,
'only_leaf' => false
])
;
'only_leaf' => false,
]);
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('class', ClosingMotive::class)
;
->setDefault('class', ClosingMotive::class);
}
}

View File

@@ -1,152 +1,138 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Chill\MainBundle\Form\Type\CenterType;
use Chill\PersonBundle\Form\Type\GenderType;
use Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Form\Type\GenderType;
use Chill\PersonBundle\Form\Type\PersonAltNameType;
use DateTime;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CreationPersonType extends AbstractType
{
public const FORM_BEING_REVIEWED = 'being_reviewed';
const NAME = 'chill_personbundle_person_creation';
public const FORM_NOT_REVIEWED = 'not_reviewed';
const FORM_NOT_REVIEWED = 'not_reviewed';
const FORM_REVIEWED = 'reviewed' ;
const FORM_BEING_REVIEWED = 'being_reviewed';
public const FORM_REVIEWED = 'reviewed';
public const NAME = 'chill_personbundle_person_creation';
/**
*
* @var CenterTransformer
*/
private $centerTransformer;
/**
*
* @var ConfigPersonAltNamesHelper
*/
protected $configPersonAltNamesHelper;
/**
* @var CenterTransformer
*/
private $centerTransformer;
public function __construct(
CenterTransformer $centerTransformer,
ConfigPersonAltNamesHelper $configPersonAltNamesHelper
) {
) {
$this->centerTransformer = $centerTransformer;
$this->configPersonAltNamesHelper = $configPersonAltNamesHelper;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['form_status'] === self::FORM_BEING_REVIEWED) {
if (self::FORM_BEING_REVIEWED === $options['form_status']) {
$dateToStringTransformer = new DateTimeToStringTransformer(
null, null, 'd-m-Y', false);
null,
null,
'd-m-Y',
false
);
$builder->add('firstName', HiddenType::class)
->add('lastName', HiddenType::class)
->add('birthdate', HiddenType::class, array(
'property_path' => 'birthdate'
))
->add('gender', HiddenType::class)
->add('creation_date', HiddenType::class, array(
'mapped' => false
))
->add('form_status', HiddenType::class, array(
'mapped' => false,
'data' => $options['form_status']
))
->add('center', HiddenType::class)
;
->add('lastName', HiddenType::class)
->add('birthdate', HiddenType::class, [
'property_path' => 'birthdate',
])
->add('gender', HiddenType::class)
->add('creation_date', HiddenType::class, [
'mapped' => false,
])
->add('form_status', HiddenType::class, [
'mapped' => false,
'data' => $options['form_status'],
])
->add('center', HiddenType::class);
if ($this->configPersonAltNamesHelper->hasAltNames()) {
$builder->add('altNames', PersonAltNameType::class, [
'by_reference' => false,
'force_hidden' => true
$builder->add('altNames', PersonAltNameType::class, [
'by_reference' => false,
'force_hidden' => true,
]);
}
$builder->get('birthdate')
->addModelTransformer($dateToStringTransformer);
->addModelTransformer($dateToStringTransformer);
$builder->get('creation_date')
->addModelTransformer($dateToStringTransformer);
->addModelTransformer($dateToStringTransformer);
$builder->get('center')
->addModelTransformer($this->centerTransformer);
->addModelTransformer($this->centerTransformer);
} else {
$builder
->add('firstName')
->add('lastName')
->add('birthdate', DateType::class, array('required' => false,
'widget' => 'single_text', 'format' => 'dd-MM-yyyy'))
->add('gender', GenderType::class, array(
'required' => true, 'placeholder' => null
))
->add('creation_date', DateType::class, array(
->add('birthdate', DateType::class, ['required' => false,
'widget' => 'single_text', 'format' => 'dd-MM-yyyy', ])
->add('gender', GenderType::class, [
'required' => true, 'placeholder' => null,
])
->add('creation_date', DateType::class, [
'required' => true,
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
'mapped' => false,
'data' => new \DateTime()))
->add('form_status', HiddenType::class, array(
'data' => new DateTime(), ])
->add('form_status', HiddenType::class, [
'data' => $options['form_status'],
'mapped' => false
))
->add('center', CenterType::class)
;
'mapped' => false,
])
->add('center', CenterType::class);
if ($this->configPersonAltNamesHelper->hasAltNames()) {
$builder->add('altNames', PersonAltNameType::class, [
'by_reference' => false
$builder->add('altNames', PersonAltNameType::class, [
'by_reference' => false,
]);
}
}
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Chill\PersonBundle\Entity\Person'
));
$resolver->setDefaults([
'data_class' => 'Chill\PersonBundle\Entity\Person',
]);
$resolver->setRequired('form_status')
->setAllowedValues('form_status', array(
self::FORM_BEING_REVIEWED,
self::FORM_NOT_REVIEWED,
self::FORM_REVIEWED
));
->setAllowedValues('form_status', [
self::FORM_BEING_REVIEWED,
self::FORM_NOT_REVIEWED,
self::FORM_REVIEWED,
]);
}
/**

View File

@@ -1,17 +1,27 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\DataMapper;
use Symfony\Component\Form\DataMapperInterface;
use Chill\PersonBundle\Entity\PersonAltName;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Chill\PersonBundle\Entity\PersonAltName;
/**
*
*
*/
use function array_key_exists;
use function is_array;
class PersonAltNameDataMapper implements DataMapperInterface
{
public function mapDataToForms($viewData, $forms)
@@ -19,26 +29,26 @@ class PersonAltNameDataMapper implements DataMapperInterface
if (null === $viewData) {
return;
}
if (!$viewData instanceof Collection) {
throw new UnexpectedTypeException($viewData, Collection::class);
}
$mapIndexToKey = [];
foreach ($viewData->getIterator() as $key => $altName) {
/** @var PersonAltName $altName */
$mapIndexToKey[$altName->getKey()] = $key;
}
foreach ($forms as $key => $form) {
if (\array_key_exists($key, $mapIndexToKey)) {
if (array_key_exists($key, $mapIndexToKey)) {
$form->setData($viewData->get($mapIndexToKey[$key])->getLabel());
}
}
}
/**
*
* @param FormInterface[] $forms
* @param Collection $viewData
*/
@@ -52,16 +62,16 @@ class PersonAltNameDataMapper implements DataMapperInterface
$dataIterator = $viewData instanceof ArrayCollection ?
$viewData->toArray() : $viewData->getIterator();
}
foreach ($dataIterator as $key => $altName) {
/** @var PersonAltName $altName */
$mapIndexToKey[$altName->getKey()] = $key;
}
foreach ($forms as $key => $form) {
$isEmpty = empty($form->getData());
if (\array_key_exists($key, $mapIndexToKey)) {
if (array_key_exists($key, $mapIndexToKey)) {
if ($isEmpty) {
$viewData->remove($mapIndexToKey[$key]);
} else {
@@ -71,8 +81,7 @@ class PersonAltNameDataMapper implements DataMapperInterface
if (!$isEmpty) {
$altName = (new PersonAltName())
->setKey($key)
->setLabel($form->getData())
;
->setLabel($form->getData());
if (is_array($viewData)) {
$viewData[] = $altName;
@@ -82,6 +91,5 @@ class PersonAltNameDataMapper implements DataMapperInterface
}
}
}
}
}

View File

@@ -1,11 +1,22 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\DataTransformer;
use Chill\PersonBundle\Entity\Person;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Persistence\ObjectManager;
use Chill\PersonBundle\Entity\Person;
class PersonToIdTransformer implements DataTransformerInterface
{
@@ -14,37 +25,19 @@ class PersonToIdTransformer implements DataTransformerInterface
*/
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
/**
* Transforms an object (issue) to a string (id).
*
* @param Person|null $issue
* @return string
*/
public function transform($issue)
{
if (null === $issue) {
return "";
}
return $issue->getId();
}
/**
* Transforms a string (id) to an object (issue).
*
* @param string $id
*
* @return Person|null
*
* @throws TransformationFailedException if object (issue) is not found.
*
* @return Person|null
*/
public function reverseTransform($id)
{
@@ -54,8 +47,7 @@ class PersonToIdTransformer implements DataTransformerInterface
$issue = $this->om
->getRepository('ChillPersonBundle:Person')
->findOneBy(array('id' => $id))
;
->findOneBy(['id' => $id]);
if (null === $issue) {
throw new TransformationFailedException(sprintf(
@@ -66,5 +58,20 @@ class PersonToIdTransformer implements DataTransformerInterface
return $issue;
}
/**
* Transforms an object (issue) to a string (id).
*
* @param Person|null $issue
*
* @return string
*/
public function transform($issue)
{
if (null === $issue) {
return '';
}
return $issue->getId();
}
}
?>

View File

@@ -1,45 +1,41 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form;
use Symfony\Component\Form\AbstractType;
use Chill\PersonBundle\Entity\MaritalStatus;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Chill\PersonBundle\Entity\MaritalStatus;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class MaritalStatusType
*
* @package Chill\PersonBundle\Form
*/
class MaritalStatusType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', TextType::class, [
'label' => 'Identifiant'
'label' => 'Identifiant',
])
->add('name', TranslatableStringFormType::class, [
'label' => 'Nom'
])
;
'label' => 'Nom',
]);
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('class', MaritalStatus::class)
;
->setDefault('class', MaritalStatus::class);
}
}
}

View File

@@ -1,5 +1,16 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form;
use Symfony\Component\Form\AbstractType;
@@ -8,17 +19,13 @@ use Symfony\Component\Form\FormBuilderInterface;
class PersonConfimDuplicateType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('confirm', CheckboxType::class, [
'label' => 'I confirm the merger of these 2 people',
'mapped' => false,
]);
->add('confirm', CheckboxType::class, [
'label' => 'I confirm the merger of these 2 people',
'mapped' => false,
]);
}
/**

View File

@@ -1,19 +1,25 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form;
use Chill\PersonBundle\Form\Type\PickPersonType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
class PersonFindManuallyDuplicateType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
@@ -23,8 +29,7 @@ class PersonFindManuallyDuplicateType extends AbstractType
])
->add('direction', HiddenType::class, [
'data' => 'starting',
])
;
]);
}
/**

View File

@@ -1,24 +1,16 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form;
use Chill\CustomFieldsBundle\Form\Type\CustomFieldType;
@@ -27,10 +19,10 @@ use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\Select2CountryType;
use Chill\MainBundle\Form\Type\Select2LanguageType;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\PersonPhone;
use Chill\PersonBundle\Form\Type\GenderType;
use Chill\PersonBundle\Form\Type\PersonAltNameType;
use Chill\PersonBundle\Form\Type\PersonPhoneType;
use Chill\PersonBundle\Entity\PersonPhone;
use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
@@ -50,16 +42,14 @@ class PersonType extends AbstractType
*
* @var string[]
*/
protected $config = array();
protected $config = [];
/**
*
* @var ConfigPersonAltNamesHelper
*/
protected $configAltNamesHelper;
/**
*
* @param string[] $personFieldsConfiguration configuration of visibility of some fields
*/
public function __construct(
@@ -70,46 +60,41 @@ class PersonType extends AbstractType
$this->configAltNamesHelper = $configAltNamesHelper;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstName')
->add('lastName')
->add('birthdate', DateType::class, array('required' => false, 'widget' => 'single_text', 'format' => 'dd-MM-yyyy'))
->add('gender', GenderType::class, array(
'required' => true
));
->add('birthdate', DateType::class, ['required' => false, 'widget' => 'single_text', 'format' => 'dd-MM-yyyy'])
->add('gender', GenderType::class, [
'required' => true,
]);
if ($this->configAltNamesHelper->hasAltNames()) {
$builder->add('altNames', PersonAltNameType::class, [
'by_reference' => false
'by_reference' => false,
]);
}
if ($this->config['memo'] === 'visible') {
if ('visible' === $this->config['memo']) {
$builder
->add('memo', ChillTextareaType::class, array('required' => false))
;
->add('memo', ChillTextareaType::class, ['required' => false]);
}
if ($this->config['place_of_birth'] === 'visible') {
$builder->add('placeOfBirth', TextType::class, array('required' => false));
if ('visible' === $this->config['place_of_birth']) {
$builder->add('placeOfBirth', TextType::class, ['required' => false]);
}
if ($this->config['contact_info'] === 'visible') {
$builder->add('contactInfo', ChillTextareaType::class, array('required' => false));
if ('visible' === $this->config['contact_info']) {
$builder->add('contactInfo', ChillTextareaType::class, ['required' => false]);
}
if ($this->config['phonenumber'] === 'visible') {
$builder->add('phonenumber', TelType::class, array('required' => false));
if ('visible' === $this->config['phonenumber']) {
$builder->add('phonenumber', TelType::class, ['required' => false]);
}
if ($this->config['mobilenumber'] === 'visible') {
$builder->add('mobilenumber', TelType::class, array('required' => false));
if ('visible' === $this->config['mobilenumber']) {
$builder->add('mobilenumber', TelType::class, ['required' => false]);
}
$builder->add('otherPhoneNumbers', ChillCollectionType::class, [
@@ -121,46 +106,48 @@ class PersonType extends AbstractType
'allow_delete' => true,
'by_reference' => false,
'label' => false,
'delete_empty' => function(PersonPhone $pp = null) {
return NULL === $pp || $pp->isEmpty();
'delete_empty' => static function (?PersonPhone $pp = null) {
return null === $pp || $pp->isEmpty();
},
'error_bubbling' => false
'error_bubbling' => false,
]);
if ($this->config['email'] === 'visible') {
$builder->add('email', EmailType::class, array('required' => false));
if ('visible' === $this->config['email']) {
$builder->add('email', EmailType::class, ['required' => false]);
}
if ($this->config['country_of_birth'] === 'visible') {
$builder->add('countryOfBirth', Select2CountryType::class, array(
'required' => false
));
}
if ($this->config['nationality'] === 'visible') {
$builder->add('nationality', Select2CountryType::class, array(
'required' => false
));
}
if ($this->config['spoken_languages'] === 'visible') {
$builder->add('spokenLanguages', Select2LanguageType::class, array(
if ('visible' === $this->config['country_of_birth']) {
$builder->add('countryOfBirth', Select2CountryType::class, [
'required' => false,
'multiple' => true
));
]);
}
if ($this->config['marital_status'] === 'visible'){
$builder->add('maritalStatus', Select2MaritalStatusType::class, array(
'required' => false
));
if ('visible' === $this->config['nationality']) {
$builder->add('nationality', Select2CountryType::class, [
'required' => false,
]);
}
if($options['cFGroup']) {
if ('visible' === $this->config['spoken_languages']) {
$builder->add('spokenLanguages', Select2LanguageType::class, [
'required' => false,
'multiple' => true,
]);
}
if ('visible' === $this->config['marital_status']) {
$builder->add('maritalStatus', Select2MaritalStatusType::class, [
'required' => false,
]);
}
if ($options['cFGroup']) {
$builder
->add('cFData', CustomFieldType::class,
array('attr' => array('class' => 'cf-fields'), 'group' => $options['cFGroup']))
;
->add(
'cFData',
CustomFieldType::class,
['attr' => ['class' => 'cf-fields'], 'group' => $options['cFGroup']]
);
}
}
@@ -169,17 +156,18 @@ class PersonType extends AbstractType
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
$resolver->setDefaults([
'data_class' => 'Chill\PersonBundle\Entity\Person',
'validation_groups' => array('general', 'creation'),
));
'validation_groups' => ['general', 'creation'],
]);
$resolver->setRequired(array(
'cFGroup'
));
$resolver->setRequired([
'cFGroup',
]);
$resolver->setAllowedTypes(
'cFGroup', array('null', 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup')
'cFGroup',
['null', 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup']
);
}

View File

@@ -1,32 +1,33 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension;
use Symfony\Component\OptionsResolver\Options;
use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class ClosingMotivePickerType
* A type to add a closing motive
*
* @package Chill\PersonBundle\Form\Type
* A type to add a closing motive.
*/
class ClosingMotivePickerType extends AbstractType
{
/**
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
* @var ChillEntityRenderExtension
*/
@@ -37,12 +38,13 @@ class ClosingMotivePickerType extends AbstractType
*/
protected $repository;
/**
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
* ClosingMotivePickerType constructor.
*
* @param TranslatableStringHelper $translatableStringHelper
* @param ChillEntityRenderExtension $chillEntityRenderExtension
* @param ClosingMotiveRepository $closingMotiveRepository
*/
public function __construct(
TranslatableStringHelper $translatableStringHelper,
@@ -53,7 +55,27 @@ class ClosingMotivePickerType extends AbstractType
$this->entityRenderExtension = $chillEntityRenderExtension;
$this->repository = $closingMotiveRepository;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => ClosingMotive::class,
'empty_data' => null,
'placeholder' => 'Choose a motive',
'choice_label' => function (ClosingMotive $cm) {
return $this->entityRenderExtension->renderString($cm);
},
'only_leaf' => true,
]);
$resolver
->setAllowedTypes('only_leaf', 'bool')
->setNormalizer('choices', function (Options $options) {
return $this->repository
->getActiveClosingMotive($options['only_leaf']);
});
}
/**
* @return string
*/
@@ -61,38 +83,12 @@ class ClosingMotivePickerType extends AbstractType
{
return 'closing_motive';
}
/**
* @return null|string
* @return string|null
*/
public function getParent()
{
return EntityType::class;
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => ClosingMotive::class,
'empty_data' => null,
'placeholder' => 'Choose a motive',
'choice_label' => function(ClosingMotive $cm) {
return $this->entityRenderExtension->renderString($cm);
},
'only_leaf' => true
]);
$resolver
->setAllowedTypes('only_leaf', 'bool')
->setNormalizer('choices', function (Options $options) {
return $this->repository
->getActiveClosingMotive($options['only_leaf']);
})
;
}
}

View File

@@ -1,38 +1,46 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* A type to select the civil union state
*
* @author julien
* A type to select the civil union state.
*/
class GenderType extends AbstractType {
public function getParent() {
return ChoiceType::class;
}
public function configureOptions(OptionsResolver $resolver) {
$a = array(
class GenderType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$a = [
Person::MALE_GENDER => Person::MALE_GENDER,
Person::FEMALE_GENDER => Person::FEMALE_GENDER,
Person::BOTH_GENDER => Person::BOTH_GENDER
);
Person::BOTH_GENDER => Person::BOTH_GENDER,
];
$resolver->setDefaults(array(
$resolver->setDefaults([
'choices' => $a,
'expanded' => true,
'multiple' => false,
'placeholder' => null
));
'placeholder' => null,
]);
}
public function getParent()
{
return ChoiceType::class;
}
}

View File

@@ -1,35 +1,40 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
*
*
*/
class PersonAltNameType extends AbstractType
{
/**
*
* @var ConfigPersonAltNamesHelper
*/
private $configHelper;
/**
*
* @var TranslatableStringHelper
*/
private $translatableStringHelper;
public function __construct(
ConfigPersonAltNamesHelper $configHelper,
ConfigPersonAltNamesHelper $configHelper,
TranslatableStringHelper $translatableStringHelper
) {
$this->configHelper = $configHelper;
@@ -40,28 +45,17 @@ class PersonAltNameType extends AbstractType
{
foreach ($this->getKeyChoices() as $label => $key) {
$builder->add(
$key,
$options['force_hidden'] ? HiddenType::class : TextType::class, [
'label' => $label,
'required' => false
]);
$key,
$options['force_hidden'] ? HiddenType::class : TextType::class,
[
'label' => $label,
'required' => false,
]
);
}
$builder->setDataMapper(new \Chill\PersonBundle\Form\DataMapper\PersonAltNameDataMapper());
}
protected function getKeyChoices()
{
$choices = $this->configHelper->getChoices();
$translatedChoices = [];
foreach ($choices as $key => $labels) {
$label = $this->translatableStringHelper->localize($labels);
$translatedChoices[$label] = $key;
}
return $translatedChoices;
}
public function configureOptions(OptionsResolver $resolver)
{
@@ -69,8 +63,19 @@ class PersonAltNameType extends AbstractType
->setDefault('class', \Chill\PersonBundle\Entity\PersonAltName::class)
->setDefined('force_hidden')
->setAllowedTypes('force_hidden', 'bool')
->setDefault('force_hidden', false)
;
->setDefault('force_hidden', false);
}
protected function getKeyChoices()
{
$choices = $this->configHelper->getChoices();
$translatedChoices = [];
foreach ($choices as $key => $labels) {
$label = $this->translatableStringHelper->localize($labels);
$translatedChoices[$label] = $key;
}
return $translatedChoices;
}
}

View File

@@ -1,5 +1,16 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type;
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
@@ -11,16 +22,14 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PersonPhoneType extends AbstractType
{
private PhonenumberHelper $phonenumberHelper;
private EntityManagerInterface $em;
private PhonenumberHelper $phonenumberHelper;
public function __construct(PhonenumberHelper $phonenumberHelper, EntityManagerInterface $em)
{
$this->phonenumberHelper = $phonenumberHelper;
@@ -38,11 +47,11 @@ class PersonPhoneType extends AbstractType
'required' => false,
]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
if (NULL === $event->getData()) {
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
if (null === $event->getData()) {
return;
}
$oldPersonPhone = $this->em->getUnitOfWork()
->getOriginalEntityData($event->getData());
@@ -59,7 +68,6 @@ class PersonPhoneType extends AbstractType
->setDefaults([
'data_class' => PersonPhone::class,
'validation_groups' => ['general', 'creation'],
])
;
]);
}
}

View File

@@ -1,41 +1,38 @@
<?php
/*
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\GroupCenter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Form\ChoiceLoader\PersonChoiceLoader;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Search\PersonSearch;
use RuntimeException;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Chill\MainBundle\Entity\GroupCenter;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Entity\Center;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Search\PersonSearch;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\PersonBundle\Form\ChoiceLoader\PersonChoiceLoader;
use Symfony\Component\OptionsResolver\Options;
use function in_array;
use function is_array;
/**
* This type allow to pick a person.
@@ -49,49 +46,41 @@ use Symfony\Component\OptionsResolver\Options;
* `Chill\MainBundle\Entity\Center`. By default, all the reachable centers as selected.
* - with the `role` option, only the people belonging to the reachable center for the
* given role are displayed.
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class PickPersonType extends AbstractType
{
/**
* @var PersonRepository
*/
protected $personRepository;
/**
*
* @var \Chill\MainBundle\Entity\User
*/
protected $user;
/**
*
* @var AuthorizationHelper
*/
protected $authorizationHelper;
/**
*
* @var UrlGeneratorInterface
* @var PersonRepository
*/
protected $urlGenerator;
protected $personRepository;
/**
*
* @var TranslatorInterface
*/
protected $translator;
/**
* @var UrlGeneratorInterface
*/
protected $urlGenerator;
/**
* @var \Chill\MainBundle\Entity\User
*/
protected $user;
public function __construct(
PersonRepository $personRepository,
TokenStorageInterface $tokenStorage,
AuthorizationHelper $authorizationHelper,
UrlGeneratorInterface $urlGenerator,
TranslatorInterface $translator
)
{
PersonRepository $personRepository,
TokenStorageInterface $tokenStorage,
AuthorizationHelper $authorizationHelper,
UrlGeneratorInterface $urlGenerator,
TranslatorInterface $translator
) {
$this->personRepository = $personRepository;
$this->user = $tokenStorage->getToken()->getUser();
$this->authorizationHelper = $authorizationHelper;
@@ -99,42 +88,16 @@ class PickPersonType extends AbstractType
$this->translator = $translator;
}
protected function filterCentersfom(Options $options)
public function buildView(\Symfony\Component\Form\FormView $view, \Symfony\Component\Form\FormInterface $form, array $options)
{
if ($options['role'] === NULL) {
$centers = array_map(function (GroupCenter $g) {
return $g->getCenter();
}, $this->user->getGroupCenters()->toArray());
} else {
$centers = $this->authorizationHelper
->getReachableCenters($this->user, $options['role']);
}
if ($options['centers'] === NULL) {
// we select all selected centers
$selectedCenters = $centers;
} else {
$selectedCenters = array();
$optionsCenters = is_array($options['centers']) ?
$options['centers'] : array($options['centers']);
foreach ($optionsCenters as $c) {
// check that every member of the array is a center
if (!$c instanceof Center) {
throw new \RuntimeException('Every member of the "centers" '
. 'option must be an instance of '.Center::class);
}
if (!in_array($c->getId(), array_map(
function(Center $c) { return $c->getId();},
$centers))) {
throw new AccessDeniedException('The given center is not reachable');
}
$selectedCenters[] = $c;
}
}
return $selectedCenters;
$view->vars['attr']['data-person-picker'] = true;
$view->vars['attr']['data-select-interactive-loading'] = true;
$view->vars['attr']['data-search-url'] = $this->urlGenerator
->generate('chill_main_search', ['name' => PersonSearch::NAME, '_format' => 'json']);
$view->vars['attr']['data-placeholder'] = $this->translator->trans($options['placeholder']);
$view->vars['attr']['data-no-results-label'] = $this->translator->trans('select2.no_results');
$view->vars['attr']['data-error-load-label'] = $this->translator->trans('select2.error_loading');
$view->vars['attr']['data-searching-label'] = $this->translator->trans('select2.searching');
}
public function configureOptions(OptionsResolver $resolver)
@@ -143,49 +106,78 @@ class PickPersonType extends AbstractType
// add the possibles options for this type
$resolver->setDefined('centers')
->addAllowedTypes('centers', array('array', Center::class, 'null'))
->setDefault('centers', null)
->setDefined('role')
->addAllowedTypes('role', array(Role::class, 'null'))
->setDefault('role', null)
;
->addAllowedTypes('centers', ['array', Center::class, 'null'])
->setDefault('centers', null)
->setDefined('role')
->addAllowedTypes('role', [Role::class, 'null'])
->setDefault('role', null);
// add the default options
$resolver->setDefaults(array(
$resolver->setDefaults([
'class' => Person::class,
'choice_label' => function(Person $p) {
return $p->getFirstname().' '.$p->getLastname();
'choice_label' => static function (Person $p) {
return $p->getFirstname() . ' ' . $p->getLastname();
},
'placeholder' => 'Pick a person',
'choice_attr' => function(Person $p) {
return array(
'data-center' => $p->getCenter()->getId()
);
'choice_attr' => static function (Person $p) {
return [
'data-center' => $p->getCenter()->getId(),
];
},
'attr' => array('class' => 'select2 '),
'choice_loader' => function(Options $options) {
'attr' => ['class' => 'select2 '],
'choice_loader' => function (Options $options) {
$centers = $this->filterCentersfom($options);
return new PersonChoiceLoader($this->personRepository, $centers);
}
));
},
]);
}
public function getParent()
{
return EntityType::class;
}
public function buildView(\Symfony\Component\Form\FormView $view, \Symfony\Component\Form\FormInterface $form, array $options)
{
$view->vars['attr']['data-person-picker'] = true;
$view->vars['attr']['data-select-interactive-loading'] = true;
$view->vars['attr']['data-search-url'] = $this->urlGenerator
->generate('chill_main_search', [ 'name' => PersonSearch::NAME, '_format' => 'json' ]);
$view->vars['attr']['data-placeholder'] = $this->translator->trans($options['placeholder']);
$view->vars['attr']['data-no-results-label'] = $this->translator->trans('select2.no_results');
$view->vars['attr']['data-error-load-label'] = $this->translator->trans('select2.error_loading');
$view->vars['attr']['data-searching-label'] = $this->translator->trans('select2.searching');
}
protected function filterCentersfom(Options $options)
{
if (null === $options['role']) {
$centers = array_map(static function (GroupCenter $g) {
return $g->getCenter();
}, $this->user->getGroupCenters()->toArray());
} else {
$centers = $this->authorizationHelper
->getReachableCenters($this->user, $options['role']);
}
if (null === $options['centers']) {
// we select all selected centers
$selectedCenters = $centers;
} else {
$selectedCenters = [];
$optionsCenters = is_array($options['centers']) ?
$options['centers'] : [$options['centers']];
foreach ($optionsCenters as $c) {
// check that every member of the array is a center
if (!$c instanceof Center) {
throw new RuntimeException('Every member of the "centers" '
. 'option must be an instance of ' . Center::class);
}
if (
!in_array($c->getId(), array_map(
static function (Center $c) {
return $c->getId();
},
$centers
), true)
) {
throw new AccessDeniedException('The given center is not reachable');
}
$selectedCenters[] = $c;
}
}
return $selectedCenters;
}
}

View File

@@ -1,63 +1,53 @@
<?php
/*
* Chill is a software for social workers
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\MainBundle\Form\Type\DataTransformer\ObjectToIdTransformer;
use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use const SORT_FLAG_CASE;
use const SORT_STRING;
/**
* A type to select the marital status
*
* @author Champs-Libres COOP
* A type to select the marital status.
*/
class Select2MaritalStatusType extends AbstractType
{
/** @var RequestStack */
private $requestStack;
/** @var ObjectManager */
/**
* @var ObjectManager
*/
private $em;
public function __construct(RequestStack $requestStack,ObjectManager $em)
/**
* @var RequestStack
*/
private $requestStack;
public function __construct(RequestStack $requestStack, ObjectManager $em)
{
$this->requestStack = $requestStack;
$this->em = $em;
}
public function getBlockPrefix() {
return 'select2_chill_marital_status';
}
public function getParent() {
return Select2ChoiceType::class;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new ObjectToIdTransformer($this->em,'Chill\PersonBundle\Entity\MaritalStatus');
$transformer = new ObjectToIdTransformer($this->em, 'Chill\PersonBundle\Entity\MaritalStatus');
$builder->addModelTransformer($transformer);
}
@@ -65,7 +55,7 @@ class Select2MaritalStatusType extends AbstractType
{
$locale = $this->requestStack->getCurrentRequest()->getLocale();
$maritalStatuses = $this->em->getRepository('Chill\PersonBundle\Entity\MaritalStatus')->findAll();
$choices = array();
$choices = [];
foreach ($maritalStatuses as $ms) {
$choices[$ms->getId()] = $ms->getName()[$locale];
@@ -73,9 +63,19 @@ class Select2MaritalStatusType extends AbstractType
asort($choices, SORT_STRING | SORT_FLAG_CASE);
$resolver->setDefaults(array(
'class' => 'Chill\PersonBundle\Entity\MaritalStatus',
'choices' => array_combine(array_values($choices),array_keys($choices))
));
$resolver->setDefaults([
'class' => 'Chill\PersonBundle\Entity\MaritalStatus',
'choices' => array_combine(array_values($choices), array_keys($choices)),
]);
}
public function getBlockPrefix()
{
return 'select2_chill_marital_status';
}
public function getParent()
{
return Select2ChoiceType::class;
}
}

View File

@@ -1,58 +1,60 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Class AccompanyingCourseMenuBuilder
*
* @package Chill\PersonBundle\Menu
* @author mathieu.jaumotte@champs-libres.coop
*/
class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
{
/**
* @var TranslatorInterface
*/
protected $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public static function getMenuIds(): array
{
return [ 'accompanyingCourse' ];
}
public function buildMenu($menuId, MenuItem $menu, array $parameters): void
{
$menu->addChild($this->translator->trans('Resume Accompanying Course'), [
'route' => 'chill_person_accompanying_course_index',
'routeParameters' => [
'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
]])
->setExtras(['order' => 10]);
'accompanying_period_id' => $parameters['accompanyingCourse']->getId(),
], ])
->setExtras(['order' => 10]);
$menu->addChild($this->translator->trans('Edit Accompanying Course'), [
'route' => 'chill_person_accompanying_course_show',
'routeParameters' => [
'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
]])
->setExtras(['order' => 20]);
'accompanying_period_id' => $parameters['accompanyingCourse']->getId(),
], ])
->setExtras(['order' => 20]);
$menu->addChild($this->translator->trans('Accompanying Course Details'), [
'route' => 'chill_person_accompanying_course_history',
'routeParameters' => [
'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
]])
->setExtras(['order' => 30]);
'accompanying_period_id' => $parameters['accompanyingCourse']->getId(),
], ])
->setExtras(['order' => 30]);
}
public static function getMenuIds(): array
{
return ['accompanyingCourse'];
}
}

View File

@@ -1,61 +1,50 @@
<?php
/*
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
/**
*
*
*/
class AdminMenuBuilder implements LocalMenuBuilderInterface
{
/**
*
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
public function __construct(AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
return;
}
$menu->addChild('Person', [
'route' => 'chill_person_admin'
])
'route' => 'chill_person_admin',
])
->setExtras([
'order' => 20
'order' => 20,
]);
}
public static function getMenuIds(): array
{
return [ 'admin_section' ];
return ['admin_section'];
}
}

View File

@@ -1,20 +1,16 @@
<?php
/*
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
@@ -23,27 +19,24 @@ use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Add menu entrie to person menu.
*
*
* Menu entries added :
*
*
* - person details ;
* - accompanying period (if `visible`)
*
*/
class PersonMenuBuilder implements LocalMenuBuilderInterface
{
/**
*
* @var string 'visible' or 'hidden'
*/
protected $showAccompanyingPeriod;
/**
*
* @var TranslatorInterface
*/
protected $translator;
public function __construct(
$showAccompanyingPeriod,
TranslatorInterface $translator
@@ -51,44 +44,44 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
$this->showAccompanyingPeriod = $showAccompanyingPeriod;
$this->translator = $translator;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
$menu->addChild($this->translator->trans('Person details'), [
'route' => 'chill_person_view',
'routeParameters' => [
'person_id' => $parameters['person']->getId()
]
])
'route' => 'chill_person_view',
'routeParameters' => [
'person_id' => $parameters['person']->getId(),
],
])
->setExtras([
'order' => 50
'order' => 50,
]);
$menu->addChild($this->translator->trans('Person duplicate'), [
'route' => 'chill_person_duplicate_view',
'routeParameters' => [
'person_id' => $parameters['person']->getId()
]
'route' => 'chill_person_duplicate_view',
'routeParameters' => [
'person_id' => $parameters['person']->getId(),
],
])
->setExtras([
'order' => 51
]);
if ($this->showAccompanyingPeriod === 'visible') {
->setExtras([
'order' => 51,
]);
if ('visible' === $this->showAccompanyingPeriod) {
$menu->addChild($this->translator->trans('Accompanying period list'), [
'route' => 'chill_person_accompanying_period_list',
'routeParameters' => [
'person_id' => $parameters['person']->getId()
]
])
'route' => 'chill_person_accompanying_period_list',
'routeParameters' => [
'person_id' => $parameters['person']->getId(),
],
])
->setExtras([
'order' => 100
'order' => 100,
]);
}
}
public static function getMenuIds(): array
{
return [ 'person' ];
return ['person'];
}
}

View File

@@ -1,83 +1,63 @@
<?php
/*
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Translation\TranslatorInterface;
/**
* Class SectionMenuBuilder
*
* @package Chill\PersonBundle\Menu
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class SectionMenuBuilder implements LocalMenuBuilderInterface
{
/**
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
/**
* @var TranslatorInterface
*/
protected $translator;
/**
* SectionMenuBuilder constructor.
*
* @param AuthorizationCheckerInterface $authorizationChecker
* @param TranslatorInterface $translator
*/
public function __construct(AuthorizationCheckerInterface $authorizationChecker, TranslatorInterface $translator)
{
$this->authorizationChecker = $authorizationChecker;
$this->translator = $translator;
}
/**
* @param $menuId
* @param MenuItem $menu
* @param array $parameters
*/
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
if ($this->authorizationChecker->isGranted(PersonVoter::CREATE)) {
$menu->addChild($this->translator->trans('Add a person'), [
'route' => 'chill_person_new'
])
'route' => 'chill_person_new',
])
->setExtras([
'order' => 10,
'icons' => [ 'plus' ]
'icons' => ['plus'],
]);
}
}
/**
* @return array
*/
public static function getMenuIds(): array
{
return [ 'section' ];
return ['section'];
}
}

View File

@@ -1,104 +1,63 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Privacy;
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <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/>.
*/
use Symfony\Component\EventDispatcher\Event;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\EventDispatcher\Event;
use function count;
/**
* Class PrivacyEvent
*
* Array $args expects arguments with the following keys: 'element_class', 'element_id', 'action'
* By default, action is set to 'show'
*
* @package Chill\PersonBundle\Privacy
* By default, action is set to 'show'.
*/
class PrivacyEvent extends Event
{
const PERSON_PRIVACY_EVENT = 'chill_person.privacy_event';
/**
* @var Person
*/
private $person;
public const PERSON_PRIVACY_EVENT = 'chill_person.privacy_event';
/**
* @var array
*/
private $args;
/**
* @var Person
*/
private $person;
/**
* @var array
*/
private $persons;
/**
* PrivacyEvent constructor.
*
* @param Person $person
* @param array $args
*/
public function __construct(Person $person, array $args = array('action' => 'show'))
public function __construct(Person $person, array $args = ['action' => 'show'])
{
$this->person = $person;
$this->args = $args;
$this->persons = array();
$this->persons = [];
}
/**
* @return Person
*/
public function getPerson()
{
return $this->person;
}
/**
* @param Person $person
*/
public function addPerson(Person $person)
{
$this->persons[] = $person;
return $this;
}
/**
* @return array $persons
*/
public function getPersons()
{
return $this->persons;
}
/**
* @return bool
*/
public function hasPersons()
{
return count($this->persons) >= 1;
}
/**
* @return array
*/
@@ -106,5 +65,28 @@ class PrivacyEvent extends Event
{
return $this->args;
}
}
/**
* @return Person
*/
public function getPerson()
{
return $this->person;
}
/**
* @return array $persons
*/
public function getPersons()
{
return $this->persons;
}
/**
* @return bool
*/
public function hasPersons()
{
return count($this->persons) >= 1;
}
}

View File

@@ -1,89 +1,79 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Privacy;
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <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/>.
*/
use Chill\PersonBundle\Entity\Person;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Chill\PersonBundle\Entity\Person;
class PrivacyEventSubscriber implements EventSubscriberInterface
{
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var TokenStorageInterface
*/
protected $token;
/**
* PrivacyEventSubscriber constructor.
*
* @param LoggerInterface $logger
*/
public function __construct(LoggerInterface $logger, TokenStorageInterface $token)
{
$this->logger = $logger;
$this->token = $token;
}
public static function getSubscribedEvents()
{
return array(PrivacyEvent::PERSON_PRIVACY_EVENT => array(
array('onPrivacyEvent')
));
return [PrivacyEvent::PERSON_PRIVACY_EVENT => [
['onPrivacyEvent'],
]];
}
public function onPrivacyEvent(PrivacyEvent $event)
{
$persons = array();
$persons = [];
if ($event->hasPersons() === true) {
foreach ($event->getPersons() as $person) {
$persons[] = $person->getId();
}
}
$involved = array(
$involved = [
'by_user' => $this->token->getToken()->getUser()->getUsername(),
'by_user_id' => $this->token->getToken()->getUser()->getId(),
'person_id' => $event->getPerson()->getId(),
);
];
if ($event->hasPersons()) {
$involved['persons'] = \array_map(
function(Person $p) { return $p->getId(); },
$involved['persons'] = array_map(
static function (Person $p) {
return $p->getId();
},
$event->getPersons()
);
}
$this->logger->notice(
"[Privacy Event] A Person Folder has been viewed",
'[Privacy Event] A Person Folder has been viewed',
array_merge($involved, $event->getArgs())
);
}
}
}

View File

@@ -1,64 +1,50 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
/**
* Class ClosingMotiveRepository
* Entity repository for closing motives
*
* @package Chill\PersonBundle\Repository
* Entity repository for closing motives.
*/
class ClosingMotiveRepository extends EntityRepository
{
/**
* @param bool $onlyLeaf
* @return mixed
*/
public function getActiveClosingMotive(bool $onlyLeaf = true)
{
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
$rsm->addRootEntityFromClassMetadata($this->getClassName(), 'cm');
$sql = "SELECT ".(string) $rsm."
$sql = 'SELECT ' . (string) $rsm . '
FROM chill_person_accompanying_period_closingmotive AS cm
WHERE
active IS TRUE ";
active IS TRUE ';
if ($onlyLeaf) {
$sql .= "AND cm.id NOT IN (
$sql .= 'AND cm.id NOT IN (
SELECT DISTINCT parent_id FROM chill_person_accompanying_period_closingmotive WHERE parent_id IS NOT NULL
)";
)';
}
$sql .= " ORDER BY cm.ordering ASC";
$sql .= ' ORDER BY cm.ordering ASC';
return $this->_em
->createNativeQuery($sql, $rsm)
->getResult()
;
->getResult();
}
}

View File

@@ -1,25 +1,16 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
@@ -38,5 +29,4 @@ class CommentRepository extends ServiceEntityRepository
{
parent::__construct($registry, Comment::class);
}
}

View File

@@ -1,25 +1,16 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
@@ -38,5 +29,4 @@ class OriginRepository extends ServiceEntityRepository
{
parent::__construct($registry, Origin::class);
}
}

View File

@@ -1,25 +1,16 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
@@ -27,10 +18,10 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Resource|null find($id, $lockMode = null, $lockVersion = null)
* @method Resource|null findOneBy(array $criteria, array $orderBy = null)
* @method Resource[] findAll()
* @method Resource[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
* @method resource|null find($id, $lockMode = null, $lockVersion = null)
* @method resource|null findOneBy(array $criteria, array $orderBy = null)
* @method resource[] findAll()
* @method resource[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ResourceRepository extends ServiceEntityRepository
{
@@ -38,5 +29,4 @@ class ResourceRepository extends ServiceEntityRepository
{
parent::__construct($registry, Resource::class);
}
}

View File

@@ -1,25 +1,16 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
@@ -38,5 +29,4 @@ class AccompanyingPeriodParticipationRepository extends ServiceEntityRepository
{
parent::__construct($registry, AccompanyingPeriodParticipation::class);
}
}

View File

@@ -1,25 +1,16 @@
<?php
/*
* Chill is a software for social workers
/**
* Chill is a software for social workers.
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
@@ -38,5 +29,4 @@ class AccompanyingPeriodRepository extends ServiceEntityRepository
{
parent::__construct($registry, AccompanyingPeriod::class);
}
}

View File

@@ -1,9 +1,20 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository;
/**
* PersonAltNameRepository
* PersonAltNameRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.

View File

@@ -1,33 +1,36 @@
<?php
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonNotDuplicate;
use Doctrine\ORM\EntityRepository;
/**
* Class PersonNotDuplicateRepository
*
* @package Chill\PersonBundle\Repository
*/
class PersonNotDuplicateRepository extends EntityRepository
class PersonNotDuplicateRepository extends EntityRepository
{
/**
* @param \Chill\PersonBundle\Entity\Person $person
*
* @return array
*/
public function findNotDuplicatePerson(Person $person)
{
$qb = $this->createQueryBuilder('pnd');
$qb->select('pnd')
->where('pnd.person1 = :person OR pnd.person2 = :person')
;
->where('pnd.person1 = :person OR pnd.person2 = :person');
$qb->setParameter('person', $person);
$result = $qb->getQuery()->getResult();
$persons = [];
foreach ($result as $row) {
if ($row->getPerson1() === $person) {
$persons[] = $row->getPerson2();

View File

@@ -1,126 +1,75 @@
<?php
/*
* Copyright (C) 2019 Julien Fastré <julien.fastre@champs-libres.coop>
/**
* Chill is a software for social workers.
*
* 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.
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Exception;
use function count;
use function in_array;
/**
* Class PersonRepository
*
* @package Chill\PersonBundle\Repository
*/
class PersonRepository extends EntityRepository
{
/**
* @param string $phonenumber
* @param $centers
*
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function countByPhone(
string $phonenumber,
$centers,
array $only = ['mobile', 'phone']
): int {
$qb = $this->createQueryBuilder('p');
$qb->select('COUNT(p)');
$this->addByCenters($qb, $centers);
$this->addPhoneNumber($qb, $phonenumber, $only);
return $qb->getQuery()->getSingleScalarResult();
}
/**
* @param $centers
* @param $firstResult
* @param $maxResults
* @param array $only
*
* @throws Exception
*
* @return mixed
* @throws \Exception
*/
public function findByPhone(
string $phonenumber,
$centers,
string $phonenumber,
$centers,
$firstResult,
$maxResults,
array $only = ['mobile', 'phone']
) {
$qb = $this->createQueryBuilder('p');
$qb->select('p');
$this->addByCenters($qb, $centers);
$this->addPhoneNumber($qb, $phonenumber, $only);
$qb->setFirstResult($firstResult)
->setMaxResults($maxResults)
;
->setMaxResults($maxResults);
return $qb->getQuery()->getResult();
}
/**
* @param string $phonenumber
* @param $centers
* @param array $only
* @return int
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function countByPhone(
string $phonenumber,
$centers,
array $only = ['mobile', 'phone']
): int
{
$qb = $this->createQueryBuilder('p');
$qb->select('COUNT(p)');
$this->addByCenters($qb, $centers);
$this->addPhoneNumber($qb, $phonenumber, $only);
return $qb->getQuery()->getSingleScalarResult();
}
/**
* @param QueryBuilder $qb
* @param string $phonenumber
* @param array $only
* @throws \Exception
*/
protected function addPhoneNumber(QueryBuilder $qb, string $phonenumber, array $only)
{
if (count($only) === 0) {
throw new \Exception("No array field to search");
}
$phonenumber = $this->parsePhoneNumber($phonenumber);
$orX = $qb->expr()->orX();
if (\in_array('mobile', $only)) {
$orX->add($qb->expr()->like("REPLACE(p.mobilenumber, ' ', '')", ':phonenumber'));
}
if (\in_array('phone', $only)) {
$orX->add($qb->expr()->like("REPLACE(p.phonenumber, ' ', '')", ':phonenumber'));
}
$qb->andWhere($orX);
$qb->setParameter('phonenumber', '%'.$phonenumber.'%');
}
/**
* @param $phonenumber
* @return string
*/
protected function parsePhoneNumber($phonenumber): string
{
return \str_replace(' ', '', $phonenumber);
}
/**
* @param QueryBuilder $qb
* @param array $centers
*/
protected function addByCenters(QueryBuilder $qb, array $centers)
{
if (count($centers) > 0) {
@@ -128,4 +77,38 @@ class PersonRepository extends EntityRepository
$qb->setParameter('centers', $centers);
}
}
/**
* @throws Exception
*/
protected function addPhoneNumber(QueryBuilder $qb, string $phonenumber, array $only)
{
if (count($only) === 0) {
throw new Exception('No array field to search');
}
$phonenumber = $this->parsePhoneNumber($phonenumber);
$orX = $qb->expr()->orX();
if (in_array('mobile', $only, true)) {
$orX->add($qb->expr()->like("REPLACE(p.mobilenumber, ' ', '')", ':phonenumber'));
}
if (in_array('phone', $only, true)) {
$orX->add($qb->expr()->like("REPLACE(p.phonenumber, ' ', '')", ':phonenumber'));
}
$qb->andWhere($orX);
$qb->setParameter('phonenumber', '%' . $phonenumber . '%');
}
/**
* @param $phonenumber
*/
protected function parsePhoneNumber($phonenumber): string
{
return str_replace(' ', '', $phonenumber);
}
}

View File

@@ -1,13 +1,40 @@
<?php
use Symfony\Component\HttpKernel\Kernel;
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Kernel;
class AppKernel extends Kernel
{
/**
* @return string
*/
public function getCacheDir()
{
return sys_get_temp_dir() . '/PersonBundle/cache';
}
/**
* @return string
*/
public function getLogDir()
{
return sys_get_temp_dir() . '/PersonBundle/logs';
}
public function registerBundles()
{
return array(
return [
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Chill\CustomFieldsBundle\ChillCustomFieldsBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
@@ -19,29 +46,12 @@ class AppKernel extends Kernel
new \Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
#add here all the required bundle (some bundle are not required)
);
//add here all the required bundle (some bundle are not required)
];
}
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}
/**
* @return string
*/
public function getCacheDir()
{
return sys_get_temp_dir().'/PersonBundle/cache';
}
/**
* @return string
*/
public function getLogDir()
{
return sys_get_temp_dir().'/PersonBundle/logs';
$loader->load(__DIR__ . '/config/config_' . $this->getEnvironment() . '.yml');
}
}

View File

@@ -1,11 +1,22 @@
<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
use Composer\Autoload\ClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
/** @var ClassLoader $loader */
$loader = require __DIR__.'/../../../../../vendor/autoload.php';
$loader = require __DIR__ . '/../../../../../vendor/autoload.php';
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
return $loader;

View File

@@ -1,7 +1,18 @@
<?php
use Symfony\Component\HttpFoundation\Request;
/**
* Chill is a software for social workers.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
use Symfony\Component\Debug\Debug;
use Symfony\Component\HttpFoundation\Request;
// If you don't want to setup permissions the proper way, just uncomment the following PHP line
// read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information
@@ -9,18 +20,20 @@ use Symfony\Component\Debug\Debug;
// This check prevents access to debug front controllers that are deployed by accident to production servers.
// Feel free to remove this, extend it, or make something more sophisticated.
if (isset($_SERVER['HTTP_CLIENT_IP'])
if (
isset($_SERVER['HTTP_CLIENT_IP'])
|| isset($_SERVER['HTTP_X_FORWARDED_FOR'])
|| !(in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', 'fe80::1', '::1')) || php_sapi_name() === 'cli-server')
|| !(\in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', 'fe80::1', '::1'], true) || \PHP_SAPI === 'cli-server')
) {
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
exit('You are not allowed to access this file. Check ' . basename(__FILE__) . ' for more information.');
}
$loader = require_once __DIR__.'/../app/bootstrap.php.cache';
$loader = require_once __DIR__ . '/../app/bootstrap.php.cache';
Debug::enable();
require_once __DIR__.'/../app/AppKernel.php';
require_once __DIR__ . '/../app/AppKernel.php';
$kernel = new AppKernel('dev', true);
$kernel->loadClassCache();

View File

@@ -1,80 +1,77 @@
<?php
/*
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Search;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Search\AbstractSearch;
use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
use Chill\MainBundle\Search\ParsingException;
use Chill\MainBundle\Search\SearchInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\Person;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Exception;
use LogicException;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Chill\MainBundle\Search\ParsingException;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Security\Core\Role\Role;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
use Doctrine\ORM\Query;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role;
use function array_key_exists;
use function in_array;
class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
class PersonSearch extends AbstractSearch implements
ContainerAwareInterface,
HasAdvancedSearchFormInterface
{
use ContainerAwareTrait;
public const NAME = 'person_regular';
/**
* @var PaginatorFactory
*/
protected $paginatorFactory;
private $_cacheQuery = [];
/**
*
* @var EntityManagerInterface
*/
private $em;
/**
*
* @var \Chill\MainBundle\Entity\User
*/
private $user;
/**
*
* @var AuthorizationHelper
*/
private $helper;
/**
*
* @var PaginatorFactory
* @var \Chill\MainBundle\Entity\User
*/
protected $paginatorFactory;
const NAME = "person_regular";
private $user;
public function __construct(
EntityManagerInterface $em,
TokenStorageInterface $tokenStorage,
AuthorizationHelper $helper,
PaginatorFactory $paginatorFactory)
{
EntityManagerInterface $em,
TokenStorageInterface $tokenStorage,
AuthorizationHelper $helper,
PaginatorFactory $paginatorFactory
) {
$this->em = $em;
$this->user = $tokenStorage->getToken()->getUser();
$this->helper = $helper;
@@ -82,13 +79,211 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
// throw an error if user is not a valid user
if (!$this->user instanceof \Chill\MainBundle\Entity\User) {
throw new \LogicException('The user provided must be an instance'
throw new LogicException('The user provided must be an instance'
. ' of Chill\MainBundle\Entity\User');
}
}
/*
* (non-PHPdoc)
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('_default', TextType::class, [
'label' => 'First name or Last name',
'required' => false,
])
->add('firstname', TextType::class, [
'label' => 'First name',
'required' => false,
])
->add('lastname', TextType::class, [
'label' => 'Last name',
'required' => false,
])
->add('birthdate-after', ChillDateType::class, [
'label' => 'Birthdate after',
'required' => false,
])
->add('birthdate', ChillDateType::class, [
'label' => 'Birthdate',
'required' => false,
])
->add('birthdate-before', ChillDateType::class, [
'label' => 'Birthdate before',
'required' => false,
])
->add('gender', ChoiceType::class, [
'choices' => [
'Man' => Person::MALE_GENDER,
'Woman' => Person::FEMALE_GENDER,
],
'label' => 'Gender',
'required' => false,
]);
}
public function convertFormDataToQuery(array $data)
{
$string = '@person ';
$string .= empty($data['_default']) ? '' : $data['_default'] . ' ';
foreach (['firstname', 'lastname', 'gender'] as $key) {
$string .= empty($data[$key]) ? '' : $key . ':' .
// add quote if contains spaces
(strpos($data[$key], ' ') !== false ? '"' . $data[$key] . '"' : $data[$key])
. ' ';
}
foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) {
$string .= empty($data[$key]) ?
''
:
$key . ':' . $data[$key]->format('Y-m-d') . ' ';
}
return $string;
}
public function convertTermsToFormData(array $terms)
{
foreach (['firstname', 'lastname', 'gender', '_default']
as $key) {
$data[$key] = $terms[$key] ?? null;
}
// parse dates
foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) {
if (array_key_exists($key, $terms)) {
try {
$date = new DateTime($terms[$key]);
} catch (Exception $ex) {
throw new ParsingException("The date for {$key} is "
. 'not parsable', 0, $ex);
}
}
$data[$key] = $date ?? null;
}
return $data;
}
/**
* @return \Doctrine\ORM\QueryBuilder
*/
public function createQuery(array $terms)
{
//get from cache
$cacheKey = md5(serialize($terms));
if (array_key_exists($cacheKey, $this->_cacheQuery)) {
return clone $this->_cacheQuery[$cacheKey];
}
$qb = $this->em->createQueryBuilder();
$qb->from('ChillPersonBundle:Person', 'p');
if (array_key_exists('firstname', $terms)) {
$qb->andWhere($qb->expr()->like('UNACCENT(LOWER(p.firstName))', ':firstname'))
->setParameter('firstname', '%' . $terms['firstname'] . '%');
}
if (array_key_exists('lastname', $terms)) {
$qb->andWhere($qb->expr()->like('UNACCENT(LOWER(p.lastName))', ':lastname'))
->setParameter('lastname', '%' . $terms['lastname'] . '%');
}
foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) {
if (array_key_exists($key, $terms)) {
try {
$date = new DateTime($terms[$key]);
} catch (Exception $ex) {
throw new ParsingException('The date is '
. 'not parsable', 0, $ex);
}
switch ($key) {
case 'birthdate':
$qb->andWhere($qb->expr()->eq('p.birthdate', ':birthdate'))
->setParameter('birthdate', $date);
break;
case 'birthdate-before':
$qb->andWhere($qb->expr()->lt('p.birthdate', ':birthdatebefore'))
->setParameter('birthdatebefore', $date);
break;
case 'birthdate-after':
$qb->andWhere($qb->expr()->gt('p.birthdate', ':birthdateafter'))
->setParameter('birthdateafter', $date);
break;
default:
throw new LogicException("this case {$key} should not exists");
}
}
}
if (array_key_exists('gender', $terms)) {
if (!in_array($terms['gender'], [Person::MALE_GENDER, Person::FEMALE_GENDER], true)) {
throw new ParsingException('The gender '
. $terms['gender'] . ' is not accepted. Should be "' . Person::MALE_GENDER
. '" or "' . Person::FEMALE_GENDER . '"');
}
$qb->andWhere($qb->expr()->eq('p.gender', ':gender'))
->setParameter('gender', $terms['gender']);
}
if (array_key_exists('nationality', $terms)) {
try {
$country = $this->em->createQuery('SELECT c FROM '
. 'ChillMainBundle:Country c WHERE '
. 'LOWER(c.countryCode) LIKE :code')
->setParameter('code', $terms['nationality'])
->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $ex) {
throw new ParsingException('The country code "' . $terms['nationality'] . '" '
. ', used in nationality, is unknow', 0, $ex);
}
$qb->andWhere($qb->expr()->eq('p.nationality', ':nationality'))
->setParameter('nationality', $country);
}
if ('' !== $terms['_default']) {
$grams = explode(' ', $terms['_default']);
foreach ($grams as $key => $gram) {
$qb->andWhere($qb->expr()
->like('p.fullnameCanonical', 'UNACCENT(LOWER(:default_' . $key . '))'))
->setParameter('default_' . $key, '%' . $gram . '%');
}
}
//restraint center for security
$reachableCenters = $this->helper->getReachableCenters(
$this->user,
new Role('CHILL_PERSON_SEE')
);
$qb->andWhere($qb->expr()
->in('p.center', ':centers'))
->setParameter('centers', $reachableCenters);
$this->_cacheQuery[$cacheKey] = $qb;
return clone $qb;
}
public function getAdvancedSearchTitle()
{
return 'Search within persons';
}
/**
* (non-PHPdoc).
*
* @see \Chill\MainBundle\Search\SearchInterface::getOrder()
*/
public function getOrder()
@@ -96,8 +291,9 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
return 100;
}
/*
* (non-PHPdoc)
/**
* (non-PHPdoc).
*
* @see \Chill\MainBundle\Search\SearchInterface::isActiveByDefault()
*/
public function isActiveByDefault()
@@ -105,64 +301,80 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
return true;
}
/**
* (non-PHPdoc).
*
* @see \Chill\MainBundle\Search\SearchInterface::renderResult()
*
* @param mixed $start
* @param mixed $limit
* @param mixed $format
*/
public function renderResult(array $terms, $start = 0, $limit = 50, array $options = [], $format = 'html')
{
$total = $this->count($terms);
$paginator = $this->paginatorFactory->create($total);
if ('html' === $format) {
return $this->container->get('templating')->render(
'ChillPersonBundle:Person:list.html.twig',
[
'persons' => $this->search($terms, $start, $limit, $options),
'pattern' => $this->recomposePattern($terms, ['nationality',
'firstname', 'lastname', 'birthdate', 'gender',
'birthdate-before', 'birthdate-after', ], $terms['_domain']),
'total' => $total,
'start' => $start,
'search_name' => self::NAME,
'preview' => $options[SearchInterface::SEARCH_PREVIEW_OPTION],
'paginator' => $paginator,
]
);
}
if ('json' === $format) {
return [
'results' => $this->search($terms, $start, $limit, array_merge($options, ['simplify' => true])),
'pagination' => [
'more' => $paginator->hasNextPage(),
],
];
}
}
public function supports($domain, $format)
{
return 'person' === $domain;
}
/*
* (non-PHPdoc)
* @see \Chill\MainBundle\Search\SearchInterface::renderResult()
*/
public function renderResult(array $terms, $start = 0, $limit = 50, array $options = array(), $format = 'html')
protected function count(array $terms)
{
$total = $this->count($terms);
$paginator = $this->paginatorFactory->create($total);
$qb = $this->createQuery($terms);
if ($format === 'html') {
return $this->container->get('templating')->render('ChillPersonBundle:Person:list.html.twig',
array(
'persons' => $this->search($terms, $start, $limit, $options),
'pattern' => $this->recomposePattern($terms, array('nationality',
'firstname', 'lastname', 'birthdate', 'gender',
'birthdate-before','birthdate-after'), $terms['_domain']),
'total' => $total,
'start' => $start,
'search_name' => self::NAME,
'preview' => $options[SearchInterface::SEARCH_PREVIEW_OPTION],
'paginator' => $paginator
));
} elseif ($format === 'json') {
return [
'results' => $this->search($terms, $start, $limit, \array_merge($options, [ 'simplify' => true ])),
'pagination' => [
'more' => $paginator->hasNextPage()
]
];
}
$qb->select('COUNT(p.id)');
return $qb->getQuery()->getSingleScalarResult();
}
/**
*
* @param string $pattern
* @param int $start
* @param int $limit
* @param array $options
*
* @return Person[]
*/
protected function search(array $terms, $start, $limit, array $options = array())
protected function search(array $terms, $start, $limit, array $options = [])
{
$qb = $this->createQuery($terms, 'search');
if ($options['simplify'] ?? false) {
$qb->select(
'p.id',
$qb->expr()->concat(
'p.firstName',
$qb->expr()->literal(' '),
'p.lastName'
).'AS text'
);
'p.id',
$qb->expr()->concat(
'p.firstName',
$qb->expr()->literal(' '),
'p.lastName'
) . 'AS text'
);
} else {
$qb->select('p');
}
@@ -176,219 +388,11 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
$qb
->orderBy('p.firstName')
->addOrderBy('p.lastName');
if ($options['simplify'] ?? false) {
return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
} else {
return $qb->getQuery()->getResult();
}
return $qb->getQuery()->getResult();
}
protected function count(array $terms)
{
$qb = $this->createQuery($terms);
$qb->select('COUNT(p.id)');
return $qb->getQuery()->getSingleScalarResult();
}
private $_cacheQuery = array();
/**
*
* @param array $terms
* @return \Doctrine\ORM\QueryBuilder
*/
public function createQuery(array $terms)
{
//get from cache
$cacheKey = md5(serialize($terms));
if (array_key_exists($cacheKey, $this->_cacheQuery)) {
return clone $this->_cacheQuery[$cacheKey];
}
$qb = $this->em->createQueryBuilder();
$qb->from('ChillPersonBundle:Person', 'p');
if (array_key_exists('firstname', $terms)) {
$qb->andWhere($qb->expr()->like('UNACCENT(LOWER(p.firstName))', ':firstname'))
->setParameter('firstname', '%'.$terms['firstname'].'%');
}
if (array_key_exists('lastname', $terms)) {
$qb->andWhere($qb->expr()->like('UNACCENT(LOWER(p.lastName))', ':lastname'))
->setParameter('lastname', '%'.$terms['lastname'].'%');
}
foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key)
if (array_key_exists($key, $terms)) {
try {
$date = new \DateTime($terms[$key]);
} catch (\Exception $ex) {
throw new ParsingException('The date is '
. 'not parsable', 0, $ex);
}
switch($key) {
case 'birthdate':
$qb->andWhere($qb->expr()->eq('p.birthdate', ':birthdate'))
->setParameter('birthdate', $date);
break;
case 'birthdate-before':
$qb->andWhere($qb->expr()->lt('p.birthdate', ':birthdatebefore'))
->setParameter('birthdatebefore', $date);
break;
case 'birthdate-after':
$qb->andWhere($qb->expr()->gt('p.birthdate', ':birthdateafter'))
->setParameter('birthdateafter', $date);
break;
default:
throw new \LogicException("this case $key should not exists");
}
}
if (array_key_exists('gender', $terms)) {
if (!in_array($terms['gender'], array(Person::MALE_GENDER, Person::FEMALE_GENDER))) {
throw new ParsingException('The gender '
.$terms['gender'].' is not accepted. Should be "'.Person::MALE_GENDER
.'" or "'.Person::FEMALE_GENDER.'"');
}
$qb->andWhere($qb->expr()->eq('p.gender', ':gender'))
->setParameter('gender', $terms['gender']);
}
if (array_key_exists('nationality', $terms)) {
try {
$country = $this->em->createQuery('SELECT c FROM '
. 'ChillMainBundle:Country c WHERE '
. 'LOWER(c.countryCode) LIKE :code')
->setParameter('code', $terms['nationality'])
->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $ex) {
throw new ParsingException('The country code "'.$terms['nationality'].'" '
. ', used in nationality, is unknow', 0, $ex);
}
$qb->andWhere($qb->expr()->eq('p.nationality', ':nationality'))
->setParameter('nationality', $country);
}
if ($terms['_default'] !== '') {
$grams = explode(' ', $terms['_default']);
foreach($grams as $key => $gram) {
$qb->andWhere($qb->expr()
->like('p.fullnameCanonical', 'UNACCENT(LOWER(:default_'.$key.'))'))
->setParameter('default_'.$key, '%'.$gram.'%');
}
}
//restraint center for security
$reachableCenters = $this->helper->getReachableCenters($this->user,
new Role('CHILL_PERSON_SEE'));
$qb->andWhere($qb->expr()
->in('p.center', ':centers'))
->setParameter('centers', $reachableCenters)
;
$this->_cacheQuery[$cacheKey] = $qb;
return clone $qb;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('_default', TextType::class, [
'label' => 'First name or Last name',
'required' => false
])
->add('firstname', TextType::class, [
'label' => 'First name',
'required' => false
])
->add('lastname', TextType::class, [
'label' => 'Last name',
'required' => false
])
->add('birthdate-after', ChillDateType::class, [
'label' => 'Birthdate after',
'required' => false
])
->add('birthdate', ChillDateType::class, [
'label' => 'Birthdate',
'required' => false
])
->add('birthdate-before', ChillDateType::class, [
'label' => 'Birthdate before',
'required' => false
])
->add('gender', ChoiceType::class, [
'choices' => [
'Man' => Person::MALE_GENDER,
'Woman' => Person::FEMALE_GENDER
],
'label' => 'Gender',
'required' => false
])
;
}
public function convertFormDataToQuery(array $data)
{
$string = '@person ';
$string .= empty($data['_default']) ? '' : $data['_default'].' ';
foreach(['firstname', 'lastname', 'gender'] as $key) {
$string .= empty($data[$key]) ? '' : $key.':'.
// add quote if contains spaces
(strpos($data[$key], ' ') !== false ? '"'.$data[$key].'"': $data[$key])
.' ';
}
foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) {
$string .= empty($data[$key]) ?
''
:
$key.':'.$data[$key]->format('Y-m-d').' '
;
}
return $string;
}
public function convertTermsToFormData(array $terms)
{
foreach(['firstname', 'lastname', 'gender', '_default']
as $key) {
$data[$key] = $terms[$key] ?? null;
}
// parse dates
foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) {
if (\array_key_exists($key, $terms)) {
try {
$date = new \DateTime($terms[$key]);
} catch (\Exception $ex) {
throw new ParsingException("The date for $key is "
. 'not parsable', 0, $ex);
}
}
$data[$key] = $date ?? null;
}
return $data;
}
public function getAdvancedSearchTitle()
{
return 'Search within persons';
}
}

View File

@@ -1,95 +1,78 @@
<?php
/*
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014-2019, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Search;
use Chill\MainBundle\Search\AbstractSearch;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Search\SearchInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Search\AbstractSearch;
use Chill\MainBundle\Search\SearchInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Templating\EngineInterface;
/**
*
*
*/
class PersonSearchByPhone extends AbstractSearch
{
/**
*
* @var PersonRepository
*/
private $personRepository;
public const NAME = 'phone';
/**
*
* @var TokenStorageInterface
* @var bool
*/
private $tokenStorage;
protected $activeByDefault;
/**
* @var Templating
*/
protected $engine;
/**
* @var PaginatorFactory
*/
protected $paginatorFactory;
/**
*
* @var AuthorizationHelper
*/
private $helper;
/**
*
* @var PaginatorFactory
* @var PersonRepository
*/
protected $paginatorFactory;
private $personRepository;
/**
*
* @var bool
* @var TokenStorageInterface
*/
protected $activeByDefault;
/**
*
* @var Templating
*/
protected $engine;
const NAME = 'phone';
private $tokenStorage;
public function __construct(
PersonRepository $personRepository,
TokenStorageInterface $tokenStorage,
AuthorizationHelper $helper,
PersonRepository $personRepository,
TokenStorageInterface $tokenStorage,
AuthorizationHelper $helper,
PaginatorFactory $paginatorFactory,
EngineInterface $engine,
$activeByDefault)
{
$activeByDefault
) {
$this->personRepository = $personRepository;
$this->tokenStorage = $tokenStorage;
$this->helper = $helper;
$this->paginatorFactory = $paginatorFactory;
$this->engine = $engine;
$this->activeByDefault = $activeByDefault === 'always';
$this->activeByDefault = 'always' === $activeByDefault;
}
public function getOrder(): int
{
return 110;
@@ -100,34 +83,35 @@ class PersonSearchByPhone extends AbstractSearch
return $this->activeByDefault;
}
public function renderResult(array $terms, $start = 0, $limit = 50, $options = array(), $format = 'html')
public function renderResult(array $terms, $start = 0, $limit = 50, $options = [], $format = 'html')
{
$phonenumber = $terms['_default'];
$centers = $this->helper->getReachableCenters($this->tokenStorage
$centers = $this->helper->getReachableCenters($this->tokenStorage
->getToken()->getUser(), new Role(PersonVoter::SEE));
$total = $this->personRepository
->countByPhone($phonenumber, $centers);
$persons = $this->personRepository
->findByPhone($phonenumber, $centers, $start, $limit)
;
->findByPhone($phonenumber, $centers, $start, $limit);
$paginator = $this->paginatorFactory
->create($total);
return $this->engine->render('ChillPersonBundle:Person:list_by_phonenumber.html.twig',
array(
'persons' => $persons,
'pattern' => $this->recomposePattern($terms, array(), $terms['_domain'] ?? self::NAME),
'phonenumber' => $phonenumber,
'total' => $total,
'start' => $start,
'search_name' => self::NAME,
'preview' => $options[SearchInterface::SEARCH_PREVIEW_OPTION],
'paginator' => $paginator
));
return $this->engine->render(
'ChillPersonBundle:Person:list_by_phonenumber.html.twig',
[
'persons' => $persons,
'pattern' => $this->recomposePattern($terms, [], $terms['_domain'] ?? self::NAME),
'phonenumber' => $phonenumber,
'total' => $total,
'start' => $start,
'search_name' => self::NAME,
'preview' => $options[SearchInterface::SEARCH_PREVIEW_OPTION],
'paginator' => $paginator,
]
);
}
public function supports($domain, $format): bool
{
return $domain === 'phone' && $format === 'html';
return 'phone' === $domain && 'html' === $format;
}
}

Some files were not shown because too many files have changed in this diff Show More