{%- if options['addLink'] and is_granted('CHILL_PERSON_SEE', person) -%}
- {{ _self.raw(person, options) }}
-
- {%- else -%}
- {{ _self.raw(person, options) }}
{%- endif -%}
+
+ {% if options['customArea']['beforeLabel'] is defined %}
+ {{ options['customArea']['beforeLabel'] }}
+ {% endif %}
+
+ {{ _self.raw(person, options) }}
+
+ {% if options['customArea']['afterLabel'] is defined %}
+ {{ options['customArea']['afterLabel'] }}
+ {% endif %}
+
+ {%- if options['addLink'] and is_granted('CHILL_PERSON_SEE', person) -%}
+
+ {%- endif -%}
+
{%- if options['addEntity'] -%}
{{ 'Person'|trans }}
{%- endif -%}
@@ -131,10 +146,10 @@
{% endif %}
{% endif %}
- {% if options['addCenter'] %}
+ {% if options['addCenter'] and person|chill_resolve_center is not null %}
- {{ person.center }}
+ {{ person|chill_resolve_center.name }}
{% endif %}
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/Member/edit.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/Member/edit.html.twig
index 85fee6ec9..ac712264a 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Household/Member/edit.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/Member/edit.html.twig
@@ -4,7 +4,8 @@
{% block content %}
-
{{ block('title') }}
+
{{ member.person|chill_entity_render_string }}
+
{{ 'household.Edit his household'|trans }}
{{ form_start(form) }}
{{ form_widget(form) }}
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/_render_member.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/_render_member.html.twig
new file mode 100644
index 000000000..5b41a15e3
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/_render_member.html.twig
@@ -0,0 +1,65 @@
+{% macro addHolder(holder) %}
+ {% if holder %}
+
+
+ T
+
+ {% endif %}
+{% endmacro %}
+
+
+
+
+ {{ member.person|chill_entity_render_box({
+ 'render': 'label',
+ 'addLink': true,
+ 'addInfo': true,
+ 'customArea': {
+ 'afterLabel': _self.addHolder(member.holder)
+ }
+ }) }}
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/address_edit.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/address_edit.html.twig
index 446cd769b..c8342fb07 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Household/address_edit.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/address_edit.html.twig
@@ -7,9 +7,12 @@
{# include vue_address component #}
- {% include '@ChillPerson/Address/_insert_vue_address.html.twig' with {
- binModalStep1: false,
- binModalStep2: false,
+ {% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
+ targetEntity: { name: 'household', id: household.id },
+ backUrl: path('chill_person_household_addresses', { 'household_id': household.id }),
+ openPanesInModal: false,
+ stickyActions: true,
+ useValidFrom: true,
} %}
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/address_move.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/address_move.html.twig
index b2da2097a..b10ef7942 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Household/address_move.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/address_move.html.twig
@@ -7,10 +7,15 @@
{# include vue_address component #}
- {% include '@ChillPerson/Address/_insert_vue_address.html.twig' with {
- binModalStep1: false,
- binModalStep2: false,
+ {% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
+ targetEntity: { name: 'household', id: household.id },
+ backUrl: path('chill_person_household_addresses', { 'household_id': household.id }),
+ openPanesInModal: false,
+ stickyActions: true,
+ useValidFrom: true,
} %}
+ {#
+ #}
{% endblock %}
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/addresses.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/addresses.html.twig
index 2acd68731..646463945 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Household/addresses.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/addresses.html.twig
@@ -15,13 +15,17 @@
{# include vue_address component #}
- {% include '@ChillPerson/Address/_insert_vue_address.html.twig' with {
+ {% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
+ targetEntity: { name: 'household', id: household.id },
+ backUrl: path('chill_person_household_addresses', { 'household_id': household.id }),
mode: 'new',
- backUrl: chill_path_add_return_path('chill_person_household_address_move', { 'household_id': household.id }),
buttonSize: 'btn-lg',
buttonText: 'Move household',
modalTitle: 'Move household',
} %}
+ {#
+ useValidFrom: true,
+ #}
@@ -41,7 +45,6 @@
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/layout.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/layout.html.twig
index d97de654f..0bd208e7a 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Household/layout.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/layout.html.twig
@@ -17,4 +17,8 @@
'layout': '@ChillPerson/menu.html.twig',
'args' : { 'household': household }
}) }}
+
+ {% block block_post_menu %}
+ {% endblock %}
+
{% endblock %}
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/members_editor.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/members_editor.html.twig
index cdea2dee2..301054666 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Household/members_editor.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/members_editor.html.twig
@@ -4,7 +4,7 @@
{% block title 'household.Edit household members'|trans %}
{% block content %}
-
+
{{ block('title') }}
@@ -22,5 +22,6 @@
{% endblock %}
{% block css %}
- {{ encore_entry_link_tags('vue_household_members_editor') }}
+ {{ parent() }}
+ {{ encore_entry_link_tags('vue_household_members_editor') }}
{% endblock %}
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig
index 933032426..ceb835d2e 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig
@@ -2,213 +2,196 @@
{% block title 'household.Household summary'|trans %}
+{% block block_post_menu %}
+
+{% endblock %}
+
{% block content %}
-
{{ block('title') }}
+ {#
+
{{ block('title') }}
+
{{ 'household.Current address'|trans }}
+ #}
-
{{ 'household.Current address'|trans }}
+ {% set address = household.currentAddress %}
-{% set address = household.currentAddress %}
+
+
+
{{ 'Address'|trans }}
-{% if address is empty %}
-
{{ 'household.Household does not have any address currently'|trans }}
-{% else %}
-
- {{ address|chill_entity_render_box({'multiline': true}) }}
-
-{% endif %}
-
-
{{ 'household.Household members'|trans }}
-
-{% if form is not null %}
- {{ form_start(form) }}
-
- {{ form_row(form.commentMembers) }}
-
-
- {{ form_row(form.waitingForBirth) }}
-
-
-
- {{ form_row(form.waitingForBirthDate) }}
-
-
-
-
-
- {{ 'Save'|trans }}
-
-
-
-
- {{ form_end(form) }}
-{% else %}
-
- {% if not household.commentMembers.isEmpty() %}
- {{ household.commentMembers|chill_entity_render_box }}
- {% endif %}
-
- {% if household.waitingForBirth %}
- {% if household.waitingForBirthDate is not null %}
- {{ 'household.Expecting for birth on date'|trans({ 'date': household.waitingForBirthDate|format_date('long') }) }}
- {% else %}
- {{ 'household.Expecting for birth'|trans }}
- {% endif %}
- {% else %}
-
- {{ 'household.Any expecting birth'|trans }}
-
- {% endif %}
-
-
-
-{% endif %}
-
-
-{% for p in positions %}
-
{{ p.label|localize_translatable_string }}
-
- {% if false == p.shareHousehold %}
-
{{ 'household.Those members does not share address'|trans }}
- {% endif %}
-
- {%- set members = household.currentMembersByPosition(p) %}
- {% if members|length > 0 %}
-
- {% for m in members %}
-
-
-
-
- {{ m.person|chill_entity_render_box({'addLink': true}) }}
- {% if m.holder %}
- {{ 'household.holder'|trans }}
- {% endif %}
-
-
- {{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }}
-
-
-
-
- {% if m.startDate is not empty %}
- {{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}
- {% endif %}
- {% if m.endDate is not empty %}
- {{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}
- {% endif %}
-
-
-
-
- {{ 'household.Update membership'|trans }}
-
-
-
-
-
- {{ 'household.Change position'|trans }}
-
-
-
-
-
- {{ 'household.Leave'|trans }}
-
-
-
-
-
- {% if m.comment is not empty %}
-
- {% endif %}
-
- {% endfor %}
-
- {% else %}
-
{{ 'household.Any persons into this position'|trans }}
- {% endif %}
-
- {% set members = household.nonCurrentMembersByPosition(p) %}
- {% if members|length > 0 %}
-
-
- {{ 'household.Show future or past memberships'|trans({'length': members|length}) }}
-
-
-
-
- {% for m in members %}
-
-
-
-
- {{ m.person|chill_entity_render_box({'addLink': true}) }}
- {% if m.holder %}
- {{ 'household.holder'|trans }}
- {% endif %}
-
-
- {{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }}
-
-
-
-
- {% if m.startDate is not empty %}
- {{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}
- {% endif %}
- {% if m.endDate is not empty %}
- {{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}
- {% endif %}
-
-
-
-
- {% if m.comment is not empty %}
-
+ {% if address is empty %}
+
{{ 'household.Household does not have any address currently'|trans }}
+ {% else %}
+ {{ address|chill_entity_render_box({'multiline': true, 'extended_infos': true }) }}
{% endif %}
-
- {% endfor %}
-
+
+
+
+ {# include vue_address component #}
+ {% include '@ChillMain/Address/_insert_vue_address.html.twig' with {
+ targetEntity: { name: 'household', id: household.id },
+ backUrl: path('chill_person_household_summary', { 'household_id': household.id }),
+ hideAddress: true,
+ mode: 'new',
+ buttonSize: 'btn-sm',
+ buttonText: 'Move household',
+ modalTitle: 'Move household',
+ buttonDisplayText: false,
+ } %}
+
+
+
+
+
+
+
+
+ {% if address is not empty %}
+
+ {% endif %}
- {% endif %}
-{% endfor %}
+
{{ 'household.Household members'|trans }}
-
+ {% for p in positions %}
+
+
{{ p.label|localize_translatable_string }}
+ {% if false == p.shareHousehold %}
+
+ {% endif %}
+
+
+ {%- set members = household.currentMembersByPosition(p) %}
+
+ {% macro customButtons(member, household) %}
+
+
+
+
+
+
+ {% endmacro %}
+
+ {% if members|length > 0 %}
+
+ {% for m in members %}
+ {% include '@ChillPerson/Household/_render_member.html.twig' with {
+ 'member': m,
+ 'customButtons': { 'after': _self.customButtons(m, household) }
+ } %}
+ {% endfor %}
+
+ {% else %}
+
{{ 'household.Any persons into this position'|trans }}
+ {% endif %}
+
+ {% set members = household.nonCurrentMembersByPosition(p) %}
+ {% if members|length > 0 %}
+
+
+
+
+
+
+
+ {% for m in members %}
+ {% include '@ChillPerson/Household/_render_member.html.twig' with { 'member': m } %}
+ {% endfor %}
+
+
+
+
+ {% endif %}
+
+
+ {% endfor %}
+
+
{% endblock %}
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/banner_custom.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/banner_custom.html.twig
index d01a53c22..564f1803e 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Person/banner_custom.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/banner_custom.html.twig
@@ -17,11 +17,11 @@
{%- endif -%}
- {%- if chill_person.fields.spoken_languages == 'visible' -%}
+ {% if person|chill_resolve_center is not null%}
{{ 'Center'|trans|upper}} :
- {{ person.center.name|upper }}
+ {{ person|chill_resolve_center.name|upper }}
{%- endif -%}
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig
index 9718308ed..bd1042dd7 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig
@@ -64,10 +64,10 @@
{{ form_start(form) }}
- {{ form_row(form.firstName, { 'label' : 'First name'|trans }) }}
-
{{ form_row(form.lastName, { 'label' : 'Last name'|trans }) }}
+ {{ form_row(form.firstName, { 'label' : 'First name'|trans }) }}
+
{% if form.altNames is defined %}
{{ form_widget(form.altNames) }}
{% endif %}
@@ -76,12 +76,27 @@
{{ form_row(form.gender, { 'label' : 'Gender'|trans }) }}
+ {% if form.center is defined %}
+ {{ form_row(form.center) }}
+ {% endif %}
+
diff --git a/src/Bundle/ChillPersonBundle/Search/PersonSearch.php b/src/Bundle/ChillPersonBundle/Search/PersonSearch.php
index fe938999a..f62b90006 100644
--- a/src/Bundle/ChillPersonBundle/Search/PersonSearch.php
+++ b/src/Bundle/ChillPersonBundle/Search/PersonSearch.php
@@ -20,71 +20,39 @@
namespace Chill\PersonBundle\Search;
use Chill\MainBundle\Search\AbstractSearch;
-use Doctrine\ORM\EntityManagerInterface;
+use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Search\SearchInterface;
-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\FormBuilderInterface;
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
-use Doctrine\ORM\Query;
+use Symfony\Component\Templating\EngineInterface;
-class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
- HasAdvancedSearchFormInterface
+class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterface
{
- use ContainerAwareTrait;
-
- /**
- *
- * @var EntityManagerInterface
- */
- private $em;
-
- /**
- *
- * @var \Chill\MainBundle\Entity\User
- */
- private $user;
-
- /**
- *
- * @var AuthorizationHelper
- */
- private $helper;
-
- /**
- *
- * @var PaginatorFactory
- */
- protected $paginatorFactory;
+ protected EngineInterface $templating;
+ protected PaginatorFactory $paginatorFactory;
+ protected PersonACLAwareRepositoryInterface $personACLAwareRepository;
const NAME = "person_regular";
+ private const POSSIBLE_KEYS = [
+ '_default', 'firstname', 'lastname', 'birthdate', 'birthdate-before',
+ 'birthdate-after', 'gender', 'nationality'
+ ];
public function __construct(
- EntityManagerInterface $em,
- TokenStorageInterface $tokenStorage,
- AuthorizationHelper $helper,
- PaginatorFactory $paginatorFactory)
- {
- $this->em = $em;
- $this->user = $tokenStorage->getToken()->getUser();
- $this->helper = $helper;
+ EngineInterface $templating,
+ PaginatorFactory $paginatorFactory,
+ PersonACLAwareRepositoryInterface $personACLAwareRepository
+ ) {
+ $this->templating = $templating;
$this->paginatorFactory = $paginatorFactory;
-
- // 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'
- . ' of Chill\MainBundle\Entity\User');
- }
+ $this->personACLAwareRepository = $personACLAwareRepository;
}
/*
@@ -120,12 +88,14 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
$paginator = $this->paginatorFactory->create($total);
if ($format === 'html') {
- return $this->container->get('templating')->render('@ChillPerson/Person/list_with_period.html.twig',
+ return $this->templating->render('@ChillPerson/Person/list_with_period.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']),
+ 'pattern' => $this->recomposePattern(
+ $terms,
+ \array_filter(self::POSSIBLE_KEYS, fn($item) => $item !== '_default'),
+ $terms['_domain']
+ ),
'total' => $total,
'start' => $start,
'search_name' => self::NAME,
@@ -152,153 +122,81 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
*/
protected function search(array $terms, $start, $limit, array $options = array())
{
- $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'
- );
- } else {
- $qb->select('p');
+ [
+ '_default' => $default,
+ 'firstname' => $firstname,
+ 'lastname' => $lastname,
+ 'birthdate' => $birthdate,
+ 'birthdate-before' => $birthdateBefore,
+ 'birthdate-after' => $birthdateAfter,
+ 'gender' => $gender,
+ 'nationality' => $countryCode,
+
+ ] = $terms + \array_fill_keys(self::POSSIBLE_KEYS, null);
+
+ foreach (['birthdateBefore', 'birthdateAfter', 'birthdate'] as $v) {
+ if (NULL !== ${$v}) {
+ try {
+ ${$v} = new \DateTime(${$v});
+ } catch (\Exception $e) {
+ throw new ParsingException('The date is '
+ . 'not parsable', 0, $e);
+ }
+ }
}
- $qb
- ->setMaxResults($limit)
- ->setFirstResult($start);
-
- //order by firstname, lastname
-
- $qb
- ->orderBy('p.firstName')
- ->addOrderBy('p.lastName');
-
- if ($options['simplify'] ?? false) {
- return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
- } else {
- return $qb->getQuery()->getResult();
- }
+ return $this->personACLAwareRepository
+ ->findBySearchCriteria(
+ $start,
+ $limit,
+ $options['simplify'] ?? false,
+ $default,
+ $firstname,
+ $lastname,
+ $birthdate,
+ $birthdateBefore,
+ $birthdateAfter,
+ $gender,
+ $countryCode,
+ );
}
- protected function count(array $terms)
+ protected function count(array $terms): int
{
- $qb = $this->createQuery($terms);
+ [
+ '_default' => $default,
+ 'firstname' => $firstname,
+ 'lastname' => $lastname,
+ 'birthdate' => $birthdate,
+ 'birthdate-before' => $birthdateBefore,
+ 'birthdate-after' => $birthdateAfter,
+ 'gender' => $gender,
+ 'nationality' => $countryCode,
- $qb->select('COUNT(p.id)');
+ ] = $terms + \array_fill_keys(self::POSSIBLE_KEYS, null);
- 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.'%');
+ foreach (['birthdateBefore', 'birthdateAfter', 'birthdate'] as $v) {
+ if (NULL !== ${$v}) {
+ try {
+ ${$v} = new \DateTime(${$v});
+ } catch (\Exception $e) {
+ throw new ParsingException('The date is '
+ . 'not parsable', 0, $e);
+ }
}
}
- //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;
+ return $this->personACLAwareRepository
+ ->countBySearchCriteria(
+ $default,
+ $firstname,
+ $lastname,
+ $birthdate,
+ $birthdateBefore,
+ $birthdateAfter,
+ $gender,
+ $countryCode,
+ );
}
public function buildForm(FormBuilderInterface $builder)
@@ -391,4 +289,10 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
return 'Search within persons';
}
+ public static function getAlias(): string
+ {
+ return self::NAME;
+ }
+
+
}
diff --git a/src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php b/src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php
index ea2c9a5c7..e62c2ee55 100644
--- a/src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php
+++ b/src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php
@@ -2,90 +2,32 @@
namespace Chill\PersonBundle\Search;
-
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Search\AbstractSearch;
use Chill\MainBundle\Search\SearchInterface;
-use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
-use Doctrine\ORM\EntityManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerAwareInterface;
-use Symfony\Component\DependencyInjection\ContainerAwareTrait;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
-use Symfony\Component\Security\Core\Role\Role;
+use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
+use Symfony\Component\Templating\EngineInterface;
-/**
- * Class SimilarityPersonSearch
- *
- * @package Chill\PersonBundle\Search
- */
class SimilarityPersonSearch extends AbstractSearch
{
-
- use ContainerAwareTrait;
-
- /**
- *
- * @var EntityManagerInterface
- */
- private $em;
-
- /**
- *
- * @var \Chill\MainBundle\Entity\User
- */
- private $user;
-
- /**
- *
- * @var AuthorizationHelper
- */
- private $helper;
-
- /**
- *
- * @var PaginatorFactory
- */
- protected $paginatorFactory;
-
+ protected PaginatorFactory $paginatorFactory;
+
+ private PersonACLAwareRepositoryInterface $personACLAwareRepository;
+
+ private EngineInterface $templating;
+
const NAME = "person_similarity";
-
- /**
- *
- * @var PersonSearch
- */
- private $personSearch;
-
-
- /**
- * SimilarityPersonSearch constructor.
- *
- * @param EntityManagerInterface $em
- * @param TokenStorageInterface $tokenStorage
- * @param AuthorizationHelper $helper
- * @param PaginatorFactory $paginatorFactory
- * @param PersonSearch $personSearch
- */
+
public function __construct(
- EntityManagerInterface $em,
- TokenStorageInterface $tokenStorage,
- AuthorizationHelper $helper,
PaginatorFactory $paginatorFactory,
- PersonSearch $personSearch)
- {
- $this->em = $em;
- $this->user = $tokenStorage->getToken()->getUser();
- $this->helper = $helper;
+ PersonACLAwareRepositoryInterface $personACLAwareRepository,
+ EngineInterface $templating
+ ) {
$this->paginatorFactory = $paginatorFactory;
- $this->personSearch = $personSearch;
-
- // 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'
- . ' of Chill\MainBundle\Entity\User');
- }
+ $this->personACLAwareRepository = $personACLAwareRepository;
+ $this->templating = $templating;
}
-
+
/*
* (non-PHPdoc)
* @see \Chill\MainBundle\Search\SearchInterface::getOrder()
@@ -94,7 +36,7 @@ class SimilarityPersonSearch extends AbstractSearch
{
return 200;
}
-
+
/*
* (non-PHPdoc)
* @see \Chill\MainBundle\Search\SearchInterface::isActiveByDefault()
@@ -103,30 +45,27 @@ class SimilarityPersonSearch extends AbstractSearch
{
return true;
}
-
+
public function supports($domain, $format)
{
return 'person' === $domain;
}
-
+
/**
* @param array $terms
* @param int $start
* @param int $limit
* @param array $options
* @param string $format
- * @return array
*/
public function renderResult(array $terms, $start = 0, $limit = 50, array $options = array(), $format = 'html')
{
$total = $this->count($terms);
$paginator = $this->paginatorFactory->create($total);
-
- if ($format === 'html')
- {
- if ($total !== 0)
- {
- return $this->container->get('templating')->render('ChillPersonBundle:Person:list.html.twig',
+
+ if ($format === 'html') {
+ if ($total !== 0) {
+ return $this->templating->render('ChillPersonBundle:Person:list.html.twig',
array(
'persons' => $this->search($terms, $start, $limit, $options),
'pattern' => $this->recomposePattern($terms, array('nationality',
@@ -143,9 +82,8 @@ class SimilarityPersonSearch extends AbstractSearch
else {
return null;
}
-
- } elseif ($format === 'json')
- {
+
+ } elseif ($format === 'json') {
return [
'results' => $this->search($terms, $start, $limit, \array_merge($options, [ 'simplify' => true ])),
'pagination' => [
@@ -154,8 +92,7 @@ class SimilarityPersonSearch extends AbstractSearch
];
}
}
-
-
+
/**
*
* @param string $pattern
@@ -166,101 +103,12 @@ class SimilarityPersonSearch extends AbstractSearch
*/
protected function search(array $terms, $start, $limit, array $options = array())
{
- $qb = $this->createQuery($terms, 'search');
-
-
- if ($options['simplify'] ?? false) {
- $qb->select(
- 'sp.id',
- $qb->expr()->concat(
- 'sp.firstName',
- $qb->expr()->literal(' '),
- 'sp.lastName'
- ).'AS text'
- );
- } else {
- $qb->select('sp');
- }
-
- $qb
- ->setMaxResults($limit)
- ->setFirstResult($start);
-
- //order by firstname, lastname
-
- $qb
- ->orderBy('sp.firstName')
- ->addOrderBy('sp.lastName');
-
- if ($options['simplify'] ?? false) {
- return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
- } else {
- return $qb->getQuery()->getResult();
- }
+ return $this->personACLAwareRepository
+ ->findBySimilaritySearch($terms['_default'], $start, $limit, $options['simplify'] ?? false);
}
-
-
+
protected function count(array $terms)
{
- $qb = $this->createQuery($terms);
-
-
- $qb->select('COUNT(sp.id)');
-
- return $qb->getQuery()->getSingleScalarResult();
+ return $this->personACLAwareRepository->countBySimilaritySearch($terms['_default']);
}
-
-
- private $_cacheQuery = array();
-
- /**
- *
- * @param array $terms
- * @return \Doctrine\ORM\QueryBuilder
- */
- protected 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 ->select('sp')
- ->from('ChillPersonBundle:Person', 'sp');
-
- if ($terms['_default'] !== '') {
- $grams = explode(' ', $terms['_default']);
-
- foreach($grams as $key => $gram) {
- $qb->andWhere('SIMILARITY(sp.fullnameCanonical, UNACCENT(LOWER(:default_'.$key.')) ) >= 0.15')
- ->setParameter('default_'.$key, '%'.$gram.'%');
- }
-
- $qb->andWhere($qb->expr()
- ->notIn(
- 'sp.id',
- $this->personSearch
- ->createQuery($terms)
- ->addSelect('p.id')
- ->getDQL()
- )
- );
- }
-
- //restraint center for security
- $reachableCenters = $this->helper->getReachableCenters($this->user,
- new Role('CHILL_PERSON_SEE'));
- $qb->andWhere($qb->expr()
- ->in('sp.center', ':centers'))
- ->setParameter('centers', $reachableCenters)
- ;
-
- $this->_cacheQuery[$cacheKey] = $qb;
-
- return clone $qb;
- }
-
-}
\ No newline at end of file
+}
diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php
index 2801cb8f7..5247df505 100644
--- a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php
+++ b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php
@@ -4,73 +4,101 @@ namespace Chill\PersonBundle\Security\Authorization;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Entity\User;
-use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
+use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
+use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
-use Chill\MainBundle\Entity\Center;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
-use Symfony\Component\Security\Core\Role\Role;
+use Symfony\Component\Security\Core\Security;
class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
- protected AuthorizationHelper $helper;
-
public const SEE = 'CHILL_PERSON_ACCOMPANYING_PERIOD_SEE';
+ /**
+ * details are for seeing:
+ *
+ * * SocialIssues
+ */
+ public const SEE_DETAILS = 'CHILL_PERSON_ACCOMPANYING_PERIOD_SEE_DETAILS';
+ public const CREATE = 'CHILL_PERSON_ACCOMPANYING_PERIOD_CREATE';
+ public const EDIT = 'CHILL_PERSON_ACCOMPANYING_PERIOD_UPDATE';
+ public const DELETE = 'CHILL_PERSON_ACCOMPANYING_PERIOD_DELETE';
/**
- * @param AuthorizationHelper $helper
+ * Give all the right above
*/
- public function __construct(AuthorizationHelper $helper)
- {
- $this->helper = $helper;
+ public const FULL = 'CHILL_PERSON_ACCOMPANYING_PERIOD_FULL';
+
+ public const ALL = [
+ self::SEE,
+ self::SEE_DETAILS,
+ self::CREATE,
+ self::EDIT,
+ self::DELETE,
+ self::FULL,
+ ];
+
+ private VoterHelperInterface $voterHelper;
+
+ private Security $security;
+
+ public function __construct(
+ Security $security,
+ VoterHelperFactoryInterface $voterHelperFactory
+ ) {
+ $this->security = $security;
+ $this->voterHelper = $voterHelperFactory
+ ->generate(self::class)
+ ->addCheckFor(null, [self::CREATE])
+ ->addCheckFor(AccompanyingPeriod::class, self::ALL)
+ ->addCheckFor(Person::class, [self::SEE])
+ ->build();
}
protected function supports($attribute, $subject)
{
- return $subject instanceof AccompanyingPeriod;
+ return $this->voterHelper->supports($attribute, $subject);
}
- protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
+ protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
if (!$token->getUser() instanceof User) {
return false;
}
- // TODO take scopes into account
- if (count($subject->getPersons()) === 0) {
- return true;
- }
- foreach ($subject->getPersons() as $person) {
- // give access as soon as on center is reachable
- if ($this->helper->userHasAccess($token->getUser(), $person->getCenter(), $attribute)) {
- return true;
+ if ($subject instanceof AccompanyingPeriod) {
+ if (AccompanyingPeriod::STEP_DRAFT === $subject->getStep()) {
+ // only creator can see, edit, delete, etc.
+ if ($subject->getCreatedBy() === $token->getUser()
+ || NULL === $subject->getCreatedBy()) {
+ return true;
+ }
+
+ return false;
}
- return false;
+ // if confidential, only the referent can see it
+ if ($subject->isConfidential()) {
+ return $token->getUser() === $subject->getUser();
+ }
}
- }
- private function getAttributes()
- {
- return [
- self::SEE
- ];
+ return $this->voterHelper->voteOnAttribute($attribute, $subject, $token);
}
public function getRoles()
{
- return $this->getAttributes();
+ return self::ALL;
}
public function getRolesWithoutScope()
{
return [];
}
-
+
public function getRolesWithHierarchy()
{
- return [ 'Person' => $this->getRoles() ];
+ return [ 'Accompanying period' => $this->getRoles() ];
}
-
}
diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/PersonVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/PersonVoter.php
index f81a6efa1..2d585e80e 100644
--- a/src/Bundle/ChillPersonBundle/Security/Authorization/PersonVoter.php
+++ b/src/Bundle/ChillPersonBundle/Security/Authorization/PersonVoter.php
@@ -22,17 +22,15 @@ namespace Chill\PersonBundle\Security\Authorization;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
+use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
+use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
+use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\Center;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Role\Role;
-/**
- *
- *
- * @author Julien Fastré
- */
class PersonVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
const CREATE = 'CHILL_PERSON_CREATE';
@@ -41,51 +39,31 @@ class PersonVoter extends AbstractChillVoter implements ProvideRoleHierarchyInte
const STATS = 'CHILL_PERSON_STATS';
const LISTS = 'CHILL_PERSON_LISTS';
const DUPLICATE = 'CHILL_PERSON_DUPLICATE';
-
- /**
- *
- * @var AuthorizationHelper
- */
- protected $helper;
-
- public function __construct(AuthorizationHelper $helper)
- {
- $this->helper = $helper;
+
+ protected VoterHelperInterface $voter;
+
+ public function __construct(
+ VoterHelperFactoryInterface $voterFactory
+ ) {
+ $this->voter = $voterFactory
+ ->generate(self::class)
+ ->addCheckFor(Center::class, [self::STATS, self::LISTS, self::DUPLICATE])
+ ->addCheckFor(Person::class, [self::CREATE, self::UPDATE, self::SEE, self::DUPLICATE])
+ ->addCheckFor(null, [self::CREATE] )
+ ->build()
+ ;
}
-
+
protected function supports($attribute, $subject)
{
- if ($subject instanceof Person) {
- return \in_array($attribute, [
- self::CREATE, self::UPDATE, self::SEE, self::DUPLICATE
- ]);
- } elseif ($subject instanceof Center) {
- return \in_array($attribute, [
- self::STATS, self::LISTS, self::DUPLICATE
- ]);
- } elseif ($subject === null) {
- return $attribute === self::CREATE;
- } else {
- return false;
- }
+ return $this->voter->supports($attribute, $subject);
}
-
+
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
- if (!$token->getUser() instanceof User) {
- return false;
- }
-
- if ($subject === null) {
- $centers = $this->helper->getReachableCenters($token->getUser(),
- new Role($attribute));
-
- return count($centers) > 0;
- }
-
- return $this->helper->userHasAccess($token->getUser(), $subject, $attribute);
+ return $this->voter->voteOnAttribute($attribute, $subject, $token);
}
-
+
private function getAttributes()
{
return array(self::CREATE, self::UPDATE, self::SEE, self::STATS, self::LISTS, self::DUPLICATE);
@@ -100,7 +78,7 @@ class PersonVoter extends AbstractChillVoter implements ProvideRoleHierarchyInte
{
return $this->getAttributes();
}
-
+
public function getRolesWithHierarchy()
{
return [ 'Person' => $this->getRoles() ];
diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php
index cde495f52..2b82780c2 100644
--- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php
+++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php
@@ -19,6 +19,7 @@
namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\MainBundle\Entity\Center;
+use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
@@ -61,6 +62,9 @@ class PersonNormalizer implements
public function normalize($person, string $format = null, array $context = array())
{
+ /** @var Household $household */
+ $household = $person->getCurrentHousehold();
+
/** @var Person $person */
return [
'type' => 'person',
@@ -69,6 +73,7 @@ class PersonNormalizer implements
'firstName' => $person->getFirstName(),
'lastName' => $person->getLastName(),
'birthdate' => $this->normalizer->normalize($person->getBirthdate()),
+ 'deathdate' => $this->normalizer->normalize($person->getDeathdate()),
'center' => $this->normalizer->normalize($person->getCenter()),
'phonenumber' => $person->getPhonenumber(),
'mobilenumber' => $person->getMobilenumber(),
@@ -76,6 +81,7 @@ class PersonNormalizer implements
'gender' => $person->getGender(),
'gender_numeric' => $person->getGenderNumeric(),
'current_household_address' => $this->normalizer->normalize($person->getCurrentHouseholdAddress()),
+ 'current_household_id' => $household ? $this->normalizer->normalize($household->getId()) : null,
];
}
@@ -89,7 +95,7 @@ class PersonNormalizer implements
return $r;
}
-
+
public function supportsNormalization($data, string $format = null): bool
{
@@ -125,6 +131,7 @@ class PersonNormalizer implements
foreach ([
'birthdate' => \DateTime::class,
+ 'deathdate' => \DateTime::class,
'center' => Center::class
] as $item => $class) {
if (\array_key_exists($item, $data)) {
diff --git a/src/Bundle/ChillPersonBundle/Templating/Entity/PersonRender.php b/src/Bundle/ChillPersonBundle/Templating/Entity/PersonRender.php
index 55890c4b0..fc112a1e7 100644
--- a/src/Bundle/ChillPersonBundle/Templating/Entity/PersonRender.php
+++ b/src/Bundle/ChillPersonBundle/Templating/Entity/PersonRender.php
@@ -62,6 +62,7 @@ class PersonRender extends AbstractChillEntityRender
'address_multiline' => $options['address_multiline'] ?? false,
'hLevel' => $options['hLevel'] ?? 3,
'customButtons' => $options['customButtons'] ?? [],
+ 'customArea' => $options['customArea'] ?? [],
];
return
diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php
index 3f427e811..a24e94bfa 100644
--- a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php
+++ b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php
@@ -23,6 +23,7 @@
namespace Chill\PersonBundle\Tests\Controller;
+use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
@@ -47,6 +48,8 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
protected ?AccompanyingPeriod $period = NULL;
+ protected ?int $periodId = NULL;
+
/**
* Setup before the first test of this class (see phpunit doc)
*/
@@ -70,15 +73,15 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
*
* @dataProvider dataGenerateRandomAccompanyingCourse
*/
- public function testAccompanyingCourseShow(int $personId, AccompanyingPeriod $period)
+ public function testAccompanyingCourseShow(int $personId, int $periodId)
{
- $c = $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $period->getId()));
+ $c = $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $periodId));
$response = $this->client->getResponse();
$this->assertEquals(200, $response->getStatusCode(), "Test that the response of rest api has a status code ok (200)");
$data = \json_decode($response->getContent());
- $this->assertEquals($data->id, $period->getId(),
+ $this->assertEquals($data->id, $periodId,
"test that the response's data contains the id of the period"
);
$this->assertGreaterThan(0, $data->participations);
@@ -326,14 +329,16 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
*
* @dataProvider dataGenerateRandomAccompanyingCourse
*/
- public function testAccompanyingPeriodPatch(int $personId, AccompanyingPeriod $period)
+ public function testAccompanyingPeriodPatch(int $personId, int $periodId)
{
+ $period = self::$container->get(AccompanyingPeriodRepository::class)
+ ->find($periodId);
$initialValueEmergency = $period->isEmergency();
$em = self::$container->get(EntityManagerInterface::class);
$this->client->request(
Request::METHOD_PATCH,
- sprintf('/api/1.0/person/accompanying-course/%d.json', $period->getId()),
+ sprintf('/api/1.0/person/accompanying-course/%d.json', $periodId),
[], // parameters
[], // files
[], // server parameters
@@ -343,11 +348,10 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
$this->assertEquals(200, $response->getStatusCode());
$period = $em->getRepository(AccompanyingPeriod::class)
- ->find($period->getId());
- $em->refresh($period);
+ ->find($periodId);
$this->assertEquals(!$initialValueEmergency, $period->isEmergency());
- // restore the initial valud
+ // restore the initial value
$period->setEmergency($initialValueEmergency);
$em->flush();
}
@@ -361,12 +365,32 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
$em = static::$container->get(EntityManagerInterface::class);
$center = $em->getRepository(Center::class)
->findOneBy(array('name' => 'Center A'));
+ $qb = $em->createQueryBuilder();
- $personIds = $em->createQuery("SELECT p.id FROM ".
- Person::class." p ".
- " WHERE p.center = :center")
+ $personIds = $qb
+ ->select('p.id')
+ ->distinct(true)
+ ->from(Person::class, 'p')
+ ->join('p.accompanyingPeriodParticipations', 'participation')
+ ->join('participation.accompanyingPeriod', 'ap')
+ ->where(
+ $qb->expr()->eq(
+ 'p.center',
+ ':center'
+ )
+ )
+ ->andWhere(
+ $qb->expr()->gt(
+ 'SIZE(p.accompanyingPeriodParticipations)',
+ 0)
+ )
+ ->andWhere(
+ $qb->expr()->eq('ap.step', ':step')
+ )
->setParameter('center', $center)
+ ->setParameter('step', AccompanyingPeriod::STEP_CONFIRMED)
->setMaxResults($maxResults)
+ ->getQuery()
->getScalarResult();
// create a random order
@@ -400,11 +424,11 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
*
* @dataProvider dataGenerateRandomAccompanyingCourse
*/
- public function testAccompanyingCourseAddParticipation(int $personId, AccompanyingPeriod $period)
+ public function testAccompanyingCourseAddParticipation(int $personId, int $periodId)
{
$this->client->request(
Request::METHOD_POST,
- sprintf('/api/1.0/person/accompanying-course/%d/participation.json', $period->getId()),
+ sprintf('/api/1.0/person/accompanying-course/%d/participation.json', $periodId),
[], // parameters
[], // files
[], // server parameters
@@ -420,7 +444,8 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
// check by deownloading the accompanying cours
- $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $period->getId()));
+ $this->client->request(Request::METHOD_GET, sprintf('/api/1.0/person/accompanying-course/%d.json', $periodId));
+ $this->assertEquals(200, $response->getStatusCode(), "Test that the response of rest api has a status code ok (200)");
$response = $this->client->getResponse();
$data = \json_decode($response->getContent());
@@ -435,7 +460,7 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
// check removing the participation
$this->client->request(
Request::METHOD_DELETE,
- sprintf('/api/1.0/person/accompanying-course/%d/participation.json', $period->getId()),
+ sprintf('/api/1.0/person/accompanying-course/%d/participation.json', $periodId),
[], // parameters
[], // files
[], // server parameters
@@ -450,42 +475,6 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
$this->assertNotNull($data['startDate']);
$this->assertArrayHasKey('endDate', $data);
$this->assertNotNull($data['endDate']);
-
-
- // set to variable for tear down
- $this->personId = $personId;
- $this->period = $period;
- }
-
- protected function tearDown()
- {
- $em = static::$container->get(EntityManagerInterface::class);
-
- // remove participation if set
- if ($this->personId && $this->period) {
- $participation = $em
- ->getRepository(AccompanyingPeriodParticipation::class)
- ->findOneBy(['person' => $this->personId, 'accompanyingPeriod' => $this->period])
- ;
-
- if (NULL !== $participation) {
- $em->remove($participation);
- $em->flush();
- }
- $this->personId = NULL;
- $this->period = NULL;
- } elseif ($this->period) {
- $period = $em
- ->getRepository(AccompanyingPeriod::class)
- ->find($this->period->getId()) ;
-
- if ($period !== NULL) {
- $em->remove($period);
- $em->flush();
- }
-
- $this->period = null;
- }
}
public function dataGenerateRandomAccompanyingCourseWithSocialIssue()
@@ -505,12 +494,32 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
$em = static::$container->get(EntityManagerInterface::class);
$center = $em->getRepository(Center::class)
->findOneBy(array('name' => 'Center A'));
+ $qb = $em->createQueryBuilder();
- $personIds = $em->createQuery("SELECT p.id FROM ".
- Person::class." p ".
- " WHERE p.center = :center")
+ $personIds = $qb
+ ->select('p.id')
+ ->distinct(true)
+ ->from(Person::class, 'p')
+ ->join('p.accompanyingPeriodParticipations', 'participation')
+ ->join('participation.accompanyingPeriod', 'ap')
+ ->where(
+ $qb->expr()->eq(
+ 'p.center',
+ ':center'
+ )
+ )
+ ->andWhere(
+ $qb->expr()->gt(
+ 'SIZE(p.accompanyingPeriodParticipations)',
+ 0)
+ )
+ ->andWhere(
+ $qb->expr()->eq('ap.step', ':step')
+ )
->setParameter('center', $center)
+ ->setParameter('step', AccompanyingPeriod::STEP_CONFIRMED)
->setMaxResults($maxResults)
+ ->getQuery()
->getScalarResult();
// create a random order
@@ -556,7 +565,10 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
$qb = $em->createQueryBuilder();
$personIds = $qb
->select('p.id')
+ ->distinct(true)
->from(Person::class, 'p')
+ ->join('p.accompanyingPeriodParticipations', 'participation')
+ ->join('participation.accompanyingPeriod', 'ap')
->where(
$qb->expr()->eq(
'p.center',
@@ -568,7 +580,11 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
'SIZE(p.accompanyingPeriodParticipations)',
0)
)
+ ->andWhere(
+ $qb->expr()->eq('ap.step', ':step')
+ )
->setParameter('center', $center)
+ ->setParameter('step', AccompanyingPeriod::STEP_CONFIRMED)
->setMaxResults($maxResults)
->getQuery()
->getScalarResult();
@@ -584,7 +600,7 @@ class AccompanyingCourseApiControllerTest extends WebTestCase
->find($id);
$periods = $person->getAccompanyingPeriods();
- yield [\array_pop($personIds)["id"], $periods[\array_rand($periods)] ];
+ yield [\array_pop($personIds)["id"], $periods[\array_rand($periods)]->getId() ];
$nbGenerated++;
}
diff --git a/src/Bundle/ChillPersonBundle/Tests/Validator/BirthdateValidatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Validator/BirthdateValidatorTest.php
deleted file mode 100644
index 57b4c2577..000000000
--- a/src/Bundle/ChillPersonBundle/Tests/Validator/BirthdateValidatorTest.php
+++ /dev/null
@@ -1,112 +0,0 @@
-
- *
- * 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 .
- */
-
-namespace Chill\PersonBundle\Tests\Validator;
-
-use Chill\PersonBundle\Validator\Constraints\BirthdateValidator;
-use Chill\PersonBundle\Validator\Constraints\Birthdate;
-use Prophecy\Argument;
-use Prophecy\Prophet;
-
-/**
- * Test the behaviour of BirthdayValidator
- *
- * @author Julien Fastré
- */
-class BirthdateValidatorTest extends \PHPUnit\Framework\TestCase
-{
- /**
- * A prophecy for \Symfony\Component\Validator\Context\ExecutionContextInterface
- *
- * Will reveal \Symfony\Component\Validator\Context\ExecutionContextInterface
- */
- private $context;
-
- private $prophet;
-
- /**
- *
- * @var Birthdate
- */
- private $constraint;
-
- public function setUp()
- {
- $this->prophet = new Prophet;
-
- $constraintViolationBuilder = $this->prophet->prophesize();
- $constraintViolationBuilder->willImplement('Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface');
-
- $constraintViolationBuilder->setParameter(Argument::any(), Argument::any())
- ->willReturn($constraintViolationBuilder->reveal());
- $constraintViolationBuilder->addViolation()
- ->willReturn($constraintViolationBuilder->reveal());
-
- $this->context = $this->prophet->prophesize();
- $this->context->willImplement('Symfony\Component\Validator\Context\ExecutionContextInterface');
- $this->context->buildViolation(Argument::type('string'))
- ->willReturn($constraintViolationBuilder->reveal());
-
- $this->constraint = new Birthdate();
- }
-
- public function testValidBirthDate()
- {
-
- $date = new \DateTime('2015-01-01');
-
- $birthdateValidator = new BirthdateValidator();
- $birthdateValidator->initialize($this->context->reveal());
-
- $birthdateValidator->validate($date, $this->constraint);
-
- $this->context->buildViolation(Argument::any())->shouldNotHaveBeenCalled();
- }
-
- public function testInvalidBirthDate()
- {
- $date = new \DateTime('tomorrow');
-
- $birthdateValidator = new BirthdateValidator();
- $birthdateValidator->initialize($this->context->reveal());
-
- $birthdateValidator->validate($date, $this->constraint);
-
- $this->context->buildViolation(Argument::type('string'))->shouldHaveBeenCalled();
- }
-
- public function testInvalidBirthDateWithParameter()
- {
- $date = (new \DateTime('today'))->sub(new \DateInterval('P1M'));
-
- $birthdateValidator = new BirthdateValidator('P1Y');
- $birthdateValidator->initialize($this->context->reveal());
-
- $birthdateValidator->validate($date, $this->constraint);
-
- $this->context->buildViolation(Argument::type('string'))->shouldHaveBeenCalled();
- }
-
- public function tearDown()
- {
- $this->prophet->checkPredictions();
- }
-
-
-}
diff --git a/src/Bundle/ChillPersonBundle/Tests/Validator/Person/BirthdateValidatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/BirthdateValidatorTest.php
new file mode 100644
index 000000000..6c726dd1c
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/BirthdateValidatorTest.php
@@ -0,0 +1,74 @@
+
+ *
+ * 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 .
+ */
+
+namespace Chill\PersonBundle\Tests\Validator\Person;
+
+use Chill\PersonBundle\Validator\Constraints\Person\BirthdateValidator;
+use Chill\PersonBundle\Validator\Constraints\Person\Birthdate;
+use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
+use Chill\PersonBundle\Entity\Person;
+
+/**
+ * Test the behaviour of BirthdayValidator
+ *
+ * @author Julien Fastré
+ */
+class BirthdateValidatorTest extends ConstraintValidatorTestCase
+{
+
+ public function testValidateTodayInvalid()
+ {
+ $bornToday = new \DateTime('now');
+ $this->validator->validate($bornToday, $this->createConstraint());
+ $this->buildViolation('msg')
+ ->setParameter('%date%', (new \DateTime('yesterday'))->format('d-m-Y'))
+ ->setCode('3f42fd96-0b2d-11ec-8cf3-0f3b1b1ca1c4')
+ ->assertRaised();
+ }
+
+ public function testValidateYesterdayValid()
+ {
+ $bornYesterday = new \DateTime('yesterday');
+ $this->validator->validate($bornYesterday, $this->createConstraint());
+ $this->assertNoViolation();
+ }
+
+ public function testTomorrowInvalid()
+ {
+ $bornAfter = new \DateTime('+2 days');
+ $this->validator->validate($bornAfter, $this->createConstraint());
+ $this->buildViolation('msg')
+ ->setParameter('%date%', (new \DateTime('yesterday'))->format('d-m-Y'))
+ ->setCode('3f42fd96-0b2d-11ec-8cf3-0f3b1b1ca1c4')
+ ->assertRaised();
+ }
+
+ private function createConstraint()
+ {
+ return new Birthdate([
+ 'message' => 'msg'
+ ]);
+ }
+
+
+ protected function createValidator()
+ {
+ return new BirthdateValidator('P1D');
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php
new file mode 100644
index 000000000..8cd0e6b83
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php
@@ -0,0 +1,50 @@
+getConstraint();
+ $personHasCenter = (new Person())->setCenter(new Center());
+ $personNoCenter = new Person();
+
+ $this->validator->validate($personHasCenter, $constraint);
+ $this->assertNoViolation();
+
+ $this->validator->validate($personNoCenter, $constraint);
+ $this->buildViolation('msg')
+ ->atPath('property.path.center')
+ ->assertRaised();
+ }
+
+ protected function getConstraint()
+ {
+ return new PersonHasCenter([
+ 'message' => 'msg'
+ ]);
+ }
+
+ protected function createValidator()
+ {
+ $parameterBag = $this->createMock(ParameterBagInterface::class);
+ $parameterBag
+ ->method('get')
+ ->with($this->equalTo('chill_person'))
+ ->willReturn([
+ 'validation' => [
+ 'center_required' => true
+ ]
+ ])
+ ;
+
+ return new PersonHasCenterValidator($parameterBag);
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonValidationTest.php b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonValidationTest.php
new file mode 100644
index 000000000..8d6c930d9
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonValidationTest.php
@@ -0,0 +1,55 @@
+validator = self::$container->get(ValidatorInterface::class);
+ }
+
+ public function testFirstnameValidation()
+ {
+ $person = (new Person())
+ ->setFirstname(\str_repeat('a', 500));
+ $errors = $this->validator->validate($person, null, ["creation"]);
+ foreach ($errors->getIterator() as $error) {
+ if (Length::TOO_LONG_ERROR === $error->getCode()) {
+ $this->assertTrue(true,
+ "error code for firstname too long is present");
+ return;
+ }
+ }
+ $this->assertTrue(false,
+ "error code for fistname too long is present");
+
+ }
+
+ public function testBirthdateInFuture()
+ {
+ $person = (new Person())
+ ->setBirthdate(new \Datetime('+2 months'));
+ $errors = $this->validator->validate($person, null, ["creation"]);
+ foreach ($errors->getIterator() as $error) {
+ if (Birthdate::BIRTHDATE_INVALID_CODE === $error->getCode()) {
+ $this->assertTrue(true,
+ "error code for birthdate invalid is present");
+ return;
+ }
+ }
+ $this->assertTrue(false,
+ "error code for birthdate invalid is present");
+
+ }
+
+}
diff --git a/src/Bundle/ChillPersonBundle/Validator/Constraints/Birthdate.php b/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/Birthdate.php
similarity index 85%
rename from src/Bundle/ChillPersonBundle/Validator/Constraints/Birthdate.php
rename to src/Bundle/ChillPersonBundle/Validator/Constraints/Person/Birthdate.php
index d09d549fd..20f3ed97b 100644
--- a/src/Bundle/ChillPersonBundle/Validator/Constraints/Birthdate.php
+++ b/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/Birthdate.php
@@ -17,26 +17,23 @@
* along with this program. If not, see .
*/
-namespace Chill\PersonBundle\Validator\Constraints;
+namespace Chill\PersonBundle\Validator\Constraints\Person;
use Symfony\Component\Validator\Constraint;
/**
* Create a constraint on birth date: the birthdate after today are not allowed.
- *
- * It is possible to add a delay before today, expressed as described in
+ *
+ * It is possible to add a delay before today, expressed as described in
* interval_spec : http://php.net/manual/en/dateinterval.construct.php
* (this interval_spec itself is based on ISO8601 :
* https://en.wikipedia.org/wiki/ISO_8601#Durations)
*
- * @author Julien Fastré
+ * @Annotation
*/
class Birthdate extends Constraint
{
+ public const BIRTHDATE_INVALID_CODE = '3f42fd96-0b2d-11ec-8cf3-0f3b1b1ca1c4';
+
public $message = "The birthdate must be before %date%";
-
- public function validatedBy()
- {
- return 'birthdate_not_before';
- }
}
diff --git a/src/Bundle/ChillPersonBundle/Validator/Constraints/BirthdateValidator.php b/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/BirthdateValidator.php
similarity index 93%
rename from src/Bundle/ChillPersonBundle/Validator/Constraints/BirthdateValidator.php
rename to src/Bundle/ChillPersonBundle/Validator/Constraints/Person/BirthdateValidator.php
index cba955ef6..238a3d160 100644
--- a/src/Bundle/ChillPersonBundle/Validator/Constraints/BirthdateValidator.php
+++ b/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/BirthdateValidator.php
@@ -17,49 +17,50 @@
* along with this program. If not, see .
*/
-namespace Chill\PersonBundle\Validator\Constraints;
+namespace Chill\PersonBundle\Validator\Constraints\Person;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
- *
+ *
*
* @author Julien Fastré
*/
class BirthdateValidator extends ConstraintValidator
{
private $interval_spec = null;
-
+
public function __construct($interval_spec = null)
{
$this->interval_spec = $interval_spec;
}
-
+
public function validate($value, Constraint $constraint)
{
if ($value === NULL) {
-
+
return;
}
-
+
if (!$value instanceof \DateTime) {
throw new \LogicException('The input should a be a \DateTime interface,'
. (is_object($value) ? get_class($value) : gettype($value)));
}
-
+
$limitDate = $this->getLimitDate();
-
+
if ($limitDate < $value) {
$this->context->buildViolation($constraint->message)
->setParameter('%date%', $limitDate->format('d-m-Y'))
+ ->setCode(Birthdate::BIRTHDATE_INVALID_CODE)
->addViolation();
}
-
+
}
-
+
/**
- *
+ *
* @return \DateTime
*/
private function getLimitDate()
diff --git a/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/PersonHasCenter.php b/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/PersonHasCenter.php
new file mode 100644
index 000000000..5cabd8838
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/PersonHasCenter.php
@@ -0,0 +1,18 @@
+centerRequired = $parameterBag->get('chill_person')['validation']['center_required'];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validate($person, Constraint $constraint)
+ {
+ if (!$person instanceof Person) {
+ throw new UnexpectedTypeException($constraint, Person::class);
+ }
+
+ if (!$this->centerRequired) {
+ return;
+ }
+
+ if (NULL === $person->getCenter()) {
+ $this
+ ->context
+ ->buildViolation($constraint->message)
+ ->atPath('center')
+ ->addViolation()
+ ;
+ }
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml
index 1b2c9cd85..020fa409f 100644
--- a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml
+++ b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml
@@ -45,7 +45,7 @@ components:
type:
type: string
enum:
- - 'person'
+ - "person"
firstName:
type: string
lastName:
@@ -55,7 +55,9 @@ components:
description: a canonical representation for the person name
readOnly: true
birthdate:
- $ref: '#/components/schemas/Date'
+ $ref: "#/components/schemas/Date"
+ deathdate:
+ $ref: "#/components/schemas/Date"
phonenumber:
type: string
mobilenumber:
@@ -78,7 +80,7 @@ components:
type:
type: string
enum:
- - 'person'
+ - "person"
required:
- id
- type
@@ -96,7 +98,7 @@ components:
type:
type: string
enum:
- - 'thirdparty'
+ - "thirdparty"
required:
- id
- type
@@ -119,15 +121,15 @@ components:
type:
type: string
enum:
- - 'accompanying_period_resource'
+ - "accompanying_period_resource"
readOnly: true
id:
type: integer
readOnly: true
resource:
anyOf:
- - $ref: '#/components/schemas/PersonById'
- - $ref: '#/components/schemas/ThirdPartyById'
+ - $ref: "#/components/schemas/PersonById"
+ - $ref: "#/components/schemas/ThirdPartyById"
ResourceById:
type: object
properties:
@@ -136,7 +138,7 @@ components:
type:
type: string
enum:
- - 'accompanying_period_resource'
+ - "accompanying_period_resource"
required:
- id
- type
@@ -146,7 +148,7 @@ components:
type:
type: string
enum:
- - 'accompanying_period_comment'
+ - "accompanying_period_comment"
readOnly: true
id:
type: integer
@@ -161,7 +163,7 @@ components:
type:
type: string
enum:
- - 'accompanying_period_comment'
+ - "accompanying_period_comment"
required:
- id
- type
@@ -173,7 +175,7 @@ components:
type:
type: string
enum:
- - 'social_issue'
+ - "social_issue"
parent_id:
type: integer
readOnly: true
@@ -195,12 +197,12 @@ components:
Household:
type: object
properties:
- id:
- type: integer
- type:
- type: string
- enum:
- - 'household'
+ id:
+ type: integer
+ type:
+ type: string
+ enum:
+ - "household"
HouseholdPosition:
type: object
properties:
@@ -209,7 +211,7 @@ components:
type:
type: string
enum:
- - 'household_position'
+ - "household_position"
AccompanyingCourseWork:
type: object
properties:
@@ -218,7 +220,7 @@ components:
type:
type: string
enum:
- - 'accompanying_period_work'
+ - "accompanying_period_work"
note:
type: string
startDate:
@@ -243,15 +245,15 @@ components:
type:
type: string
enum:
- - 'accompanying_period_work_goal'
+ - "accompanying_period_work_goal"
note:
type: string
goal:
- $ref: '#/components/schemas/SocialWorkGoalById'
+ $ref: "#/components/schemas/SocialWorkGoalById"
results:
type: array
items:
- $ref: '#/components/schemas/SocialWorkGoalById'
+ $ref: "#/components/schemas/SocialWorkGoalById"
SocialWorkResultById:
type: object
@@ -261,7 +263,7 @@ components:
type:
type: string
enum:
- - 'social_work_result'
+ - "social_work_result"
SocialWorkGoalById:
type: object
properties:
@@ -270,7 +272,7 @@ components:
type:
type: string
enum:
- - 'social_work_goal'
+ - "social_work_goal"
paths:
/1.0/person/person/{id}.json:
@@ -308,7 +310,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Person'
+ $ref: "#/components/schemas/Person"
responses:
200:
description: "OK"
@@ -355,18 +357,17 @@ paths:
422:
description: "Unprocessable entity (validation errors)"
-
/1.0/person/address/suggest/by-person/{id}.json:
get:
tags:
- address
summary: get a list of suggested address for a person
description: >
- The address are computed from various source. Currently:
+ The address are computed from various source. Currently:
- - the address of course to which the person is participating
+ - the address of course to which the person is participating
- The current person's address is always ignored.
+ The current person's address is always ignored.
parameters:
- name: id
in: path
@@ -390,11 +391,11 @@ paths:
- address
summary: get a list of suggested address for a household
description: >
- The address are computed from various source. Currently:
+ The address are computed from various source. Currently:
- - the address of course to which the members is participating
+ - the address of course to which the members is participating
- The current household address is always ignored.
+ The current household address is always ignored.
parameters:
- name: id
in: path
@@ -452,7 +453,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/AccompanyingPeriod'
+ $ref: "#/components/schemas/AccompanyingPeriod"
examples:
Set the requestor as anonymous:
value:
@@ -488,7 +489,7 @@ paths:
id: 0
personLocation: null
addressLocation:
- id: 7960
+ id: 7960
responses:
401:
description: "Unauthorized"
@@ -520,8 +521,8 @@ paths:
application/json:
schema:
oneOf:
- - $ref: '#/components/schemas/PersonById'
- - $ref: '#/components/schemas/ThirdPartyById'
+ - $ref: "#/components/schemas/PersonById"
+ - $ref: "#/components/schemas/ThirdPartyById"
examples:
add person with id 50:
summary: "a person with id 50"
@@ -585,7 +586,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/PersonById'
+ $ref: "#/components/schemas/PersonById"
responses:
401:
description: "Unauthorized"
@@ -614,7 +615,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/PersonById'
+ $ref: "#/components/schemas/PersonById"
responses:
401:
description: "Unauthorized"
@@ -645,7 +646,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Resource'
+ $ref: "#/components/schemas/Resource"
examples:
add person with id 50:
summary: "a person with id 50"
@@ -690,7 +691,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ResourceById'
+ $ref: "#/components/schemas/ResourceById"
responses:
401:
description: "Unauthorized"
@@ -721,7 +722,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Comment'
+ $ref: "#/components/schemas/Comment"
examples:
a single comment:
summary: "a simple comment"
@@ -759,7 +760,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/CommentById'
+ $ref: "#/components/schemas/CommentById"
responses:
401:
description: "Unauthorized"
@@ -790,7 +791,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Scope'
+ $ref: "#/components/schemas/Scope"
examples:
add a scope:
value:
@@ -824,7 +825,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/ScopeById'
+ $ref: "#/components/schemas/ScopeById"
responses:
401:
description: "Unauthorized"
@@ -855,7 +856,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/SocialIssue'
+ $ref: "#/components/schemas/SocialIssue"
examples:
add a social issue:
value:
@@ -889,7 +890,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/SocialIssue'
+ $ref: "#/components/schemas/SocialIssue"
responses:
401:
description: "Unauthorized"
@@ -926,11 +927,11 @@ paths:
type:
type: string
enum:
- - 'accompanying_period_work'
+ - "accompanying_period_work"
startDate:
- $ref: '#/components/schemas/Date'
+ $ref: "#/components/schemas/Date"
endDate:
- $ref: '#/components/schemas/Date'
+ $ref: "#/components/schemas/Date"
examples:
create a work:
value:
@@ -992,7 +993,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/AccompanyingCourseWork'
+ $ref: "#/components/schemas/AccompanyingCourseWork"
responses:
401:
description: "Unauthorized"
@@ -1029,8 +1030,6 @@ paths:
400:
description: "transition cannot be applyed"
-
-
/1.0/person/accompanying-period/origin.json:
get:
tags:
@@ -1064,8 +1063,6 @@ paths:
404:
description: "Not found"
-
-
/1.0/person/household.json:
get:
tags:
@@ -1095,7 +1092,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Household'
+ $ref: "#/components/schemas/Household"
404:
description: "not found"
401:
@@ -1107,9 +1104,9 @@ paths:
- household
summary: Return households associated with the given person through accompanying periods
description: |
- Return households associated with the given person throught accompanying periods participation.
+ Return households associated with the given person throught accompanying periods participation.
- The current household of the given person is excluded.
+ The current household of the given person is excluded.
parameters:
- name: person_id
in: path
@@ -1125,7 +1122,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/Household'
+ $ref: "#/components/schemas/Household"
404:
description: "not found"
401:
@@ -1149,23 +1146,22 @@ paths:
type: object
properties:
person:
- $ref: '#/components/schemas/PersonById'
+ $ref: "#/components/schemas/PersonById"
start_date:
- $ref: '#/components/schemas/Date'
+ $ref: "#/components/schemas/Date"
position:
- $ref: '#/components/schemas/HouseholdPosition'
+ $ref: "#/components/schemas/HouseholdPosition"
holder:
type: boolean
comment:
type: string
destination:
- $ref: '#/components/schemas/Household'
+ $ref: "#/components/schemas/Household"
examples:
Moving person to a new household:
value:
concerned:
- -
- person:
+ - person:
id: 0
type: person
position:
@@ -1180,8 +1176,7 @@ paths:
Moving person to a new household and set an address to this household:
value:
concerned:
- -
- person:
+ - person:
id: 0
type: person
position:
@@ -1198,8 +1193,7 @@ paths:
Moving person to an existing household:
value:
concerned:
- -
- person:
+ - person:
id: 0
type: person
position:
@@ -1215,8 +1209,7 @@ paths:
Removing a person from any household:
value:
concerned:
- -
- person:
+ - person:
id: 0
type: person
start_date:
@@ -1270,8 +1263,6 @@ paths:
400:
description: "transition cannot be applyed"
-
-
/1.0/person/social/social-action.json:
get:
tags:
@@ -1349,7 +1340,6 @@ paths:
404:
description: not found
-
/1.0/person/social-work/social-issue.json:
get:
tags:
@@ -1379,7 +1369,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/SocialIssue'
+ $ref: "#/components/schemas/SocialIssue"
404:
description: "not found"
401:
diff --git a/src/Bundle/ChillPersonBundle/config/services.yaml b/src/Bundle/ChillPersonBundle/config/services.yaml
index c2f60a729..6133068a4 100644
--- a/src/Bundle/ChillPersonBundle/config/services.yaml
+++ b/src/Bundle/ChillPersonBundle/config/services.yaml
@@ -24,14 +24,6 @@ services:
tags:
- { name: console.command }
- chill.person.form.type.select2maritalstatus:
- class: Chill\PersonBundle\Form\Type\Select2MaritalStatusType
- arguments:
- - "@request_stack"
- - "@doctrine.orm.entity_manager"
- tags:
- - { name: form.type, alias: select2_chill_marital_status }
-
chill.person.timeline.accompanying_period_opening:
class: Chill\PersonBundle\Timeline\TimelineAccompanyingPeriodOpening
arguments:
@@ -62,23 +54,22 @@ services:
- { name: security.voter }
- { name: chill.role }
- chill.person.birthdate_validation:
- class: Chill\PersonBundle\Validator\Constraints\BirthdateValidator
+ Chill\PersonBundle\Validator\Constraints\:
+ autowire: true
+ autoconfigure: true
+ resource: '../Validator/Constraints/'
+
+ # override default config, must be loaded after resource
+ Chill\PersonBundle\Validator\Constraints\BirthdateValidator:
arguments:
- "%chill_person.validation.birtdate_not_before%"
tags:
- { name: validator.constraint_validator, alias: birthdate_not_before }
- Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequentialValidator:
- autowire: true
- tags:
- - { name: validator.constraint_validator }
-
Chill\PersonBundle\Repository\:
autowire: true
autoconfigure: true
resource: '../Repository/'
- tags: ['doctrine.repository_service']
Chill\PersonBundle\Controller\:
autowire: true
diff --git a/src/Bundle/ChillPersonBundle/config/services/controller.yaml b/src/Bundle/ChillPersonBundle/config/services/controller.yaml
index 6efdb4640..489168425 100644
--- a/src/Bundle/ChillPersonBundle/config/services/controller.yaml
+++ b/src/Bundle/ChillPersonBundle/config/services/controller.yaml
@@ -12,9 +12,8 @@ services:
tags: ['controller.service_arguments']
Chill\PersonBundle\Controller\AccompanyingPeriodController:
- arguments:
- $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
- $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
+ autowire: true
+ autoconfigure: true
tags: ['controller.service_arguments']
Chill\PersonBundle\Controller\PersonAddressController:
diff --git a/src/Bundle/ChillPersonBundle/config/services/form.yaml b/src/Bundle/ChillPersonBundle/config/services/form.yaml
index b61de615d..dacfa41de 100644
--- a/src/Bundle/ChillPersonBundle/config/services/form.yaml
+++ b/src/Bundle/ChillPersonBundle/config/services/form.yaml
@@ -1,5 +1,10 @@
services:
+ Chill\PersonBundle\Form\:
+ autowire: true
+ autoconfigure: true
+ resource: '../../Form/'
+
Chill\PersonBundle\Form\PersonType:
arguments:
- '%chill_person.person_fields%'
@@ -7,14 +12,7 @@ services:
tags:
- { name: form.type, alias: '@chill.person.form.person_creation' }
- Chill\PersonBundle\Form\CreationPersonType:
- arguments:
- - '@chill.main.form.data_transformer.center_transformer'
- - '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
- tags:
- - { name: form.type, alias: '@chill.main.form.person_creation' }
-
- chill.person.accompanying_period_closing_motive:
+ Chill\PersonBundle\Form\Type\ClosingMotivePickerType:
class: Chill\PersonBundle\Form\Type\ClosingMotivePickerType
arguments:
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
@@ -28,60 +26,3 @@ services:
$config: "%chill_person.accompanying_period_fields%"
tags:
- { name: form.type }
-
- chill.person.form.type.pick_person:
- class: Chill\PersonBundle\Form\Type\PickPersonType
- arguments:
- - '@Chill\PersonBundle\Repository\PersonRepository'
- - "@security.token_storage"
- - "@chill.main.security.authorization.helper"
- - '@Symfony\Component\Routing\Generator\UrlGeneratorInterface'
- - '@Symfony\Component\Translation\TranslatorInterface'
- tags:
- - { name: form.type }
-
- Chill\PersonBundle\Form\Type\PersonAltNameType:
- arguments:
- $configHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
- $translatableStringHelper: '@chill.main.helper.translatable_string'
- tags:
- - { name: form.type }
-
- Chill\PersonBundle\Form\Type\PersonPhoneType:
- arguments:
- $phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
- $em: '@Doctrine\ORM\EntityManagerInterface'
- tags:
- - { name: form.type }
-
- Chill\PersonBundle\Form\SocialWork\SocialIssueType:
- arguments:
- $translatableStringHelper: '@chill.main.helper.translatable_string'
- tags:
- - { name: form.type }
-
- Chill\PersonBundle\Form\SocialWork\SocialActionType:
- arguments:
- $translatableStringHelper: '@chill.main.helper.translatable_string'
- tags:
- - { name: form.type }
-
- Chill\PersonBundle\Form\SocialWork\EvaluationType:
- arguments:
- $translatableStringHelper: '@chill.main.helper.translatable_string'
- tags:
- - { name: form.type }
-
-
- Chill\PersonBundle\Form\SocialWork\GoalType:
- arguments:
- $translatableStringHelper: '@chill.main.helper.translatable_string'
- tags:
- - { name: form.type }
-
-
- Chill\PersonBundle\Form\SocialWork\ResultType:
- arguments:
- $translatableStringHelper: '@chill.main.helper.translatable_string'
- tags:
- - { name: form.type }
diff --git a/src/Bundle/ChillPersonBundle/config/services/menu.yaml b/src/Bundle/ChillPersonBundle/config/services/menu.yaml
index 344eb2a17..a9f566466 100644
--- a/src/Bundle/ChillPersonBundle/config/services/menu.yaml
+++ b/src/Bundle/ChillPersonBundle/config/services/menu.yaml
@@ -19,14 +19,7 @@ services:
# - { name: 'chill.menu_builder' }
#
Chill\PersonBundle\Menu\PersonMenuBuilder:
- arguments:
- $showAccompanyingPeriod: '%chill_person.accompanying_period%'
- $translator: '@Symfony\Contracts\Translation\TranslatorInterface'
+ autowire: true
+ autoconfigure: true
tags:
- { name: 'chill.menu_builder' }
-
- # Chill\PersonBundle\Menu\AccompanyingCourseMenuBuilder:
- # arguments:
- # $translator: '@Symfony\Contracts\Translation\TranslatorInterface'
- # tags:
- # - { name: 'chill.menu_builder' }
diff --git a/src/Bundle/ChillPersonBundle/config/services/repository.yaml b/src/Bundle/ChillPersonBundle/config/services/repository.yaml
index 72ca27e24..b4d179dd1 100644
--- a/src/Bundle/ChillPersonBundle/config/services/repository.yaml
+++ b/src/Bundle/ChillPersonBundle/config/services/repository.yaml
@@ -1,6 +1,12 @@
services:
+ # note: the services.yaml file define some autoloading
+
chill.person.repository.person:
class: Chill\PersonBundle\Repository\PersonRepository
autowire: true
autoconfigure: true
Chill\PersonBundle\Repository\PersonRepository: '@chill.person.repository.person'
+
+ Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface: '@Chill\PersonBundle\Repository\PersonACLAwareRepository'
+
+ Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepositoryInterface: '@Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepository'
diff --git a/src/Bundle/ChillPersonBundle/config/services/search.yaml b/src/Bundle/ChillPersonBundle/config/services/search.yaml
index 284773196..6a7cd4496 100644
--- a/src/Bundle/ChillPersonBundle/config/services/search.yaml
+++ b/src/Bundle/ChillPersonBundle/config/services/search.yaml
@@ -1,25 +1,11 @@
services:
- chill.person.search_person:
- class: Chill\PersonBundle\Search\PersonSearch
- arguments:
- - "@doctrine.orm.entity_manager"
- - "@security.token_storage"
- - "@chill.main.security.authorization.helper"
- - "@chill_main.paginator_factory"
- calls:
- - ['setContainer', ["@service_container"]]
+ Chill\PersonBundle\Search\PersonSearch:
+ autowire: true
tags:
- { name: chill.search, alias: 'person_regular' }
Chill\PersonBundle\Search\SimilarityPersonSearch:
- arguments:
- - "@doctrine.orm.entity_manager"
- - "@security.token_storage"
- - "@chill.main.security.authorization.helper"
- - "@chill_main.paginator_factory"
- - '@chill.person.search_person'
- calls:
- - ['setContainer', ["@service_container"]]
+ autowire: true
tags:
- { name: chill.search, alias: 'person_similarity' }
diff --git a/src/Bundle/ChillPersonBundle/config/services/security.yaml b/src/Bundle/ChillPersonBundle/config/services/security.yaml
index 21590fcda..6dc3c92d3 100644
--- a/src/Bundle/ChillPersonBundle/config/services/security.yaml
+++ b/src/Bundle/ChillPersonBundle/config/services/security.yaml
@@ -1,16 +1,14 @@
services:
chill.person.security.authorization.person:
+ autowire: true
class: Chill\PersonBundle\Security\Authorization\PersonVoter
- arguments:
- - "@chill.main.security.authorization.helper"
tags:
- { name: security.voter }
- { name: chill.role }
Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter:
- arguments:
- - "@chill.main.security.authorization.helper"
+ autowire: true
tags:
- { name: security.voter }
- { name: chill.role }
-
+
diff --git a/src/Bundle/ChillPersonBundle/config/validation.yaml b/src/Bundle/ChillPersonBundle/config/validation.yaml
index 23369a467..c41513778 100644
--- a/src/Bundle/ChillPersonBundle/config/validation.yaml
+++ b/src/Bundle/ChillPersonBundle/config/validation.yaml
@@ -1,70 +1,3 @@
-Chill\PersonBundle\Entity\Person:
- properties:
- firstName:
- - NotBlank:
- groups: [general, creation]
- - Length:
- min: 2
- max: 255
- minMessage: 'This name is too short. It must containt {{ limit }} chars'
- maxMessage: 'This name is too long. It must containt {{ limit }} chars'
- groups: [general, creation]
- lastName:
- - NotBlank:
- groups: [general, creation]
- - Length:
- min: 2
- max: 255
- minMessage: 'This name is too short. It must containt {{ limit }} chars'
- maxMessage: 'This name is too long. It must containt {{ limit }} chars'
- groups: [general, creation]
- birthdate:
- - Date:
- message: 'Birthdate not valid'
- groups: [general, creation]
- - Chill\PersonBundle\Validator\Constraints\Birthdate:
- groups: [general, creation]
- gender:
- - NotNull:
- groups: [general, creation]
- #accompanyingPeriods:
- # - Valid:
- # traverse: true
- email:
- - Email:
- groups: [general, creation]
- message: 'The email is not valid'
- checkMX: true
- phonenumber:
- - Regex:
- pattern: '/^([\+{1}])([0-9\s*]{4,20})$/'
- groups: [general, creation]
- message: 'Invalid phone number: it should begin with the international prefix starting with "+", hold only digits and be smaller than 20 characters. Ex: +33123456789'
- - Chill\MainBundle\Validation\Constraint\PhonenumberConstraint:
- type: landline
- groups: [ general, creation ]
- mobilenumber:
- - Regex:
- pattern: '/^([\+{1}])([0-9\s*]{4,20})$/'
- groups: [general, creation]
- message: 'Invalid phone number: it should begin with the international prefix starting with "+", hold only digits and be smaller than 20 characters. Ex: +33623456789'
- - Chill\MainBundle\Validation\Constraint\PhonenumberConstraint:
- type: mobile
- groups: [ general, creation ]
- otherPhoneNumbers:
- - Valid:
- traverse: true
- constraints:
- - Callback:
- callback: isAccompanyingPeriodValid
- groups: [accompanying_period_consistent]
- - Callback:
- callback: isAddressesValid
- groups: [addresses_consistent]
- - Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequential:
- groups: [ 'household_memberships' ]
-
-
Chill\PersonBundle\Entity\AccompanyingPeriod:
properties:
openingDate:
diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210831140339.php b/src/Bundle/ChillPersonBundle/migrations/Version20210831140339.php
new file mode 100644
index 000000000..dd3a60d75
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/migrations/Version20210831140339.php
@@ -0,0 +1,29 @@
+addSql('ALTER TABLE chill_person_person ALTER center_id DROP NOT NULL');
+ }
+
+ public function down(Schema $schema): void
+ {
+ $this->addSql('ALTER TABLE chill_person_person ALTER center_id SET NOT NULL');
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210910161858.php b/src/Bundle/ChillPersonBundle/migrations/Version20210910161858.php
new file mode 100644
index 000000000..82cade6cf
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/migrations/Version20210910161858.php
@@ -0,0 +1,31 @@
+addSql('DROP INDEX fullnamecanonical_trgm_idx');
+ $this->addSql('CREATE INDEX fullnameCanonical_trgm_idx ON chill_person_person USING GIST (center_id, fullnameCanonical gist_trgm_ops)');
+ }
+
+ public function down(Schema $schema): void
+ {
+ $this->addSql('DROP INDEX fullnamecanonical_trgm_idx');
+ $this->addSql('CREATE INDEX fullnameCanonical_trgm_idx ON chill_person_person USING GIST (fullnameCanonical gist_trgm_ops)');
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210915093624.php b/src/Bundle/ChillPersonBundle/migrations/Version20210915093624.php
new file mode 100644
index 000000000..f285fde47
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/migrations/Version20210915093624.php
@@ -0,0 +1,49 @@
+addSql("CREATE VIEW view_chill_person_current_address AS
+ SELECT
+ cphm.person_id AS person_id,
+ cma.id AS address_id,
+ CASE WHEN cphm.startdate > COALESCE(cma.validfrom, '-infinity'::date) THEN cphm.startdate ELSE cma.validfrom END AS valid_from,
+ CASE WHEN COALESCE(cphm.enddate, 'infinity'::date) < COALESCE(cma.validto, 'infinity'::date) THEN cphm.enddate ELSE cma.validto END AS valid_to
+ FROM chill_person_household_members AS cphm
+ LEFT JOIN chill_person_household_to_addresses AS cphta ON cphta.household_id = cphm.household_id
+ LEFT JOIN chill_main_address AS cma ON cphta.address_id = cma.id
+ WHERE
+ cphm.sharedhousehold IS TRUE
+ AND
+ current_date between cphm.startdate AND coalesce(enddate, 'infinity'::date)
+ AND
+ current_date between cma.validfrom AND coalesce(validto, 'infinity'::date)
+ ");
+
+ $this->addSql("CREATE INDEX chill_custom_main_address_filtering_idx ON chill_main_address USING btree (id, validfrom ASC, validto DESC)");
+ $this->addSql("CREATE INDEX chill_custom_person_household_members_sharing_idx ON chill_person_household_members USING btree (person_id, startdate ASC, enddate DESC, household_id) WHERE sharedhousehold IS TRUE");
+ }
+
+ public function down(Schema $schema): void
+ {
+ $this->addSql("DROP VIEW view_chill_person_current_address");
+ $this->addSql("DROP INDEX chill_custom_main_address_filtering_idx");
+ $this->addSql("DROP INDEX chill_custom_person_household_members_sharing_idx");
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml
index 05738effb..cea908ccf 100644
--- a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml
+++ b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml
@@ -19,10 +19,13 @@ household:
many {Montrer # anciennes ou futures appartenances}
other {Montrer # anciennes ou futures appartenances}
}
+ Hide memberships: Masquer
Those members does not share address: Ces usagers ne partagent pas l'adresse du ménage.
Any persons into this position: Aucune personne n'appartient au ménage à cette position.
Leave household: Quitter le ménage
- Leave: Quitter
+ Leave: Détacher
+ person:
+ leave: L'usager quitte le ménage
Join: Rejoindre un ménage
Change position: Repositionner
Household file: Dossier ménage
@@ -36,10 +39,11 @@ household:
is not holder: N'est pas titulaire
holder: Titulaire
Edit member household: Modifier l'appartenance au ménage
+ Edit his household: Modifier son appartenance au ménage
Current household members: Membres actuels
Household summary: Résumé du ménage
Accompanying period: Parcours d'accompagnement
- Addresses: Adresses
+ Addresses: Historique adresse
Current address: Adresse actuelle
Household does not have any address currently: Le ménage n'a pas d'adresse renseignée actuellement
Edit household members: Modifier l'appartenance au ménage
@@ -52,7 +56,8 @@ household:
Expecting for birth on date: Naissance attendue pour le {date}
Expecting for birth: Naissance attendue (date inconnue)
Any expecting birth: Aucune naissance proche n'a été renseignée.
- Comment and expecting birth: Commentaire et naissance attendue
+ New comment and expecting birth: Écrire un commentaire
+ Edit comment and expecting birth: Mettre à jour le commentaire
Edit member metadata: Données supplémentaires
comment_membership: Commentaire général sur les membres
expecting_birth: Naissance attendue ?
diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml
index 54c1f3818..8b6b6db58 100644
--- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml
+++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml
@@ -119,7 +119,8 @@ address_country_code: Code pays
'Alreay existing person': 'Dossiers déjà encodés'
'Add the person': 'Ajouter la personne'
-'Add the person and create an accompanying period': "Ajouter la personne et créer une période d'accompagnement"
+'Add the person and create an accompanying period': "Créer la personne ET créer une période d'accompagnement"
+'Add the person and create an household': "Créer la personne ET créer un ménage"
Show person: Voir le dossier de la personne
'Confirm the creation': 'Confirmer la création'
'You will create this person': 'Vous allez créer le dossier suivant'
@@ -372,7 +373,7 @@ Confirmed: en file active
# Accompanying Course
Accompanying Course: Parcours d'accompagnement
-Accompanying Course Details: Détails du parcours
+Accompanying Course History: Historique du parcours
Resume Accompanying Course: Résumé du parcours
Show Accompanying Course: Voir le parcours
Edit Accompanying Course: Modifier le parcours
diff --git a/src/Bundle/ChillTaskBundle/Menu/UserMenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/UserMenuBuilder.php
index 33e84949f..632961953 100644
--- a/src/Bundle/ChillTaskBundle/Menu/UserMenuBuilder.php
+++ b/src/Bundle/ChillTaskBundle/Menu/UserMenuBuilder.php
@@ -115,6 +115,17 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
'icon' => 'tasks'
]);
+ $menu->addChild("My calendar list", [
+ 'route' => 'chill_calendar_calendar_list',
+ 'routeParameters' => [
+ 'user_id' => $user->getId(),
+ ]
+ ])
+ ->setExtras([
+ 'order' => -9,
+ 'icon' => 'tasks'
+ ]);
+
/*
$menu->addChild("My aside activities", [
'route' => 'chill_crud_aside_activity_index'
diff --git a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php
index 8b8bce839..2f760a84b 100644
--- a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php
+++ b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php
@@ -18,7 +18,12 @@
namespace Chill\TaskBundle\Security\Authorization;
+use Chill\EventBundle\Entity\Event;
+use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
+use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
+use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
+use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\TaskBundle\Entity\AbstractTask;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
@@ -32,12 +37,7 @@ use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Chill\TaskBundle\Security\Authorization\AuthorizationEvent;
-/**
- *
- *
- * @author Julien Fastré
- */
-class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
+final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
const CREATE = 'CHILL_TASK_TASK_CREATE';
const UPDATE = 'CHILL_TASK_TASK_UPDATE';
@@ -51,50 +51,52 @@ class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterf
self::DELETE
];
- /**
- *
- * @var AuthorizationHelper
- */
- protected $authorizationHelper;
+ protected AuthorizationHelper $authorizationHelper;
- /**
- *
- * @var AccessDecisionManagerInterface
- */
- protected $accessDecisionManager;
+ protected AccessDecisionManagerInterface $accessDecisionManager;
- /**
- *
- * @var LoggerInterface
- */
- protected $logger;
-
- /**
- *
- * @var EventDispatcherInterface
- */
- protected $eventDispatcher;
+ protected LoggerInterface $logger;
+
+ protected EventDispatcherInterface $eventDispatcher;
+
+ protected CenterResolverDispatcher $centerResolverDispatcher;
+
+ protected VoterHelperInterface $voter;
public function __construct(
AccessDecisionManagerInterface $accessDecisionManager,
AuthorizationHelper $authorizationHelper,
EventDispatcherInterface $eventDispatcher,
- LoggerInterface $logger
+ LoggerInterface $logger,
+ CenterResolverDispatcher $centerResolverDispatcher,
+ VoterHelperFactoryInterface $voterFactory
) {
$this->accessDecisionManager = $accessDecisionManager;
$this->authorizationHelper = $authorizationHelper;
$this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
+ $this->centerResolverDispatcher = $centerResolverDispatcher;
+
+ $this->voter = $voterFactory
+ ->generate(AbstractTask::class)
+ ->addCheckFor(AbstractTask::class, self::ROLES)
+ ->addCheckFor(Person::class, [self::SHOW])
+ ->addCheckFor(null, [self::SHOW])
+ ->build()
+ ;
}
public function supports($attribute, $subject)
{
+ return $this->voter->supports($attribute, $subject);
+ /*
return ($subject instanceof AbstractTask && in_array($attribute, self::ROLES))
||
($subject instanceof Person && \in_array($attribute, [ self::CREATE, self::SHOW ]))
||
(NULL === $subject && $attribute === self::SHOW )
;
+ */
}
/**
@@ -111,40 +113,60 @@ class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterf
if (!$token->getUser() instanceof User) {
return false;
}
-
+
$event = new AuthorizationEvent($subject, $attribute, $token);
-
+
$this->eventDispatcher->dispatch(AuthorizationEvent::VOTE, $event);
-
+
if ($event->hasVote()) {
-
+
$this->logger->debug("The TaskVoter is overriding by "
.AuthorizationEvent::VOTE, [
'vote' => $event->getVote(),
'task_id' => $subject->getId()
]);
-
+
return $event->getVote();
}
+ // do pre-flight check, relying on other decision manager
+ // those pre-flight check concern associated entities
+ if ($subject instanceof AbstractTask) {
+ if (NULL !== $person = $subject->getPerson()) {
+ if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
+ return false;
+ }
+ } elseif (false) {
+ // here will come the test if the task is associated to an accompanying course
+ }
+ }
+
+ // do regular check.
+ return $this->voter->voteOnAttribute($attribute, $subject, $token);
+
+ /*
if ($subject instanceof AbstractTask) {
if ($subject->getPerson() === null) {
throw new \LogicException("You should associate a person with task "
. "in order to check autorizations");
}
-
+
$person = $subject->getPerson();
} elseif ($subject instanceof Person) {
$person = $subject;
} else {
// subject is null. We check that at least one center is reachable
$centers = $this->authorizationHelper->getReachableCenters($token->getUser(), new Role($attribute));
-
+
return count($centers) > 0;
}
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
+ return false;
+ }
+ $center = $this->centerResolverDispatcher->resolveCenter($subject);
+ if (NULL === $center) {
return false;
}
@@ -153,6 +175,7 @@ class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterf
$subject,
$attribute
);
+ */
}
public function getRoles()
diff --git a/src/Bundle/ChillTaskBundle/config/services/security.yaml b/src/Bundle/ChillTaskBundle/config/services/security.yaml
index e58fd19e3..c1d176637 100644
--- a/src/Bundle/ChillTaskBundle/config/services/security.yaml
+++ b/src/Bundle/ChillTaskBundle/config/services/security.yaml
@@ -1,11 +1,7 @@
services:
chill_task.task_voter:
class: Chill\TaskBundle\Security\Authorization\TaskVoter
- arguments:
- - "@security.access.decision_manager"
- - "@chill.main.security.authorization.helper"
- - '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
- - "@logger"
+ autowire: true
+ autoconfigure: true
tags:
- - { name: security.voter }
- { name: chill.role }
diff --git a/src/Bundle/ChillThirdPartyBundle/DependencyInjection/ChillThirdPartyExtension.php b/src/Bundle/ChillThirdPartyBundle/DependencyInjection/ChillThirdPartyExtension.php
index 7ada8db8e..9b23222c6 100644
--- a/src/Bundle/ChillThirdPartyBundle/DependencyInjection/ChillThirdPartyExtension.php
+++ b/src/Bundle/ChillThirdPartyBundle/DependencyInjection/ChillThirdPartyExtension.php
@@ -8,6 +8,8 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter;
+use Symfony\Component\HttpFoundation\Request;
+
/**
* This is the class that loads and manages your bundle configuration.
@@ -42,19 +44,50 @@ class ChillThirdPartyExtension extends Extension implements PrependExtensionInte
$this->preprendRoutes($container);
$this->prependRoleHierarchy($container);
}
-
+
protected function preprendRoutes(ContainerBuilder $container)
{
//declare routes for 3party bundle
$container->prependExtensionConfig('chill_main', array(
- 'routing' => array(
+ 'routing' => [
'resources' => array(
'@ChillThirdPartyBundle/config/routes.yaml'
)
- )
+ ],
+ 'apis' => [
+ [
+ 'class' => \Chill\ThirdPartyBundle\Entity\ThirdParty::class,
+ 'name' => 'thirdparty',
+ 'base_path' => '/api/1.0/thirdparty/thirdparty',
+ 'base_role' => \Chill\ThirdPartyBundle\Security\Authorization\ThirdPartyVoter::class,
+ //'controller' => \Chill\ThirdPartyBundle\Controller\ThirdPartyApiController::class,
+ 'actions' => [
+ '_index' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true,
+ Request::METHOD_POST => true,
+ ],
+ ],
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true,
+ Request::METHOD_POST=> true,
+ ],
+ 'roles' => [
+ Request::METHOD_GET => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::SHOW,
+ Request::METHOD_HEAD => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::SHOW,
+ Request::METHOD_POST => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::CREATE,
+
+ ],
+ ]
+ ]
+ ]
+ ],
));
}
-
+
protected function prependRoleHierarchy(ContainerBuilder $container)
{
$container->prependExtensionConfig('security', array(
diff --git a/src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php b/src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php
index 277202324..0b0b3e56b 100644
--- a/src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php
+++ b/src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php
@@ -30,6 +30,7 @@ use Chill\MainBundle\Entity\Center;
use Symfony\Component\Validator\Constraints as Assert;
use Chill\MainBundle\Entity\Address;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
+use Symfony\Component\Serializer\Annotation\Groups;
/**
* ThirdParty is a party recorded in the database.
@@ -59,6 +60,7 @@ class ThirdParty
* @var string
* @ORM\Column(name="name", type="string", length=255)
* @Assert\Length(min="2")
+ * @Groups({"read"})
*/
private $name;
@@ -128,6 +130,7 @@ class ThirdParty
* @Assert\Regex("/^([\+{1}])([0-9\s*]{4,20})$/",
* message="Invalid phone number: it should begin with the international prefix starting with ""+"", hold only digits and be smaller than 20 characters. Ex: +33123456789"
* )
+ * @Groups({"read"})
*/
private $telephone;
@@ -135,6 +138,7 @@ class ThirdParty
* @var string|null
* @ORM\Column(name="email", type="string", length=255, nullable=true)
* @Assert\Email(checkMX=false)
+ * @Groups({"read"})
*/
private $email;
@@ -143,10 +147,12 @@ class ThirdParty
* @ORM\ManyToOne(targetEntity="\Chill\MainBundle\Entity\Address",
* cascade={"persist", "remove"})
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
+ * @Groups({"read"})
*/
private $address;
/**
+ * Soft-delete flag
* @var boolean
* @ORM\Column(name="active", type="boolean", options={"defaut": true})
*/
@@ -493,11 +499,28 @@ class ThirdParty
return $this;
}
+ /**
+ * Mechanism to differentiate children and parents
+ */
public function isLeaf(): bool
{
return $this->children->count() !== 0;
}
+ /**
+ * isLeaf aliases
+ */
+ public function isChild():bool
+ {
+ return $this->isLeaf();
+ }
+
+ public function isParent():bool
+ {
+ return !$this->isLeaf();
+ }
+
+
/**
* @return Collection
*/
@@ -522,7 +545,8 @@ class ThirdParty
*/
public function removeChild(ThirdParty $child): self
{
- $this->categories->removeElement($child);
+ $this->children->removeElement($child);
+ $this->active = false;
return $this;
}
diff --git a/src/Bundle/ChillThirdPartyBundle/Form/ThirdPartyType.php b/src/Bundle/ChillThirdPartyBundle/Form/ThirdPartyType.php
index 9db5013c3..2ec113eb9 100644
--- a/src/Bundle/ChillThirdPartyBundle/Form/ThirdPartyType.php
+++ b/src/Bundle/ChillThirdPartyBundle/Form/ThirdPartyType.php
@@ -2,6 +2,7 @@
namespace Chill\ThirdPartyBundle\Form;
+use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\ThirdPartyBundle\Entity\ThirdPartyCategory;
@@ -9,7 +10,9 @@ use Chill\ThirdPartyBundle\Entity\ThirdPartyCivility;
use Chill\ThirdPartyBundle\Entity\ThirdPartyProfession;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
+use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
@@ -21,8 +24,7 @@ use Chill\ThirdPartyBundle\ThirdPartyType\ThirdPartyTypeManager;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
-use Symfony\Component\Form\Extension\Core\Type\TextareaType;
-use Chill\MainBundle\Form\Type\AddressType;
+use Symfony\Component\Form\Extension\Core\Type\HiddenType;
class ThirdPartyType extends AbstractType
@@ -35,16 +37,20 @@ class ThirdPartyType extends AbstractType
protected TranslatableStringHelper $translatableStringHelper;
+ protected ObjectManager $om;
+
public function __construct(
AuthorizationHelper $authorizationHelper,
TokenStorageInterface $tokenStorage,
ThirdPartyTypeManager $typesManager,
- TranslatableStringHelper $translatableStringHelper
+ TranslatableStringHelper $translatableStringHelper,
+ ObjectManager $om
) {
$this->authorizationHelper = $authorizationHelper;
$this->tokenStorage = $tokenStorage;
$this->typesManager = $typesManager;
$this->translatableStringHelper = $translatableStringHelper;
+ $this->om = $om;
}
/**
@@ -56,6 +62,35 @@ class ThirdPartyType extends AbstractType
foreach ($this->typesManager->getProviders() as $key => $provider) {
$types['chill_3party.key_label.'.$key] = $key;
}
+ if (count($types) === 1) {
+ $builder
+ ->add('type', HiddenType::class, [
+ 'data' => array_values($types)
+ ])
+ ->get('type')
+ ->addModelTransformer(new CallbackTransformer(
+ function (?array $typeArray): ?string {
+ if (null === $typeArray) {
+ return null;
+ }
+ return implode(',', $typeArray);
+ },
+ function (?string $typeStr): ?array {
+ if (null === $typeStr) {
+ return null;
+ }
+ return explode(',', $typeStr);
+ }
+ ))
+ ;
+ } else {
+ $builder->add('type', ChoiceType::class, [
+ 'choices' => $types,
+ 'expanded' => true,
+ 'multiple' => true,
+ 'label' => 'thirdparty.Type'
+ ]);
+ }
$builder
->add('name', TextType::class, [
@@ -75,12 +110,6 @@ class ThirdPartyType extends AbstractType
'multiple' => true,
'attr' => ['class' => 'select2']
])
- ->add('type', ChoiceType::class, [
- 'choices' => $types,
- 'expanded' => true,
- 'multiple' => true,
- 'label' => 'thirdparty.Type'
- ])
->add('telephone', TextType::class, [
'label' => 'Phonenumber',
'required' => false
@@ -88,11 +117,6 @@ class ThirdPartyType extends AbstractType
->add('email', EmailType::class, [
'required' => false
])
- ->add('address', AddressType::class, [
- 'has_valid_from' => false,
- 'null_if_empty' => true,
- 'required' => false
- ])
->add('active', ChoiceType::class, [
'label' => 'thirdparty.Status',
'choices' => [
@@ -113,8 +137,29 @@ class ThirdPartyType extends AbstractType
])
;
+ $builder
+ ->add('address', HiddenType::class)
+ ->get('address')
+ ->addModelTransformer(new CallbackTransformer(
+ function (?Address $address): string {
+ if (null === $address) {
+ return '';
+ }
+ return $address->getId();
+ },
+ function (?string $addressId): ?Address {
+ if (null === $addressId) {
+ return null;
+ }
+ return $this->om
+ ->getRepository(Address::class)
+ ->findOneBy(['id' => (int) $addressId]);
+ }
+ ))
+ ;
+
// Contact Person ThirdParty (child)
- if ($options['data']->isLeaf()) {
+ if ($options['data']->isChild()) {
$builder
->add('civility', EntityType::class, [
'label' => 'thirdparty.Civility',
diff --git a/src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_api/OnTheFly.js b/src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_api/OnTheFly.js
new file mode 100644
index 000000000..ed91ef415
--- /dev/null
+++ b/src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_api/OnTheFly.js
@@ -0,0 +1,36 @@
+/*
+* GET a thirdparty by id
+*/
+const getThirdparty = (id) => {
+ const url = `/api/1.0/thirdparty/thirdparty/${id}.json`;
+ return fetch(url)
+ .then(response => {
+ if (response.ok) {
+ return response.json();
+ }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* POST a new person
+*/
+const postThirdparty = (body) => {
+ const url = `/api/1.0/thirdparty/thirdparty.json`;
+ return fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json;charset=utf-8'
+ },
+ body: JSON.stringify(body)
+ })
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+export {
+ getThirdparty,
+ postThirdparty
+};
\ No newline at end of file
diff --git a/src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/Entity/ThirdPartyRenderBox.vue b/src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/Entity/ThirdPartyRenderBox.vue
index 8db40f9ec..720d9903b 100644
--- a/src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/Entity/ThirdPartyRenderBox.vue
+++ b/src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/Entity/ThirdPartyRenderBox.vue
@@ -26,30 +26,35 @@
{{ birthdate }} - {{ deathdate }}
- {{ thirdparty.age }}
+ {{ thirdparty.age }}