mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-11-03 18:58:24 +00:00 
			
		
		
		
	Merge branch 'master' into 'bugfix/acl-add-roles-misc-places' [ci-skip]
# Conflicts: # CHANGELOG.md
This commit is contained in:
		
							
								
								
									
										16
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -12,6 +12,10 @@ and this project adheres to
 | 
			
		||||
 | 
			
		||||
<!-- write down unreleased development here -->
 | 
			
		||||
 | 
			
		||||
* [person]: Add civility to the person
 | 
			
		||||
* [person]: Various improvements on the edit person form
 | 
			
		||||
* [person]: Set available_languages and available_countries as parameters for use in the edit person form
 | 
			
		||||
* [activity] Bugfix: documents can now be added to an activity.
 | 
			
		||||
* [tasks] improve tasks with filter order
 | 
			
		||||
* [tasks] refactor singleControllerTasks: limit the number of conditions from the context
 | 
			
		||||
* [validations] validation of accompanying period added: no duplicate participations or resources (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/60).
 | 
			
		||||
@@ -22,9 +26,21 @@ and this project adheres to
 | 
			
		||||
* [activity]: perform client-side validation & show/hide fields in the "new location" modal
 | 
			
		||||
* [docstore] voter for PersonDocument and AccompanyingCourseDocument on the 2.0 way (using VoterHelperFactory)
 | 
			
		||||
* [docstore] add authorization check inside controller and menu
 | 
			
		||||
* [activity]: fix inheritance for role `ACTIVITY FULL` and add missing acl in menu
 | 
			
		||||
* [person] show current address in search results
 | 
			
		||||
* [person] show alt names in search results
 | 
			
		||||
* [admin]: links to activity admin section added again.
 | 
			
		||||
* [household]: endDate field deleted from household edit form.
 | 
			
		||||
* [household]: View accompanying periods of current and old household members.
 | 
			
		||||
* [tasks]: different layout for task list / my tasks, and fix link to tasks in alert or in warning
 | 
			
		||||
* [admin]: links to activity admin section added again.
 | 
			
		||||
* [household]: household addresses ordered by ValidFrom date and by id to show the last created address on top.
 | 
			
		||||
* [socialWorkAction]: display of social issue and parent issues + banner context added. 
 | 
			
		||||
* [DBAL dependencies] Upgrade to DBAL 3.1
 | 
			
		||||
 | 
			
		||||
## Test releases
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Test release 2021-10-27
 | 
			
		||||
 | 
			
		||||
* [person]: delete double actions buttons on search person page
 | 
			
		||||
 
 | 
			
		||||
@@ -87,7 +87,9 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
 | 
			
		||||
              ActivityVoter::UPDATE => array(ActivityVoter::SEE_DETAILS),
 | 
			
		||||
              ActivityVoter::CREATE => array(ActivityVoter::SEE_DETAILS),
 | 
			
		||||
              ActivityVoter::DELETE => array(ActivityVoter::SEE_DETAILS),
 | 
			
		||||
              ActivityVoter::SEE_DETAILS => array(ActivityVoter::SEE)
 | 
			
		||||
              ActivityVoter::SEE_DETAILS => array(ActivityVoter::SEE),
 | 
			
		||||
              ActivityVoter::FULL => [ActivityVoter::CREATE, ActivityVoter::DELETE,
 | 
			
		||||
                   ActivityVoter::UPDATE],
 | 
			
		||||
           )
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -158,7 +158,7 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer
 | 
			
		||||
    private ?Collection $thirdParties = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\ManyToMany(targetEntity="Chill\DocStoreBundle\Entity\StoredObject")
 | 
			
		||||
     * @ORM\ManyToMany(targetEntity="Chill\DocStoreBundle\Entity\StoredObject", cascade={"persist"})
 | 
			
		||||
     */
 | 
			
		||||
    private Collection $documents;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,19 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 *  License, or (at your option) any later version.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
@@ -44,7 +44,7 @@ class ActivityReason
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
@@ -108,7 +108,7 @@ class ActivityReason
 | 
			
		||||
            return $this->name;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set category of the reason. If you set to the reason an inactive
 | 
			
		||||
     * category, the reason will become inactive
 | 
			
		||||
@@ -121,7 +121,7 @@ class ActivityReason
 | 
			
		||||
        if($this->category !== $category && ! $category->getActive()) {
 | 
			
		||||
            $this->setActive(False);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $this->category = $category;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,17 +2,17 @@
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 *  License, or (at your option) any later version.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
@@ -43,7 +43,7 @@ class ActivityReasonCategory
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
@@ -52,7 +52,7 @@ class ActivityReasonCategory
 | 
			
		||||
     * @ORM\Column(type="boolean")
 | 
			
		||||
     */
 | 
			
		||||
    private $active = true;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Array of ActivityReason
 | 
			
		||||
     * @var ArrayCollection
 | 
			
		||||
@@ -61,7 +61,7 @@ class ActivityReasonCategory
 | 
			
		||||
     *     mappedBy="category")
 | 
			
		||||
     */
 | 
			
		||||
    private $reasons;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * ActivityReasonCategory constructor.
 | 
			
		||||
     */
 | 
			
		||||
@@ -69,7 +69,7 @@ class ActivityReasonCategory
 | 
			
		||||
    {
 | 
			
		||||
        $this->reasons = new ArrayCollection();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
@@ -139,9 +139,9 @@ class ActivityReasonCategory
 | 
			
		||||
                $reason->setActive($active);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $this->active = $active;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ class ActivityType
 | 
			
		||||
    private ?int $id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private array $name = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ class ActivityTypeCategory
 | 
			
		||||
    private ?int $id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private array $name = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ class TranslatableActivityType extends AbstractType
 | 
			
		||||
 | 
			
		||||
        if ($options['active_only'] === true) {
 | 
			
		||||
            $qb->where($qb->expr()->eq('at.active', ':active'));
 | 
			
		||||
            $qb->setParameter('active', true, \Doctrine\DBAL\Types\Type::BOOLEAN);
 | 
			
		||||
            $qb->setParameter('active', true, \Doctrine\DBAL\Types\Types::BOOLEAN);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,29 +2,27 @@
 | 
			
		||||
 | 
			
		||||
namespace Chill\ActivityBundle\Menu;
 | 
			
		||||
 | 
			
		||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
 | 
			
		||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
 | 
			
		||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
 | 
			
		||||
use Knp\Menu\MenuItem;
 | 
			
		||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\Security;
 | 
			
		||||
use Symfony\Contracts\Translation\TranslatorInterface;
 | 
			
		||||
 | 
			
		||||
class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
{
 | 
			
		||||
    protected TokenStorageInterface $tokenStorage;
 | 
			
		||||
 | 
			
		||||
    protected AuthorizationHelper $authorizationHelper;
 | 
			
		||||
 | 
			
		||||
    protected TranslatorInterface $translator;
 | 
			
		||||
 | 
			
		||||
    protected Security $security;
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        TokenStorageInterface $tokenStorage,
 | 
			
		||||
        AuthorizationHelper $authorizationHelper,
 | 
			
		||||
        Security $security,
 | 
			
		||||
        TranslatorInterface $translator
 | 
			
		||||
    ) {
 | 
			
		||||
        $this->security = $security;
 | 
			
		||||
        $this->translator = $translator;
 | 
			
		||||
        $this->authorizationHelper = $authorizationHelper;
 | 
			
		||||
        $this->tokenStorage = $tokenStorage;
 | 
			
		||||
    }
 | 
			
		||||
    public static function getMenuIds(): array
 | 
			
		||||
    {
 | 
			
		||||
@@ -35,7 +33,8 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
    {
 | 
			
		||||
        $period = $parameters['accompanyingCourse'];
 | 
			
		||||
 | 
			
		||||
        if (AccompanyingPeriod::STEP_DRAFT !== $period->getStep()) {
 | 
			
		||||
        if (AccompanyingPeriod::STEP_DRAFT !== $period->getStep()
 | 
			
		||||
            && $this->security->isGranted(ActivityVoter::SEE, $period)) {
 | 
			
		||||
            $menu->addChild($this->translator->trans('Activity'), [
 | 
			
		||||
                'route' => 'chill_activity_activity_list',
 | 
			
		||||
                'routeParameters' => [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\ActivityBundle\Menu;
 | 
			
		||||
 | 
			
		||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
 | 
			
		||||
use Knp\Menu\MenuItem;
 | 
			
		||||
use Symfony\Component\Security\Core\Security;
 | 
			
		||||
 | 
			
		||||
final class AdminMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
{
 | 
			
		||||
    private Security $security;
 | 
			
		||||
 | 
			
		||||
    public function __construct(Security $security)
 | 
			
		||||
    {
 | 
			
		||||
        $this->security = $security;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function getMenuIds(): array
 | 
			
		||||
    {
 | 
			
		||||
        return ['admin_index', 'admin_section', 'admin_activity'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function buildMenu($menuId, MenuItem $menu, array $parameters)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->security->isGranted('ROLE_ADMIN')) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (in_array($menuId, ['admin_index', 'admin_section'])) {
 | 
			
		||||
            $menu->addChild('Activities', [
 | 
			
		||||
                'route' => 'chill_admin_activity_index'
 | 
			
		||||
            ])
 | 
			
		||||
                ->setExtras([
 | 
			
		||||
                    'order' => 2000,
 | 
			
		||||
                    'explain' => "Activity configuration"
 | 
			
		||||
                ]);
 | 
			
		||||
        } else {
 | 
			
		||||
            $menu
 | 
			
		||||
                ->addChild('Activities', [
 | 
			
		||||
                    'route' => 'chill_admin_activity_index'
 | 
			
		||||
                ])
 | 
			
		||||
                ->setExtras([
 | 
			
		||||
                    'order' => '60'
 | 
			
		||||
                ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,7 +4,7 @@ namespace Chill\Migrations\Activity;
 | 
			
		||||
 | 
			
		||||
use Doctrine\Migrations\AbstractMigration;
 | 
			
		||||
use Doctrine\DBAL\Schema\Schema;
 | 
			
		||||
use Doctrine\DBAL\Types\Type;
 | 
			
		||||
use Doctrine\DBAL\Types\Types;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add an "active" column on activitytype table
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ final class Version20210401090853 extends AbstractMigration
 | 
			
		||||
        // this up() migration is auto-generated, please modify it to your needs
 | 
			
		||||
        $this->addSql('CREATE SEQUENCE activitytypecategory_id_seq INCREMENT BY 1 MINVALUE 1 START 1000');
 | 
			
		||||
        $this->addSql('CREATE TABLE activitytypecategory (id INT NOT NULL, name JSON NOT NULL, active BOOLEAN NOT NULL, PRIMARY KEY(id))');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN activitytypecategory.name IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('INSERT INTO activitytypecategory VALUES(1, \'{"fr": "Défaut", "en": "Default"}\', true)');
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,6 @@ final class Version20210408122329 extends AbstractMigration
 | 
			
		||||
        $this->addSql('ALTER TABLE activitytype ADD socialDataLabel VARCHAR(255) DEFAULT \'\' NOT NULL');
 | 
			
		||||
        $this->addSql('ALTER TABLE activitytype ALTER name SET NOT NULL');
 | 
			
		||||
        $this->addSql('ALTER TABLE activitytype ALTER active DROP DEFAULT');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN activitytype.name IS \'(DC2Type:json_array)\'');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema) : void
 | 
			
		||||
 
 | 
			
		||||
@@ -99,10 +99,13 @@ CHILL_ACTIVITY_LIST: Liste des activités
 | 
			
		||||
Activities: Activités
 | 
			
		||||
Activity configuration: Configuration des activités
 | 
			
		||||
Activity configuration menu: Configuration des activités
 | 
			
		||||
Activity Types: Types d'activité
 | 
			
		||||
Activity types: Types d'activité
 | 
			
		||||
Activity type configuration: Configuration des categories d'activités
 | 
			
		||||
Activity Reasons: Sujets d'une activité
 | 
			
		||||
Activity Reasons Category: Catégories de sujet d'activités
 | 
			
		||||
Activity Types Categories: Catégories des types d'activité
 | 
			
		||||
Activity Presences: Presences des activités
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Crud
 | 
			
		||||
crud:
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ final class AdminMenuBuilder implements \Chill\MainBundle\Routing\LocalMenuBuild
 | 
			
		||||
            ])
 | 
			
		||||
                ->setExtras([
 | 
			
		||||
                    'order' => 900,
 | 
			
		||||
                    'explain' => "Configure aside activities categories"
 | 
			
		||||
                    'explain' => "Aside activity type configuration"
 | 
			
		||||
                ]);
 | 
			
		||||
        } else {
 | 
			
		||||
            $menu
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{% extends "@ChillAsideActivity/Admin/layout_asideactivity.html.twig" %}
 | 
			
		||||
 | 
			
		||||
{% block admin_content %}
 | 
			
		||||
	<h1>{{ 'Aside Activity Type list'|trans }}</h1>
 | 
			
		||||
	<h1>{{ 'Aside Activity Type List'|trans }}</h1>
 | 
			
		||||
 | 
			
		||||
	<table class="records_list table table-bordered border-dark">
 | 
			
		||||
		<thead>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ Show the aside activity: Voir l'activité annexe
 | 
			
		||||
Edit the aside activity: Modifier l'activité annexe
 | 
			
		||||
Remove aside activity: Supprimer l'activité annexe
 | 
			
		||||
Aside activity: Activité annexe
 | 
			
		||||
Aside Activity Type List: Liste des catégories d'activités annexes
 | 
			
		||||
Duration time: Durée
 | 
			
		||||
durationTime: durée
 | 
			
		||||
user_username: nom de l'utilisateur
 | 
			
		||||
@@ -156,6 +157,11 @@ The activity has been successfully removed.: L'activité a été supprimée.
 | 
			
		||||
 | 
			
		||||
#Menu
 | 
			
		||||
Create an aside activity: "Créer une activité annexe"
 | 
			
		||||
Aside activity categories: Catégories des activités annexes
 | 
			
		||||
Aside activity configuration menu: "Menu de configuration des activités annexes"
 | 
			
		||||
Aside activity configuration: "Configuration des activités annexes"
 | 
			
		||||
Phonecall: "Appel téléphonique"
 | 
			
		||||
 | 
			
		||||
# admin
 | 
			
		||||
Aside activities: Activités annexes
 | 
			
		||||
Aside activity types: Types d'activités annexes
 | 
			
		||||
Aside activity type configuration: Configuration des categories d'activités annexes
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ class CancelReason
 | 
			
		||||
    private $canceledBy;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ class Invite
 | 
			
		||||
    private User $user;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private array $status = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -73,19 +73,14 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
    {
 | 
			
		||||
        $user = $this->tokenStorage->getToken()->getUser();
 | 
			
		||||
 | 
			
		||||
        if ($this->authorizationChecker->isGranted('ROLE_USER')
 | 
			
		||||
            && $user instanceof User
 | 
			
		||||
        ) {
 | 
			
		||||
            $menu->addChild("My calendar list", [
 | 
			
		||||
                    'route' => 'chill_calendar_calendar_list',
 | 
			
		||||
                    'routeParameters' => [
 | 
			
		||||
                        'user_id' => $user->getId(),
 | 
			
		||||
                    ]
 | 
			
		||||
                ])
 | 
			
		||||
                ->setExtras([
 | 
			
		||||
                    'order' => 9,
 | 
			
		||||
                    'icon' => 'tasks'
 | 
			
		||||
                ]);
 | 
			
		||||
        if ($this->authorizationChecker->isGranted('ROLE_USER')){
 | 
			
		||||
        $menu->addChild("My calendar list", [
 | 
			
		||||
                'route' => 'chill_calendar_calendar_list'
 | 
			
		||||
            ])
 | 
			
		||||
            ->setExtras([
 | 
			
		||||
                'order' => 9,
 | 
			
		||||
                'icon' => 'tasks'
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -50,10 +50,7 @@ final class Version20210715141731 extends AbstractMigration
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_calendar.calendar_range.startDate IS \'(DC2Type:date_immutable)\'');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_calendar.calendar_range.endDate IS \'(DC2Type:date_immutable)\'');
 | 
			
		||||
        $this->addSql('CREATE TABLE chill_calendar.cancel_reason (id INT NOT NULL, active BOOLEAN NOT NULL, canceledBy JSON NOT NULL, name JSON NOT NULL, PRIMARY KEY(id))');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_calendar.cancel_reason.name IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_calendar.cancel_reason.canceledBy IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('CREATE TABLE chill_calendar.invite (id INT NOT NULL, user_id INT DEFAULT NULL, status JSON NOT NULL, PRIMARY KEY(id))');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_calendar.invite.status IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('CREATE INDEX IDX_F517FFA7A76ED395 ON chill_calendar.invite (user_id)');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_calendar.calendar ADD CONSTRAINT FK_712315ACA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_calendar.calendar ADD CONSTRAINT FK_712315ACD7FA8EF0 FOREIGN KEY (accompanyingPeriod_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
 | 
			
		||||
@@ -74,7 +71,7 @@ final class Version20210715141731 extends AbstractMigration
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema): void
 | 
			
		||||
    {        
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_calendar.calendar_to_persons DROP CONSTRAINT FK_AEE94715A40A2C8');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_calendar.calendar_to_non_professionals DROP CONSTRAINT FK_FADF2C77A40A2C8');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_calendar.calendar_to_thirdparties DROP CONSTRAINT FK_2BAB7EFDA40A2C8');
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,5 @@ final class Version20210723074557 extends AbstractMigration
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_calendar.calendar_to_non_professionals ADD CONSTRAINT fk_fadf2c77217bbb47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_calendar.cancel_reason ALTER canceledBy TYPE JSON');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_calendar.cancel_reason ALTER canceledBy DROP DEFAULT');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_calendar.cancel_reason.canceledby IS \'(DC2Type:json_array)\'');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ class CustomField
 | 
			
		||||
     * @ORM\GeneratedValue(strategy="AUTO")
 | 
			
		||||
     */
 | 
			
		||||
    private $id;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     *
 | 
			
		||||
@@ -61,18 +61,18 @@ class CustomField
 | 
			
		||||
     * @ORM\Column(type="boolean")
 | 
			
		||||
     */
 | 
			
		||||
    private $active = true;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $options = array();
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
@@ -82,14 +82,14 @@ class CustomField
 | 
			
		||||
     * @ORM\Column(type="float")
 | 
			
		||||
     */
 | 
			
		||||
    private $ordering;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var boolean
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="boolean")
 | 
			
		||||
     */
 | 
			
		||||
    private $required = FALSE;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    const ONE_TO_ONE = 1;
 | 
			
		||||
    const ONE_TO_MANY = 2;
 | 
			
		||||
 | 
			
		||||
@@ -102,7 +102,7 @@ class CustomField
 | 
			
		||||
     */
 | 
			
		||||
    private $customFieldGroup;
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get id
 | 
			
		||||
     *
 | 
			
		||||
@@ -112,7 +112,7 @@ class CustomField
 | 
			
		||||
    {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
@@ -120,15 +120,15 @@ class CustomField
 | 
			
		||||
    {
 | 
			
		||||
        return $this->slug;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    function getOptions()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->options;
 | 
			
		||||
    } 
 | 
			
		||||
    
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set type
 | 
			
		||||
     *
 | 
			
		||||
@@ -152,7 +152,7 @@ class CustomField
 | 
			
		||||
        return $this->type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set active
 | 
			
		||||
     *
 | 
			
		||||
@@ -229,9 +229,9 @@ class CustomField
 | 
			
		||||
                }
 | 
			
		||||
             }
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          return '';
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
       } else {
 | 
			
		||||
          return $this->name;
 | 
			
		||||
       };
 | 
			
		||||
@@ -272,7 +272,7 @@ class CustomField
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param $slug
 | 
			
		||||
     * @return $this
 | 
			
		||||
@@ -282,20 +282,20 @@ class CustomField
 | 
			
		||||
        $this->slug = $slug;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * alias for isRequired
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public function getRequired()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->isRequired();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * return true if the field required
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public function isRequired()
 | 
			
		||||
@@ -308,5 +308,5 @@ class CustomField
 | 
			
		||||
        $this->required = $required;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,21 +39,21 @@ class Option
 | 
			
		||||
     * @ORM\GeneratedValue(strategy="AUTO")
 | 
			
		||||
     */
 | 
			
		||||
    private $id;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     * @ORM\Column(type="string", length=15)
 | 
			
		||||
     */
 | 
			
		||||
    private $key;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A json representation of text (multilingual)
 | 
			
		||||
     *
 | 
			
		||||
     * @var array
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $text;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Collection
 | 
			
		||||
     * @ORM\OneToMany(
 | 
			
		||||
@@ -61,7 +61,7 @@ class Option
 | 
			
		||||
     *     mappedBy="parent")
 | 
			
		||||
     */
 | 
			
		||||
    private $children;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Option
 | 
			
		||||
     * @ORM\ManyToOne(
 | 
			
		||||
@@ -70,19 +70,19 @@ class Option
 | 
			
		||||
     * @ORM\JoinColumn(nullable=true)
 | 
			
		||||
     */
 | 
			
		||||
    private $parent;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     * @ORM\Column(type="string", length=50, name="internal_key")
 | 
			
		||||
     */
 | 
			
		||||
    private $internalKey = '';
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var boolean
 | 
			
		||||
     * @ORM\Column(type="boolean")
 | 
			
		||||
     */
 | 
			
		||||
    private $active = true;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
@@ -90,7 +90,7 @@ class Option
 | 
			
		||||
    {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
@@ -98,7 +98,7 @@ class Option
 | 
			
		||||
    {
 | 
			
		||||
        return $this->key;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
@@ -106,7 +106,7 @@ class Option
 | 
			
		||||
    {
 | 
			
		||||
        return $this->text;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Collection
 | 
			
		||||
     */
 | 
			
		||||
@@ -114,7 +114,7 @@ class Option
 | 
			
		||||
    {
 | 
			
		||||
        return $this->children;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Option
 | 
			
		||||
     */
 | 
			
		||||
@@ -122,7 +122,7 @@ class Option
 | 
			
		||||
    {
 | 
			
		||||
        return $this->parent;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param $key
 | 
			
		||||
     * @return $this
 | 
			
		||||
@@ -132,7 +132,7 @@ class Option
 | 
			
		||||
        $this->key = $key;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param array $text
 | 
			
		||||
     * @return $this
 | 
			
		||||
@@ -142,7 +142,7 @@ class Option
 | 
			
		||||
        $this->text = $text;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param Option|null $parent
 | 
			
		||||
     * @return $this
 | 
			
		||||
@@ -153,16 +153,16 @@ class Option
 | 
			
		||||
        $this->key = $parent->getKey();
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public function hasParent()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->parent === NULL ? false : true;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
@@ -170,7 +170,7 @@ class Option
 | 
			
		||||
    {
 | 
			
		||||
        return $this->internalKey;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
@@ -178,7 +178,7 @@ class Option
 | 
			
		||||
    {
 | 
			
		||||
        return $this->active;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
@@ -186,7 +186,7 @@ class Option
 | 
			
		||||
    {
 | 
			
		||||
        return $this->isActive();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param $internal_key
 | 
			
		||||
     * @return $this
 | 
			
		||||
@@ -196,7 +196,7 @@ class Option
 | 
			
		||||
        $this->internalKey = $internal_key;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param $active
 | 
			
		||||
     * @return $this
 | 
			
		||||
@@ -206,5 +206,5 @@ class Option
 | 
			
		||||
        $this->active = $active;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ class CustomFieldsGroup
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
@@ -76,15 +76,15 @@ class CustomFieldsGroup
 | 
			
		||||
     * @var array|null
 | 
			
		||||
     */
 | 
			
		||||
    private $activeCustomFields = null;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $options = array();
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CustomFieldsGroup constructor.
 | 
			
		||||
     */
 | 
			
		||||
@@ -115,7 +115,7 @@ class CustomFieldsGroup
 | 
			
		||||
    {
 | 
			
		||||
        $this->customFields->removeElement($customField);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Collection
 | 
			
		||||
     */
 | 
			
		||||
@@ -142,7 +142,7 @@ class CustomFieldsGroup
 | 
			
		||||
 | 
			
		||||
        return $this->activeCustomFields;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get id
 | 
			
		||||
     *
 | 
			
		||||
@@ -184,9 +184,9 @@ class CustomFieldsGroup
 | 
			
		||||
                }
 | 
			
		||||
             }
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          return '';
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
       } else {
 | 
			
		||||
          return $this->name;
 | 
			
		||||
       }
 | 
			
		||||
@@ -214,10 +214,10 @@ class CustomFieldsGroup
 | 
			
		||||
    {
 | 
			
		||||
        return $this->entity;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * get options array
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getOptions()
 | 
			
		||||
@@ -227,7 +227,7 @@ class CustomFieldsGroup
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * set options array
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $options
 | 
			
		||||
     * @return CustomFieldsGroup
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ class DocGeneratorTemplate
 | 
			
		||||
    private int $id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     * @Serializer\Groups({"read"})
 | 
			
		||||
     */
 | 
			
		||||
    private array $name = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ final class Version20210812214310 extends AbstractMigration
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_docgen_template ADD entities TEXT');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_docgen_template ADD context VARCHAR(255)');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_docgen_template.entities IS \'(DC2Type:simple_array)\'');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_docgen_template.name IS \'(DC2Type:json_array)\'');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema): void
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,10 @@ class DocumentCategory
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public function __construct($bundleId, $idInsideBundle)
 | 
			
		||||
    {
 | 
			
		||||
        $this->bundleId = $bundleId;
 | 
			
		||||
 
 | 
			
		||||
@@ -39,14 +39,14 @@ class StoredObject implements AsyncFileInterface, Document
 | 
			
		||||
    private $filename;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Column(type="json_array", name="key")
 | 
			
		||||
     * @ORM\Column(type="json", name="key")
 | 
			
		||||
     */
 | 
			
		||||
    private array $keyInfos = [];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var int[]
 | 
			
		||||
     * @ORM\Column(type="json_array", name="iv")
 | 
			
		||||
     * @ORM\Column(type="json", name="iv")
 | 
			
		||||
     */
 | 
			
		||||
    private array $iv = [];
 | 
			
		||||
 | 
			
		||||
@@ -69,7 +69,7 @@ class StoredObject implements AsyncFileInterface, Document
 | 
			
		||||
    private string $type = '';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Column(type="json_array", name="datas")
 | 
			
		||||
     * @ORM\Column(type="json", name="datas")
 | 
			
		||||
     * @Serializer\Groups({"read"})
 | 
			
		||||
     */
 | 
			
		||||
    private array $datas = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@ final class Version20180605102533 extends AbstractMigration
 | 
			
		||||
        $this->addSql('CREATE SCHEMA chill_doc');
 | 
			
		||||
        $this->addSql('CREATE SEQUENCE chill_doc.person_document_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
 | 
			
		||||
        $this->addSql('CREATE TABLE chill_doc.document_category (bundle_id VARCHAR(255) NOT NULL, id_inside_bundle INT NOT NULL, document_class VARCHAR(255) NOT NULL, name JSON NOT NULL, PRIMARY KEY(bundle_id, id_inside_bundle))');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_doc.document_category.name IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('CREATE TABLE chill_doc.person_document (id INT NOT NULL, category_bundle_id VARCHAR(255) DEFAULT NULL, category_id_inside_bundle INT DEFAULT NULL, scope_id INT DEFAULT NULL, user_id INT DEFAULT NULL, person_id INT DEFAULT NULL, title TEXT NOT NULL, description TEXT NOT NULL, content TEXT NOT NULL, date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))');
 | 
			
		||||
        $this->addSql('CREATE INDEX IDX_41DA53C369A0BE36EF62EFC ON chill_doc.person_document (category_bundle_id, category_id_inside_bundle)');
 | 
			
		||||
        $this->addSql('CREATE INDEX IDX_41DA53C682B5931 ON chill_doc.person_document (scope_id)');
 | 
			
		||||
@@ -34,6 +33,6 @@ final class Version20180605102533 extends AbstractMigration
 | 
			
		||||
        $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
 | 
			
		||||
 | 
			
		||||
        $this->addSql('DROP SCHEMA chill_doc CASCADE');
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,6 @@ final class Version20180606133338 extends AbstractMigration
 | 
			
		||||
 | 
			
		||||
        $this->addSql('CREATE SEQUENCE chill_doc.stored_object_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
 | 
			
		||||
        $this->addSql('CREATE TABLE chill_doc.stored_object (id INT NOT NULL, filename TEXT NOT NULL, key JSON NOT NULL, iv JSON NOT NULL, creation_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, type TEXT NOT NULL, datas JSON NOT NULL, PRIMARY KEY(id))');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_doc.stored_object.key IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_doc.stored_object.iv IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_doc.stored_object.datas IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_doc.person_document ADD object_id INT DEFAULT NULL');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_doc.person_document DROP content');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C232D562B FOREIGN KEY (object_id) REFERENCES chill_doc.stored_object (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ class EventType
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
@@ -55,7 +55,7 @@ class EventType
 | 
			
		||||
     * @ORM\Column(type="boolean")
 | 
			
		||||
     */
 | 
			
		||||
    private $active;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Collection
 | 
			
		||||
     * @ORM\OneToMany(
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ class Role
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
@@ -53,7 +53,7 @@ class Role
 | 
			
		||||
     * @ORM\Column(type="boolean")
 | 
			
		||||
     */
 | 
			
		||||
    private $active;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var EventType
 | 
			
		||||
     * @ORM\ManyToOne(
 | 
			
		||||
 
 | 
			
		||||
@@ -41,19 +41,19 @@ class Status
 | 
			
		||||
     * @ORM\GeneratedValue(strategy="AUTO")
 | 
			
		||||
     */
 | 
			
		||||
    private $id;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var boolean
 | 
			
		||||
     * @ORM\Column(type="boolean")
 | 
			
		||||
     */
 | 
			
		||||
    private $active;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var EventType
 | 
			
		||||
     * @ORM\ManyToOne(
 | 
			
		||||
 
 | 
			
		||||
@@ -107,6 +107,9 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
 | 
			
		||||
        $container->setParameter('chill_main.available_languages',
 | 
			
		||||
            $config['available_languages']);
 | 
			
		||||
 | 
			
		||||
        $container->setParameter('chill_main.available_countries',
 | 
			
		||||
            $config['available_countries']);
 | 
			
		||||
 | 
			
		||||
        $container->setParameter('chill_main.routing.resources',
 | 
			
		||||
            $config['routing']['resources']);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,10 @@ class Configuration implements ConfigurationInterface
 | 
			
		||||
                    ->defaultValue(array('fr'))
 | 
			
		||||
                    ->prototype('scalar')->end()
 | 
			
		||||
                ->end() // end of array 'available_languages'
 | 
			
		||||
                ->arrayNode('available_countries')
 | 
			
		||||
                    ->defaultValue(array('FR'))
 | 
			
		||||
                    ->prototype('scalar')->end()
 | 
			
		||||
                ->end() // end of array 'available_countries'
 | 
			
		||||
                ->arrayNode('routing')
 | 
			
		||||
                    ->children()
 | 
			
		||||
                        ->arrayNode('resources')
 | 
			
		||||
 
 | 
			
		||||
@@ -15,17 +15,17 @@ use Doctrine\DBAL\Types\ConversionException;
 | 
			
		||||
class NativeDateIntervalType extends DateIntervalType
 | 
			
		||||
{
 | 
			
		||||
    const FORMAT = '%rP%YY%MM%DDT%HH%IM%SS';
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public function getName(): string
 | 
			
		||||
    {
 | 
			
		||||
        return \Doctrine\DBAL\Types\Type::DATEINTERVAL;
 | 
			
		||||
        return \Doctrine\DBAL\Types\Types::DATEINTERVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
 | 
			
		||||
    {
 | 
			
		||||
        return 'INTERVAL';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
@@ -41,36 +41,36 @@ class NativeDateIntervalType extends DateIntervalType
 | 
			
		||||
 | 
			
		||||
        throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateInterval']);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public function convertToPHPValue($value, AbstractPlatform $platform)
 | 
			
		||||
    {
 | 
			
		||||
        if ($value === null || $value instanceof \DateInterval) {
 | 
			
		||||
            return $value;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $strings = explode(' ', $value);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            if (count($strings) === 0) {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
            $intervalSpec = 'P';
 | 
			
		||||
            \reset($strings);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            do {
 | 
			
		||||
                $intervalSpec .= $this->convertEntry($strings);
 | 
			
		||||
            } while (next($strings) !== FALSE);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            return new \DateInterval($intervalSpec);
 | 
			
		||||
        } catch (\Exception $exception) {
 | 
			
		||||
            throw $this->createConversionException($value, $exception);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    private function convertEntry(&$strings)
 | 
			
		||||
    {
 | 
			
		||||
        $current = \current($strings);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (is_numeric($current)) {
 | 
			
		||||
            $next = \next($strings);
 | 
			
		||||
            switch($next) {
 | 
			
		||||
@@ -79,7 +79,7 @@ class NativeDateIntervalType extends DateIntervalType
 | 
			
		||||
                    $unit = 'Y';
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'mon':
 | 
			
		||||
                case 'mons': 
 | 
			
		||||
                case 'mons':
 | 
			
		||||
                    $unit = 'M';
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'day':
 | 
			
		||||
@@ -89,20 +89,20 @@ class NativeDateIntervalType extends DateIntervalType
 | 
			
		||||
                default:
 | 
			
		||||
                    throw $this->createConversionException(implode('', $strings));
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            return $current.$unit;
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
        } elseif (\preg_match('/([0-9]{2}\:[0-9]{2}:[0-9]{2})/', $current) === 1) {
 | 
			
		||||
            $tExploded = explode(':', $current);
 | 
			
		||||
            $intervalSpec = 'T';
 | 
			
		||||
            $intervalSpec.= $tExploded[0].'H';
 | 
			
		||||
            $intervalSpec.= $tExploded[1].'M';
 | 
			
		||||
            $intervalSpec.= $tExploded[2].'S';
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            return $intervalSpec;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    protected function createConversionException($value, $exception = null)
 | 
			
		||||
    {
 | 
			
		||||
        return ConversionException::conversionFailedFormat($value, $this->getName(), 'xx year xx mons xx days 01:02:03', $exception);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ class Country
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     * @groups({"read"})
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -43,14 +43,14 @@ class Language
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string array
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get id
 | 
			
		||||
     *
 | 
			
		||||
     * @return string 
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getId()
 | 
			
		||||
    {
 | 
			
		||||
@@ -59,7 +59,7 @@ class Language
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set id
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $id
 | 
			
		||||
     * @return Language
 | 
			
		||||
     */
 | 
			
		||||
@@ -78,17 +78,17 @@ class Language
 | 
			
		||||
    public function setName($name)
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $name;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get name
 | 
			
		||||
     *
 | 
			
		||||
     * @return string array 
 | 
			
		||||
     * @return string array
 | 
			
		||||
     */
 | 
			
		||||
    public function getName()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->name;
 | 
			
		||||
    } 
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ class Scope
 | 
			
		||||
     *
 | 
			
		||||
     * @var array
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     * @Groups({"read"})
 | 
			
		||||
     */
 | 
			
		||||
    private $name = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ class User implements AdvancedUserInterface {
 | 
			
		||||
     * Array where SAML attributes's data are stored
 | 
			
		||||
     * @var array
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array", nullable=true)
 | 
			
		||||
     * @ORM\Column(type="json", nullable=true)
 | 
			
		||||
     */
 | 
			
		||||
    private $attributes;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ class CommentType extends AbstractType
 | 
			
		||||
            $data = $event->getForm()->getData();
 | 
			
		||||
            $comment = $event->getData() ?? ['comment' => ''];
 | 
			
		||||
 | 
			
		||||
            if ($data->getComment() !== $comment['comment']) {
 | 
			
		||||
            if (null !== $data && $data->getComment() !== $comment['comment']) {
 | 
			
		||||
                $data->setDate(new \DateTime());
 | 
			
		||||
                $data->setUserId($this->user->getId());
 | 
			
		||||
                $event->getForm()->setData($data);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ use Chill\MainBundle\Form\Type\DataTransformer\ObjectToIdTransformer;
 | 
			
		||||
use Doctrine\Persistence\ObjectManager;
 | 
			
		||||
use Chill\MainBundle\Form\Type\Select2ChoiceType;
 | 
			
		||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Extends choice to allow adding select2 library on widget
 | 
			
		||||
@@ -37,31 +38,25 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
 | 
			
		||||
 */
 | 
			
		||||
class Select2CountryType extends AbstractType
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var RequestStack
 | 
			
		||||
     */
 | 
			
		||||
    private $requestStack;
 | 
			
		||||
    private RequestStack $requestStack;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var TranslatableStringHelper
 | 
			
		||||
     */
 | 
			
		||||
    protected $translatableStringHelper;
 | 
			
		||||
    private ObjectManager $em;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var ObjectManager
 | 
			
		||||
     */
 | 
			
		||||
    private $em;
 | 
			
		||||
    protected TranslatableStringHelper $translatableStringHelper;
 | 
			
		||||
 | 
			
		||||
    protected ParameterBagInterface $parameterBag;
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        RequestStack $requestStack,
 | 
			
		||||
        ObjectManager $em,
 | 
			
		||||
        TranslatableStringHelper $translatableStringHelper
 | 
			
		||||
        TranslatableStringHelper $translatableStringHelper,
 | 
			
		||||
        ParameterBagInterface $parameterBag
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        $this->requestStack = $requestStack;
 | 
			
		||||
        $this->em = $em;
 | 
			
		||||
        $this->translatableStringHelper = $translatableStringHelper;
 | 
			
		||||
        $this->parameterBag = $parameterBag;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getBlockPrefix()
 | 
			
		||||
@@ -82,19 +77,29 @@ class Select2CountryType extends AbstractType
 | 
			
		||||
 | 
			
		||||
    public function configureOptions(OptionsResolver $resolver)
 | 
			
		||||
    {
 | 
			
		||||
        $locale = $this->requestStack->getCurrentRequest()->getLocale();
 | 
			
		||||
        $countries = $this->em->getRepository('Chill\MainBundle\Entity\Country')->findAll();
 | 
			
		||||
        $choices = array();
 | 
			
		||||
        $choices = [];
 | 
			
		||||
        $preferredCountries = $this->parameterBag->get('chill_main.available_countries');
 | 
			
		||||
        $preferredChoices = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($countries as $c) {
 | 
			
		||||
            $choices[$c->getId()] = $this->translatableStringHelper->localize($c->getName());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($preferredCountries as $pc) {
 | 
			
		||||
            foreach ($countries as $c) {
 | 
			
		||||
                if ($c->getCountryCode() == $pc) {
 | 
			
		||||
                    $preferredChoices[$c->getId()] = $this->translatableStringHelper->localize($c->getName());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        asort($choices, SORT_STRING | SORT_FLAG_CASE);
 | 
			
		||||
 | 
			
		||||
        $resolver->setDefaults(array(
 | 
			
		||||
           'class' => 'Chill\MainBundle\Entity\Country',
 | 
			
		||||
           'choices' => array_combine(array_values($choices),array_keys($choices))
 | 
			
		||||
           'choices' => array_combine(array_values($choices),array_keys($choices)),
 | 
			
		||||
           'preferred_choices' => array_combine(array_values($preferredChoices), array_keys($preferredChoices))
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,37 +28,32 @@ use Chill\MainBundle\Form\Type\DataTransformer\MultipleObjectsToIdTransformer;
 | 
			
		||||
use Doctrine\Persistence\ObjectManager;
 | 
			
		||||
use Chill\MainBundle\Form\Type\Select2ChoiceType;
 | 
			
		||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Extends choice to allow adding select2 library on widget for languages (multiple)
 | 
			
		||||
 */
 | 
			
		||||
class Select2LanguageType extends AbstractType
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var RequestStack
 | 
			
		||||
     */
 | 
			
		||||
    private $requestStack;
 | 
			
		||||
    private RequestStack $requestStack;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var ObjectManager
 | 
			
		||||
     */
 | 
			
		||||
    private $em;
 | 
			
		||||
    private ObjectManager $em;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var TranslatableStringHelper
 | 
			
		||||
     */
 | 
			
		||||
    protected $translatableStringHelper;
 | 
			
		||||
    protected TranslatableStringHelper $translatableStringHelper;
 | 
			
		||||
 | 
			
		||||
    protected ParameterBagInterface $parameterBag;
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        RequestStack $requestStack,
 | 
			
		||||
        ObjectManager $em,
 | 
			
		||||
        TranslatableStringHelper $translatableStringHelper
 | 
			
		||||
        TranslatableStringHelper $translatableStringHelper,
 | 
			
		||||
        ParameterBagInterface $parameterBag
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        $this->requestStack = $requestStack;
 | 
			
		||||
        $this->em = $em;
 | 
			
		||||
        $this->translatableStringHelper = $translatableStringHelper;
 | 
			
		||||
        $this->parameterBag = $parameterBag;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getBlockPrefix()
 | 
			
		||||
@@ -79,19 +74,24 @@ class Select2LanguageType extends AbstractType
 | 
			
		||||
 | 
			
		||||
    public function configureOptions(OptionsResolver $resolver)
 | 
			
		||||
    {
 | 
			
		||||
        $locale = $this->requestStack->getCurrentRequest()->getLocale();
 | 
			
		||||
        $languages = $this->em->getRepository('Chill\MainBundle\Entity\Language')->findAll();
 | 
			
		||||
        $choices = array();
 | 
			
		||||
        $preferredLanguages = $this->parameterBag->get('chill_main.available_languages');
 | 
			
		||||
        $choices = [];
 | 
			
		||||
        $preferredChoices = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($languages as $l) {
 | 
			
		||||
            $choices[$l->getId()] = $this->translatableStringHelper->localize($l->getName());
 | 
			
		||||
        }
 | 
			
		||||
        foreach ($preferredLanguages as $l) {
 | 
			
		||||
            $preferredChoices[$l] = $choices[$l];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        asort($choices, SORT_STRING | SORT_FLAG_CASE);
 | 
			
		||||
 | 
			
		||||
        $resolver->setDefaults(array(
 | 
			
		||||
           'class' => 'Chill\MainBundle\Entity\Language',
 | 
			
		||||
           'choices' => array_combine(array_values($choices),array_keys($choices))
 | 
			
		||||
           'choices' => array_combine(array_values($choices), array_keys($choices)),
 | 
			
		||||
           'preferred_choices' => array_combine(array_values($preferredChoices), array_keys($preferredChoices))
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,5 @@
 | 
			
		||||
require("./show_hide.js");
 | 
			
		||||
//require("./show_hide.js");
 | 
			
		||||
 | 
			
		||||
import { ShowHide } from './show_hide.js'
 | 
			
		||||
 | 
			
		||||
export { ShowHide }
 | 
			
		||||
@@ -134,4 +134,4 @@ var ShowHide = function(options) {
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export {ShowHide};
 | 
			
		||||
export { ShowHide };
 | 
			
		||||
 
 | 
			
		||||
@@ -5,82 +5,89 @@ import App from './App.vue';
 | 
			
		||||
 | 
			
		||||
const i18n = _createI18n(addressMessages);
 | 
			
		||||
 | 
			
		||||
let inputs = document.querySelectorAll('input[type="hidden"][data-input-address]');
 | 
			
		||||
const addAddressInput = (inputs) => {
 | 
			
		||||
 | 
			
		||||
const isNumeric = function(v) { return !isNaN(v); };
 | 
			
		||||
    inputs.forEach(el => {
 | 
			
		||||
        let
 | 
			
		||||
            addressId = el.value,
 | 
			
		||||
            uniqid = el.dataset.inputAddress,
 | 
			
		||||
            container = document.querySelector('div[data-input-address-container="' + uniqid + '"]'),
 | 
			
		||||
            isEdit = addressId !== '',
 | 
			
		||||
            addressIdInt = addressId !== '' ? parseInt(addressId) : null
 | 
			
		||||
        ;
 | 
			
		||||
 | 
			
		||||
inputs.forEach(el => {
 | 
			
		||||
    let
 | 
			
		||||
        addressId = el.value,
 | 
			
		||||
        uniqid = el.dataset.inputAddress,
 | 
			
		||||
        container = document.querySelector('div[data-input-address-container="' + uniqid + '"]'),
 | 
			
		||||
        isEdit = addressId !== '',
 | 
			
		||||
        addressIdInt = addressId !== '' ? parseInt(addressId) : null
 | 
			
		||||
    ;
 | 
			
		||||
        if (container === null) {
 | 
			
		||||
            throw Error("no container");
 | 
			
		||||
        }
 | 
			
		||||
        console.log('useValidFrom',  el.dataset.useValidFrom === '1');
 | 
			
		||||
 | 
			
		||||
    if (container === null) {
 | 
			
		||||
        throw Error("no container");
 | 
			
		||||
    }
 | 
			
		||||
    console.log('useValidFrom',  el.dataset.useValidFrom === '1');
 | 
			
		||||
 | 
			
		||||
    const app = createApp({
 | 
			
		||||
        template: `<app v-bind:addAddress="this.addAddress" @address-created="associateToInput"></app>`,
 | 
			
		||||
        data() {
 | 
			
		||||
            return {
 | 
			
		||||
                addAddress: {
 | 
			
		||||
                    context: {
 | 
			
		||||
                        // for legacy ? can be remove ?
 | 
			
		||||
                        target: {
 | 
			
		||||
                            name: 'input-address',
 | 
			
		||||
                            id: addressIdInt,
 | 
			
		||||
                        },
 | 
			
		||||
                        edit: isEdit,
 | 
			
		||||
                        addressId: addressIdInt,
 | 
			
		||||
                    },
 | 
			
		||||
                    options: {
 | 
			
		||||
                        /// Options override default.
 | 
			
		||||
                        /// null value take default component value defined in AddAddress data()
 | 
			
		||||
                        button: {
 | 
			
		||||
                            text: {
 | 
			
		||||
                                create: el.dataset.buttonTextCreate || null,
 | 
			
		||||
                                edit: el.dataset.buttonTextUpdate || null,
 | 
			
		||||
        const app = createApp({
 | 
			
		||||
            template: `<app v-bind:addAddress="this.addAddress" @address-created="associateToInput"></app>`,
 | 
			
		||||
            data() {
 | 
			
		||||
                return {
 | 
			
		||||
                    addAddress: {
 | 
			
		||||
                        context: {
 | 
			
		||||
                            // for legacy ? can be remove ?
 | 
			
		||||
                            target: {
 | 
			
		||||
                                name: 'input-address',
 | 
			
		||||
                                id: addressIdInt,
 | 
			
		||||
                            },
 | 
			
		||||
                            size: null,
 | 
			
		||||
                            displayText: true
 | 
			
		||||
                            edit: isEdit,
 | 
			
		||||
                            addressId: addressIdInt,
 | 
			
		||||
                        },
 | 
			
		||||
                        options: {
 | 
			
		||||
                            /// Options override default.
 | 
			
		||||
                            /// null value take default component value defined in AddAddress data()
 | 
			
		||||
                            button: {
 | 
			
		||||
                                text: {
 | 
			
		||||
                                    create: el.dataset.buttonTextCreate || null,
 | 
			
		||||
                                    edit: el.dataset.buttonTextUpdate || null,
 | 
			
		||||
                                },
 | 
			
		||||
                                size: null,
 | 
			
		||||
                                displayText: true
 | 
			
		||||
                            },
 | 
			
		||||
 | 
			
		||||
                        /// Modal title text if create or edit address (trans chain, see i18n)
 | 
			
		||||
                        title: {
 | 
			
		||||
                            create: null,
 | 
			
		||||
                            edit: null,
 | 
			
		||||
                        },
 | 
			
		||||
                            /// Modal title text if create or edit address (trans chain, see i18n)
 | 
			
		||||
                            title: {
 | 
			
		||||
                                create: null,
 | 
			
		||||
                                edit: null,
 | 
			
		||||
                            },
 | 
			
		||||
 | 
			
		||||
                        /// Display panes in Modal for step123
 | 
			
		||||
                        openPanesInModal: true,
 | 
			
		||||
                            /// Display panes in Modal for step123
 | 
			
		||||
                            openPanesInModal: true,
 | 
			
		||||
 | 
			
		||||
                        /// Display actions buttons of panes in a sticky-form-button navbar
 | 
			
		||||
                        stickyActions: false,
 | 
			
		||||
                        showMessageWhenNoAddress: true,
 | 
			
		||||
                            /// Display actions buttons of panes in a sticky-form-button navbar
 | 
			
		||||
                            stickyActions: false,
 | 
			
		||||
                            showMessageWhenNoAddress: true,
 | 
			
		||||
 | 
			
		||||
                        /// Use Date fields
 | 
			
		||||
                        useDate: {
 | 
			
		||||
                            validFrom: el.dataset.useValidFrom === '1' || false,  //boolean, default: false
 | 
			
		||||
                            validTo: el.dataset.useValidTo === '1' || false,       //boolean, default: false
 | 
			
		||||
                        },
 | 
			
		||||
                            /// Use Date fields
 | 
			
		||||
                            useDate: {
 | 
			
		||||
                                validFrom: el.dataset.useValidFrom === '1' || false,  //boolean, default: false
 | 
			
		||||
                                validTo: el.dataset.useValidTo === '1' || false,       //boolean, default: false
 | 
			
		||||
                            },
 | 
			
		||||
 | 
			
		||||
                        /// Don't display show renderbox Address: showPane display only a button
 | 
			
		||||
                        onlyButton: false,
 | 
			
		||||
                            /// Don't display show renderbox Address: showPane display only a button
 | 
			
		||||
                            onlyButton: false,
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            methods: {
 | 
			
		||||
                associateToInput(payload) {
 | 
			
		||||
                    el.value = payload.addressId;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        methods: {
 | 
			
		||||
            associateToInput(payload) {
 | 
			
		||||
                el.value = payload.addressId;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
        .use(i18n)
 | 
			
		||||
        .component('app', App)
 | 
			
		||||
        .mount(container);
 | 
			
		||||
});
 | 
			
		||||
        })
 | 
			
		||||
            .use(i18n)
 | 
			
		||||
            .component('app', App)
 | 
			
		||||
            .mount(container);
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
document.addEventListener('DOMContentLoaded', (_e) =>
 | 
			
		||||
    addAddressInput(document.querySelectorAll('input[type="hidden"][data-input-address]'))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
window.addEventListener('collection-add-entry', (e) =>
 | 
			
		||||
    addAddressInput(e.detail.entry.querySelectorAll('input[type="hidden"][data-input-address]'))
 | 
			
		||||
);
 | 
			
		||||
@@ -39,3 +39,4 @@ assetic:
 | 
			
		||||
        
 | 
			
		||||
chill_main:
 | 
			
		||||
    available_languages: [fr, en]
 | 
			
		||||
    available_countries: [FR]
 | 
			
		||||
 
 | 
			
		||||
@@ -83,6 +83,10 @@
 | 
			
		||||
{% endmacro %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{#
 | 
			
		||||
    this enclose the rendering inside a "li", which ease the placement operation when the address
 | 
			
		||||
    must be shown in such list
 | 
			
		||||
#}
 | 
			
		||||
{%- if render == 'list' -%}
 | 
			
		||||
    <li class="chill-entity entity-address">
 | 
			
		||||
        {% if options['with_picto'] %}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,12 +33,14 @@ class ParentRoleHelper
 | 
			
		||||
     * Return all the role which give access to the given role. Only the role
 | 
			
		||||
     * which are registered into Chill are taken into account.
 | 
			
		||||
     *
 | 
			
		||||
     * The array contains always the current $role (which give access to himself)
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $role
 | 
			
		||||
     * @return string[] the role which give access to the given $role
 | 
			
		||||
     */
 | 
			
		||||
    public function getParentRoles(string $role): array
 | 
			
		||||
    {
 | 
			
		||||
        $parentRoles = [];
 | 
			
		||||
        $parentRoles = [$role];
 | 
			
		||||
        // transform the roles from role hierarchy from string to Role
 | 
			
		||||
        $roles = \array_keys($this->hierarchy);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ class FilterOrderHelper
 | 
			
		||||
    private ?array $searchBoxFields = null;
 | 
			
		||||
    private array $checkboxes = [];
 | 
			
		||||
    private ?array $submitted = null;
 | 
			
		||||
    private ?string $formName = 'filter';
 | 
			
		||||
    private ?string $formName = 'f';
 | 
			
		||||
    private string $formType = FilterOrderType::class;
 | 
			
		||||
    private array $formOptions = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,10 @@ class ParentRoleHelperTest extends KernelTestCase
 | 
			
		||||
 | 
			
		||||
        $parentRoles = $this->parentRoleHelper->getParentRoles(PersonVoter::SEE);
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(2, $parentRoles);
 | 
			
		||||
        $this->assertCount(3, $parentRoles);
 | 
			
		||||
        $this->assertContains(PersonVoter::CREATE, $parentRoles);
 | 
			
		||||
        $this->assertContains(PersonVoter::UPDATE, $parentRoles);
 | 
			
		||||
        $this->assertContains(PersonVoter::SEE, $parentRoles);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIsRoleReached()
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
namespace Chill\MainBundle\Timeline;
 | 
			
		||||
 | 
			
		||||
use Doctrine\ORM\Query\ResultSetMapping;
 | 
			
		||||
use Doctrine\DBAL\Types\Type;
 | 
			
		||||
use Doctrine\DBAL\Types\Types;
 | 
			
		||||
use Doctrine\ORM\EntityManagerInterface;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 | 
			
		||||
use Doctrine\ORM\Query;
 | 
			
		||||
@@ -31,38 +31,38 @@ use Doctrine\ORM\NativeQuery;
 | 
			
		||||
 */
 | 
			
		||||
class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
{
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var \Doctrine\ORM\EntityManagerInterface
 | 
			
		||||
     */
 | 
			
		||||
    private $em;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record provider
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * This array has the structure `[ 'service id' => $service ]`
 | 
			
		||||
     *
 | 
			
		||||
     * @var TimelineProviderInterface[]
 | 
			
		||||
     */
 | 
			
		||||
    private $providers = [];
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record provider and their context
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * This array has the structure `[ 'context' => [ 'service id' ] ]`
 | 
			
		||||
     *
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    private $providersByContext = [];
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public function __construct(EntityManagerInterface $em)
 | 
			
		||||
    {
 | 
			
		||||
        $this->em = $em;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * return an HTML string with timeline
 | 
			
		||||
     *
 | 
			
		||||
@@ -79,18 +79,18 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
    public function getTimelineHTML($context, array $args, $firstItem = 0, $number = 20)
 | 
			
		||||
    {
 | 
			
		||||
        list($union, $parameters) = $this->buildUnionQuery($context, $args);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        //add ORDER BY clause and LIMIT
 | 
			
		||||
        $query = $union . sprintf(' ORDER BY date DESC LIMIT %d OFFSET %d',
 | 
			
		||||
            $number, $firstItem);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // run query and handle results
 | 
			
		||||
        $fetched = $this->runUnionQuery($query, $parameters);
 | 
			
		||||
        $entitiesByKey = $this->getEntities($fetched, $context);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $this->render($fetched, $entitiesByKey, $context, $args);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the number of items for the given context and args
 | 
			
		||||
     *
 | 
			
		||||
@@ -101,7 +101,7 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
    public function countItems($context, array $args)
 | 
			
		||||
    {
 | 
			
		||||
        $rsm = (new ResultSetMapping())
 | 
			
		||||
            ->addScalarResult('total', 'total', Type::INTEGER);
 | 
			
		||||
            ->addScalarResult('total', 'total', Types::INTEGER);
 | 
			
		||||
 | 
			
		||||
        list($select, $parameters) = $this->buildUnionQuery($context, $args);
 | 
			
		||||
 | 
			
		||||
@@ -110,10 +110,10 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
 | 
			
		||||
        $nq = $this->em->createNativeQuery($countQuery, $rsm);
 | 
			
		||||
        $nq->setParameters($parameters);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $nq->getSingleScalarResult();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * add a provider id
 | 
			
		||||
     *
 | 
			
		||||
@@ -127,7 +127,7 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
        $this->providersByContext[$context][] = $id;
 | 
			
		||||
        $this->providers[$id] = $provider;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get providers by context
 | 
			
		||||
     *
 | 
			
		||||
@@ -141,16 +141,16 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
            throw new \LogicException(sprintf('No builders have been defined for "%s"'
 | 
			
		||||
                    . ' context', $context));
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        $providers = [];
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        foreach($this->providersByContext[$context] as $providerId) {
 | 
			
		||||
            $providers[] = $this->providers[$providerId];
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $providers;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * build the UNION query with all providers
 | 
			
		||||
     *
 | 
			
		||||
@@ -172,7 +172,7 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
            $union .= $append;
 | 
			
		||||
            $parameters = array_merge($parameters, $selectParameters);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return [$union, $parameters];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -195,7 +195,7 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
        return $s;
 | 
			
		||||
    }
 | 
			
		||||
     */
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * return the SQL SELECT query as a string,
 | 
			
		||||
     *
 | 
			
		||||
@@ -222,9 +222,9 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        return [$sql, $data['WHERE'][1]];
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * run the UNION query and return result as an array
 | 
			
		||||
     *
 | 
			
		||||
@@ -236,12 +236,12 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
                ->addScalarResult('id', 'id')
 | 
			
		||||
                ->addScalarResult('type', 'type')
 | 
			
		||||
                ->addScalarResult('date', 'date');
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $this->em->createNativeQuery($query, $resultSetMapping)
 | 
			
		||||
            ->setParameters($parameters)
 | 
			
		||||
            ->getArrayResult();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $queriedIds
 | 
			
		||||
@@ -252,11 +252,11 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
    {
 | 
			
		||||
        //gather entities by type to pass all id with same type to the TimelineProvider.
 | 
			
		||||
        $idsByType = array();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        foreach($queriedIds as $result) {
 | 
			
		||||
            $idsByType[$result['type']][] = $result['id'];
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        //fetch entities from providers
 | 
			
		||||
        $entitiesByType = array();
 | 
			
		||||
        foreach ($idsByType as $type => $ids) {
 | 
			
		||||
@@ -268,10 +268,10 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $entitiesByType;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * render the timeline as HTML
 | 
			
		||||
     *
 | 
			
		||||
@@ -294,17 +294,17 @@ class TimelineBuilder implements ContainerAwareInterface
 | 
			
		||||
            $timelineEntry['date'] = new \DateTime($result['date']);
 | 
			
		||||
            $timelineEntry['template'] = $data['template'];
 | 
			
		||||
            $timelineEntry['template_data'] = $data['template_data'];
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            $timelineEntries[] = $timelineEntry;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $this->container->get('templating')
 | 
			
		||||
                ->render('@ChillMain/Timeline/chain_timelines.html.twig', array(
 | 
			
		||||
                    'results' => $timelineEntries
 | 
			
		||||
                ));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * get the template data from the provider for the given entity, by type.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ services:
 | 
			
		||||
            - "@request_stack"
 | 
			
		||||
            - "@doctrine.orm.entity_manager"
 | 
			
		||||
            - "@chill.main.helper.translatable_string"
 | 
			
		||||
            - '@Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface'
 | 
			
		||||
        tags:
 | 
			
		||||
            - { name: form.type, alias: select2_chill_country }
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +35,7 @@ services:
 | 
			
		||||
            - "@request_stack"
 | 
			
		||||
            - "@doctrine.orm.entity_manager"
 | 
			
		||||
            - "@chill.main.helper.translatable_string"
 | 
			
		||||
            - '@Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface'
 | 
			
		||||
        tags:
 | 
			
		||||
            - { name: form.type, alias: select2_chill_language }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@ final class Version20210304085819 extends AbstractMigration
 | 
			
		||||
    public function up(Schema $schema) : void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql('ALTER TABLE users ADD attributes JSONB DEFAULT NULL');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN users.attributes IS \'(DC2Type:json_array)\'');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema) : void
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
 | 
			
		||||
use Chill\PersonBundle\Entity\Household\Household;
 | 
			
		||||
use Chill\PersonBundle\Entity\Household\Position;
 | 
			
		||||
use Chill\PersonBundle\Repository\Household\PositionRepository;
 | 
			
		||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
 | 
			
		||||
use Symfony\Component\Security\Core\Security;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Route("/{_locale}/person/household")
 | 
			
		||||
@@ -24,11 +26,14 @@ class HouseholdController extends AbstractController
 | 
			
		||||
 | 
			
		||||
    private PositionRepository $positionRepository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(TranslatorInterface $translator, PositionRepository $positionRepository)
 | 
			
		||||
    private Security $security;
 | 
			
		||||
 | 
			
		||||
    public function __construct(TranslatorInterface $translator, PositionRepository $positionRepository, Security $security)
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        $this->translator = $translator;
 | 
			
		||||
        $this->positionRepository = $positionRepository;
 | 
			
		||||
        $this->security = $security;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -77,17 +82,46 @@ class HouseholdController extends AbstractController
 | 
			
		||||
     */
 | 
			
		||||
    public function accompanyingPeriod(Request $request, Household $household)
 | 
			
		||||
    {
 | 
			
		||||
        // TODO ACL
 | 
			
		||||
 | 
			
		||||
        $members = $household->getMembers();
 | 
			
		||||
        foreach($members as $m) {
 | 
			
		||||
            $accompanyingPeriods = $m->getPerson()->getAccompanyingPeriods();
 | 
			
		||||
        $currentMembers = $household->getCurrentPersons();
 | 
			
		||||
        $accompanyingPeriods = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($currentMembers as $p) {
 | 
			
		||||
            $accompanyingPeriodsMember = $p->getCurrentAccompanyingPeriods();
 | 
			
		||||
 | 
			
		||||
            foreach ($accompanyingPeriodsMember as $accompanyingPeriod) {
 | 
			
		||||
                if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                } else {
 | 
			
		||||
                $accompanyingPeriods[$accompanyingPeriod->getId()] = $accompanyingPeriod;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $oldMembers = $household->getNonCurrentMembers();
 | 
			
		||||
        $accompanyingPeriodsOld = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($oldMembers as $m) {
 | 
			
		||||
            $accompanyingPeriodsOldMember = $m->getPerson()->getAccompanyingPeriods();
 | 
			
		||||
 | 
			
		||||
            foreach ($accompanyingPeriodsOldMember as $accompanyingPeriod) {
 | 
			
		||||
                if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                } else {
 | 
			
		||||
                    $id = $accompanyingPeriod->getId();
 | 
			
		||||
 | 
			
		||||
                    if (!array_key_exists($id, $accompanyingPeriodsOld) && !array_key_exists($id, $accompanyingPeriods)) {
 | 
			
		||||
                        $accompanyingPeriodsOld[$id] = $accompanyingPeriod;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->render('@ChillPerson/Household/accompanying_period.html.twig',
 | 
			
		||||
            [
 | 
			
		||||
                'household' => $household,
 | 
			
		||||
                'accompanying_periods' => $accompanyingPeriods
 | 
			
		||||
                'accompanying_periods' => $accompanyingPeriods,
 | 
			
		||||
                'accompanying_periods_old' => $accompanyingPeriodsOld
 | 
			
		||||
            ]
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -77,11 +77,13 @@ class Configuration implements ConfigurationInterface
 | 
			
		||||
                                ->append($this->addFieldNode('nationality'))
 | 
			
		||||
                                ->append($this->addFieldNode('country_of_birth'))
 | 
			
		||||
                                ->append($this->addFieldNode('marital_status'))
 | 
			
		||||
                                ->append($this->addFieldNode('civility'))
 | 
			
		||||
                                ->append($this->addFieldNode('spoken_languages'))
 | 
			
		||||
                                ->append($this->addFieldNode('address'))
 | 
			
		||||
                                ->append($this->addFieldNode('accompanying_period'))
 | 
			
		||||
                                ->append($this->addFieldNode('memo'))
 | 
			
		||||
                                ->append($this->addFieldNode('number_of_children'))
 | 
			
		||||
                                ->append($this->addFieldNode('acceptEmail'))
 | 
			
		||||
                                ->arrayNode('alt_names')
 | 
			
		||||
                                    ->defaultValue([])
 | 
			
		||||
                                    ->arrayPrototype()
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ class Household
 | 
			
		||||
     *     targetEntity="Chill\MainBundle\Entity\Address",
 | 
			
		||||
     *     cascade={"persist", "remove", "merge", "detach"})
 | 
			
		||||
     * @ORM\JoinTable(name="chill_person_household_to_addresses")
 | 
			
		||||
     * @ORM\OrderBy({"validFrom" = "DESC"})
 | 
			
		||||
     * @ORM\OrderBy({"validFrom" = "DESC", "id" = "DESC"})
 | 
			
		||||
     * @Serializer\Groups({"write"})
 | 
			
		||||
     */
 | 
			
		||||
    private Collection $addresses;
 | 
			
		||||
@@ -245,6 +245,16 @@ class Household
 | 
			
		||||
        $members->getIterator()
 | 
			
		||||
            ->uasort(
 | 
			
		||||
                function (HouseholdMember $a, HouseholdMember $b) {
 | 
			
		||||
                    if ($a->getPosition() === NULL) {
 | 
			
		||||
                        if ($b->getPosition() === NULL) {
 | 
			
		||||
                            return 0;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            return -1;
 | 
			
		||||
                        }
 | 
			
		||||
                    } elseif ($b->getPosition() === NULL) {
 | 
			
		||||
                        return 1;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if ($a->getPosition()->getOrdering() < $b->getPosition()->getOrdering()) {
 | 
			
		||||
                        return -1;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -334,6 +344,26 @@ class Household
 | 
			
		||||
        return $this->getNonCurrentMembers($now)->matching($criteria);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCurrentMembersWithoutPosition(\DateTimeInterface $now = null)
 | 
			
		||||
    {
 | 
			
		||||
        $criteria = new Criteria();
 | 
			
		||||
        $expr = Criteria::expr();
 | 
			
		||||
 | 
			
		||||
        $criteria->where($expr->isNull('position'));
 | 
			
		||||
 | 
			
		||||
        return $this->getCurrentMembers($now)->matching($criteria);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getNonCurrentMembersWithoutPosition(\DateTimeInterface $now = null)
 | 
			
		||||
    {
 | 
			
		||||
        $criteria = new Criteria();
 | 
			
		||||
        $expr = Criteria::expr();
 | 
			
		||||
 | 
			
		||||
        $criteria->where($expr->isNull('position'));
 | 
			
		||||
 | 
			
		||||
        return $this->getNonCurrentMembers($now)->matching($criteria);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addMember(HouseholdMember $member): self
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->members->contains($member)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ class HouseholdMember
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\ManyToOne(targetEntity=Position::class)
 | 
			
		||||
     * @Serializer\Groups({"read"})
 | 
			
		||||
     * @Assert\NotNull(groups={"household_memberships"})
 | 
			
		||||
     * @Assert\NotNull(groups={"household_memberships_created"})
 | 
			
		||||
     */
 | 
			
		||||
    private ?Position $position = null;
 | 
			
		||||
 | 
			
		||||
@@ -159,6 +159,12 @@ class HouseholdMember
 | 
			
		||||
        return $this->shareHousehold;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setShareHousehold(bool $shareHousehold): self
 | 
			
		||||
    {
 | 
			
		||||
        $this->shareHousehold = $shareHousehold;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getPerson(): ?Person
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -41,14 +41,14 @@ class MaritalStatus
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string array
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get id
 | 
			
		||||
     *
 | 
			
		||||
     * @return string 
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getId()
 | 
			
		||||
    {
 | 
			
		||||
@@ -57,7 +57,7 @@ class MaritalStatus
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set id
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $id
 | 
			
		||||
     * @return MaritalStatus
 | 
			
		||||
     */
 | 
			
		||||
@@ -76,17 +76,17 @@ class MaritalStatus
 | 
			
		||||
    public function setName($name)
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $name;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get name
 | 
			
		||||
     *
 | 
			
		||||
     * @return string array 
 | 
			
		||||
     * @return string array
 | 
			
		||||
     */
 | 
			
		||||
    public function getName()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->name;
 | 
			
		||||
    } 
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ use Chill\PersonBundle\Entity\MaritalStatus;
 | 
			
		||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
 | 
			
		||||
use Chill\MainBundle\Entity\HasCenterInterface;
 | 
			
		||||
use Chill\MainBundle\Entity\Address;
 | 
			
		||||
use Chill\MainBundle\Entity\Civility;
 | 
			
		||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
 | 
			
		||||
use Chill\PersonBundle\Entity\Person\PersonCurrentAddress;
 | 
			
		||||
use DateTime;
 | 
			
		||||
@@ -212,6 +213,15 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
 | 
			
		||||
     */
 | 
			
		||||
    private $maritalStatus;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
     * The marital status of the person
 | 
			
		||||
     * @var Civility
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Civility")
 | 
			
		||||
     * @ORM\JoinColumn(nullable=true)
 | 
			
		||||
     */
 | 
			
		||||
    private $civility;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The date of the last marital status change of the person
 | 
			
		||||
     * @var \DateTime
 | 
			
		||||
@@ -406,7 +416,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
 | 
			
		||||
     * Array where customfield's data are stored
 | 
			
		||||
     * @var array
 | 
			
		||||
     *
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $cFData;
 | 
			
		||||
 | 
			
		||||
@@ -613,6 +623,26 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
 | 
			
		||||
        return $accompanyingPeriods;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get current accompanyingPeriods array
 | 
			
		||||
     */
 | 
			
		||||
    public function getCurrentAccompanyingPeriods(): array
 | 
			
		||||
    {
 | 
			
		||||
        $currentAccompanyingPeriods = [];
 | 
			
		||||
        $currentDate = new DateTime();
 | 
			
		||||
 | 
			
		||||
        foreach ($this->accompanyingPeriodParticipations as $participation)
 | 
			
		||||
        {
 | 
			
		||||
            $endDate = $participation->getEndDate();
 | 
			
		||||
 | 
			
		||||
            if ($endDate === null || $endDate > $currentDate){
 | 
			
		||||
                $currentAccompanyingPeriods[] = $participation->getAccompanyingPeriod();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $currentAccompanyingPeriods;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get AccompanyingPeriodParticipations Collection
 | 
			
		||||
     *
 | 
			
		||||
@@ -962,6 +992,28 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
 | 
			
		||||
        return $this->maritalStatus;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set civility
 | 
			
		||||
     *
 | 
			
		||||
     * @param Civility $civility
 | 
			
		||||
     * @return Person
 | 
			
		||||
     */
 | 
			
		||||
    public function setCivility(Civility $civility = null)
 | 
			
		||||
    {
 | 
			
		||||
        $this->civility = $civility;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get civility
 | 
			
		||||
     *
 | 
			
		||||
     * @return Civility
 | 
			
		||||
     */
 | 
			
		||||
    public function getCivility()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->civility;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
      * Set contactInfo
 | 
			
		||||
      *
 | 
			
		||||
@@ -1283,11 +1335,14 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
 | 
			
		||||
    /**
 | 
			
		||||
     * get the address associated with the person at the given date
 | 
			
		||||
     *
 | 
			
		||||
     * If the `$at` parameter is now, use the method `getCurrentPersonAddress`, which is optimized
 | 
			
		||||
     * on database side.
 | 
			
		||||
     *
 | 
			
		||||
     * @param DateTime|null $at
 | 
			
		||||
     * @return Address|null
 | 
			
		||||
     * @throws \Exception
 | 
			
		||||
     */
 | 
			
		||||
    public function getCurrentPersonAddress(?\DateTime $at = null): ?Address
 | 
			
		||||
    public function getAddressAt(?\DateTime $at = null): ?Address
 | 
			
		||||
    {
 | 
			
		||||
        $at ??= new DateTime('now');
 | 
			
		||||
 | 
			
		||||
@@ -1305,6 +1360,20 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
 | 
			
		||||
            current($addresses);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the current person address
 | 
			
		||||
     *
 | 
			
		||||
     * @return Address|null
 | 
			
		||||
     */
 | 
			
		||||
    public function getCurrentPersonAddress(): ?Address
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $this->currentPersonAddress) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->currentPersonAddress->getAddress();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validation callback that checks if the accompanying periods are valid
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,11 @@ use Chill\MainBundle\Export\FilterInterface;
 | 
			
		||||
use Symfony\Component\Form\FormBuilderInterface;
 | 
			
		||||
use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillDateType;
 | 
			
		||||
use Doctrine\DBAL\Types\Type;
 | 
			
		||||
use Doctrine\DBAL\Types\Types;
 | 
			
		||||
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
 | 
			
		||||
@@ -38,14 +38,14 @@ class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportEl
 | 
			
		||||
    public function alterQuery(QueryBuilder $qb, $data)
 | 
			
		||||
    {
 | 
			
		||||
        $this->addJoinAccompanyingPeriod($qb);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $clause = $qb->expr()->andX(
 | 
			
		||||
            $qb->expr()->lte('accompanying_period.closingDate', ':date_to'),
 | 
			
		||||
            $qb->expr()->gte('accompanying_period.closingDate', ':date_from'));
 | 
			
		||||
 | 
			
		||||
        $qb->andWhere($clause);
 | 
			
		||||
        $qb->setParameter('date_from', $data['date_from'], Type::DATE);
 | 
			
		||||
        $qb->setParameter('date_to', $data['date_to'], Type::DATE);
 | 
			
		||||
        $qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
 | 
			
		||||
        $qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function applyOn(): string
 | 
			
		||||
@@ -59,7 +59,7 @@ class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportEl
 | 
			
		||||
            'label' => "Having an accompanying period closed after this date",
 | 
			
		||||
            'data'  => new \DateTime("-1 month"),
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $builder->add('date_to', ChillDateType::class, array(
 | 
			
		||||
            'label' => "Having an accompanying period closed before this date",
 | 
			
		||||
            'data'  => new \DateTime(),
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,11 @@ use Chill\MainBundle\Export\FilterInterface;
 | 
			
		||||
use Symfony\Component\Form\FormBuilderInterface;
 | 
			
		||||
use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillDateType;
 | 
			
		||||
use Doctrine\DBAL\Types\Type;
 | 
			
		||||
use Doctrine\DBAL\Types\Types;
 | 
			
		||||
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
 | 
			
		||||
@@ -38,9 +38,9 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i
 | 
			
		||||
    public function alterQuery(QueryBuilder $qb, $data)
 | 
			
		||||
    {
 | 
			
		||||
        $this->addJoinAccompanyingPeriod($qb);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $clause = $qb->expr()->andX();
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
        $clause->add(
 | 
			
		||||
            $qb->expr()->lte('accompanying_period.openingDate', ':date_to')
 | 
			
		||||
            );
 | 
			
		||||
@@ -52,8 +52,8 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        $qb->andWhere($clause);
 | 
			
		||||
        $qb->setParameter('date_from', $data['date_from'], Type::DATE);
 | 
			
		||||
        $qb->setParameter('date_to', $data['date_to'], Type::DATE);
 | 
			
		||||
        $qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
 | 
			
		||||
        $qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function applyOn(): string
 | 
			
		||||
@@ -67,7 +67,7 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i
 | 
			
		||||
            'label' => "Having an accompanying period opened after this date",
 | 
			
		||||
            'data'  => new \DateTime("-1 month"),
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $builder->add('date_to', ChillDateType::class, array(
 | 
			
		||||
            'label' => "Having an accompanying period ending before this date, or "
 | 
			
		||||
            . "still opened at this date",
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,11 @@ use Chill\MainBundle\Export\FilterInterface;
 | 
			
		||||
use Symfony\Component\Form\FormBuilderInterface;
 | 
			
		||||
use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillDateType;
 | 
			
		||||
use Doctrine\DBAL\Types\Type;
 | 
			
		||||
use Doctrine\DBAL\Types\Types;
 | 
			
		||||
use Chill\PersonBundle\Export\AbstractAccompanyingPeriodExportElement;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportElement implements FilterInterface
 | 
			
		||||
@@ -38,14 +38,14 @@ class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportEl
 | 
			
		||||
    public function alterQuery(QueryBuilder $qb, $data)
 | 
			
		||||
    {
 | 
			
		||||
        $this->addJoinAccompanyingPeriod($qb);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $clause = $qb->expr()->andX(
 | 
			
		||||
            $qb->expr()->lte('accompanying_period.openingDate', ':date_to'),
 | 
			
		||||
            $qb->expr()->gte('accompanying_period.openingDate', ':date_from'));
 | 
			
		||||
 | 
			
		||||
        $qb->andWhere($clause);
 | 
			
		||||
        $qb->setParameter('date_from', $data['date_from'], Type::DATE);
 | 
			
		||||
        $qb->setParameter('date_to', $data['date_to'], Type::DATE);
 | 
			
		||||
        $qb->setParameter('date_from', $data['date_from'], Types::DATE_MUTABLE);
 | 
			
		||||
        $qb->setParameter('date_to', $data['date_to'], Types::DATE_MUTABLE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function applyOn(): string
 | 
			
		||||
@@ -59,7 +59,7 @@ class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportEl
 | 
			
		||||
            'label' => "Having an accompanying period opened after this date",
 | 
			
		||||
            'data'  => new \DateTime("-1 month"),
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $builder->add('date_to', ChillDateType::class, array(
 | 
			
		||||
            'label' => "Having an accompanying period opened before this date",
 | 
			
		||||
            'data'  => new \DateTime(),
 | 
			
		||||
 
 | 
			
		||||
@@ -16,13 +16,7 @@ class HouseholdMemberType extends AbstractType
 | 
			
		||||
            ->add('startDate', ChillDateType::class, [
 | 
			
		||||
                'label' => 'household.Start date',
 | 
			
		||||
                'input' => 'datetime_immutable',
 | 
			
		||||
            ])
 | 
			
		||||
            ->add('endDate', ChillDateType::class, [
 | 
			
		||||
                'label' => 'household.End date',
 | 
			
		||||
                'input' => 'datetime_immutable',
 | 
			
		||||
                'required' => false
 | 
			
		||||
            ])
 | 
			
		||||
            ;
 | 
			
		||||
            ]);
 | 
			
		||||
        if ($options['data']->getPosition()->isAllowHolder()) {
 | 
			
		||||
            $builder
 | 
			
		||||
                ->add('holder', ChoiceType::class, [
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
namespace Chill\PersonBundle\Form;
 | 
			
		||||
 | 
			
		||||
use Chill\CustomFieldsBundle\Form\Type\CustomFieldType;
 | 
			
		||||
use Chill\MainBundle\Entity\Civility;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\Select2CountryType;
 | 
			
		||||
@@ -35,6 +36,11 @@ use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
 | 
			
		||||
use Symfony\Component\Form\AbstractType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillDateType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\CommentType;
 | 
			
		||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
 | 
			
		||||
use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use Doctrine\ORM\EntityRepository;
 | 
			
		||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
 | 
			
		||||
use Symfony\Component\Form\CallbackTransformer;
 | 
			
		||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
 | 
			
		||||
use Symfony\Component\Form\Extension\Core\Type\TelType;
 | 
			
		||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
 | 
			
		||||
@@ -62,16 +68,20 @@ class PersonType extends AbstractType
 | 
			
		||||
     */
 | 
			
		||||
    protected $configAltNamesHelper;
 | 
			
		||||
 | 
			
		||||
    protected TranslatableStringHelper $translatableStringHelper;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @param string[] $personFieldsConfiguration configuration of visibility of some fields
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        array $personFieldsConfiguration,
 | 
			
		||||
        ConfigPersonAltNamesHelper $configAltNamesHelper
 | 
			
		||||
        ConfigPersonAltNamesHelper $configAltNamesHelper,
 | 
			
		||||
        TranslatableStringHelper $translatableStringHelper
 | 
			
		||||
    ) {
 | 
			
		||||
        $this->config = $personFieldsConfiguration;
 | 
			
		||||
        $this->configAltNamesHelper = $configAltNamesHelper;
 | 
			
		||||
        $this->translatableStringHelper = $translatableStringHelper;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -114,7 +124,19 @@ class PersonType extends AbstractType
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->config['place_of_birth'] === 'visible') {
 | 
			
		||||
            $builder->add('placeOfBirth', TextType::class, array('required' => false));
 | 
			
		||||
            $builder->add('placeOfBirth', TextType::class, array(
 | 
			
		||||
                'required' => false,
 | 
			
		||||
                'attr' => ['style' => 'text-transform: uppercase;'],
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
            $builder->get('placeOfBirth')->addModelTransformer(new CallbackTransformer(
 | 
			
		||||
                function ($string) {
 | 
			
		||||
                    return strtoupper($string);
 | 
			
		||||
                },
 | 
			
		||||
                function ($string) {
 | 
			
		||||
                    return strtoupper($string);
 | 
			
		||||
                }
 | 
			
		||||
                ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->config['contact_info'] === 'visible') {
 | 
			
		||||
@@ -150,7 +172,11 @@ class PersonType extends AbstractType
 | 
			
		||||
 | 
			
		||||
        if ($this->config['email'] === 'visible') {
 | 
			
		||||
            $builder
 | 
			
		||||
                ->add('email', EmailType::class, array('required' => false))
 | 
			
		||||
                ->add('email', EmailType::class, array('required' => false));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->config['acceptEmail'] === 'visible') {
 | 
			
		||||
            $builder
 | 
			
		||||
                ->add('acceptEmail', CheckboxType::class, array('required' => false));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -173,6 +199,23 @@ class PersonType extends AbstractType
 | 
			
		||||
                ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->config['civility'] === 'visible'){
 | 
			
		||||
            $builder
 | 
			
		||||
                ->add('civility', EntityType::class, [
 | 
			
		||||
                    'label' => 'Civility',
 | 
			
		||||
                    'class' => Civility::class,
 | 
			
		||||
                    'choice_label' => function (Civility $civility): string {
 | 
			
		||||
                        return $this->translatableStringHelper->localize($civility->getName());
 | 
			
		||||
                    },
 | 
			
		||||
                    'query_builder' => function (EntityRepository $er): QueryBuilder {
 | 
			
		||||
                        return $er->createQueryBuilder('c')
 | 
			
		||||
                            ->where('c.active = true');
 | 
			
		||||
                    },
 | 
			
		||||
                    'placeholder' => 'choose civility',
 | 
			
		||||
                    'required' => false
 | 
			
		||||
                ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->config['marital_status'] === 'visible'){
 | 
			
		||||
            $builder
 | 
			
		||||
                ->add('maritalStatus', Select2MaritalStatusType::class, array(
 | 
			
		||||
@@ -192,6 +235,7 @@ class PersonType extends AbstractType
 | 
			
		||||
                array('attr' => array('class' => 'cf-fields'), 'group' => $options['cFGroup']))
 | 
			
		||||
            ;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -19,15 +19,17 @@ class MembersEditor
 | 
			
		||||
 | 
			
		||||
    private array $persistables = [];
 | 
			
		||||
    private array $membershipsAffected = [];
 | 
			
		||||
    private array $oldMembershipsHashes = [];
 | 
			
		||||
 | 
			
		||||
    public const VALIDATION_GROUP = 'household_memberships';
 | 
			
		||||
    public const VALIDATION_GROUP_CREATED = 'household_memberships_created';
 | 
			
		||||
    public const VALIDATION_GROUP_AFFECTED = 'household_memberships';
 | 
			
		||||
 | 
			
		||||
    public function __construct(ValidatorInterface $validator, ?Household $household)
 | 
			
		||||
    {
 | 
			
		||||
        $this->validator = $validator;
 | 
			
		||||
        $this->household = $household;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public function addMovement(\DateTimeImmutable $date, Person $person, Position $position, ?bool $holder = false, ?string $comment = null): self
 | 
			
		||||
    {
 | 
			
		||||
        if (NULL === $this->household) {
 | 
			
		||||
@@ -56,6 +58,7 @@ class MembersEditor
 | 
			
		||||
                if ($participation->getEndDate() === NULL || $participation->getEndDate() > $date) {
 | 
			
		||||
                    $participation->setEndDate($date);
 | 
			
		||||
                    $this->membershipsAffected[] = $participation;
 | 
			
		||||
                    $this->oldMembershipsHashes[] = \spl_object_hash($participation);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -97,13 +100,18 @@ class MembersEditor
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->hasHousehold()) {
 | 
			
		||||
            $list = $this->validator
 | 
			
		||||
                ->validate($this->getHousehold(), null, [ self::VALIDATION_GROUP ]);
 | 
			
		||||
                ->validate($this->getHousehold(), null, [ self::VALIDATION_GROUP_AFFECTED ]);
 | 
			
		||||
        } else {
 | 
			
		||||
            $list = new ConstraintViolationList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($this->membershipsAffected as $m) {
 | 
			
		||||
            $list->addAll($this->validator->validate($m, null, [ self::VALIDATION_GROUP ]));
 | 
			
		||||
            if (\in_array(\spl_object_hash($m), $this->oldMembershipsHashes)) {
 | 
			
		||||
                $list->addAll($this->validator->validate($m, null, [ self::VALIDATION_GROUP_AFFECTED ]));
 | 
			
		||||
            } else {
 | 
			
		||||
                $list->addAll($this->validator->validate($m, null, [ self::VALIDATION_GROUP_CREATED,
 | 
			
		||||
                    self::VALIDATION_GROUP_AFFECTED ]));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $list;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ const personAcceptEmail = document.getElementById("personAcceptEmail");
 | 
			
		||||
const personPhoneNumber = document.getElementById("personPhoneNumber");
 | 
			
		||||
const personAcceptSMS = document.getElementById("personAcceptSMS");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
new ShowHide({
 | 
			
		||||
    froms: [maritalStatus],
 | 
			
		||||
    container: [maritalStatusDate],
 | 
			
		||||
@@ -24,21 +23,24 @@ new ShowHide({
 | 
			
		||||
    event_name: 'change'
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
new ShowHide({
 | 
			
		||||
    froms: [personEmail],
 | 
			
		||||
    container: [personAcceptEmail],
 | 
			
		||||
    test: function(froms) {
 | 
			
		||||
        for (let f of froms.values()) {
 | 
			
		||||
            for (let input of f.querySelectorAll('input').values()) {
 | 
			
		||||
                if (input.value) {
 | 
			
		||||
                    return true
 | 
			
		||||
if (personAcceptEmail) {
 | 
			
		||||
    new ShowHide({
 | 
			
		||||
        froms: [personEmail],
 | 
			
		||||
        container: [personAcceptEmail],
 | 
			
		||||
        test: function(froms) {
 | 
			
		||||
            for (let f of froms.values()) {
 | 
			
		||||
                for (let input of f.querySelectorAll('input').values()) {
 | 
			
		||||
                    if (input.value) {
 | 
			
		||||
                        return true
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
    event_name: 'input'
 | 
			
		||||
});
 | 
			
		||||
            return false;
 | 
			
		||||
        },
 | 
			
		||||
        event_name: 'input'
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
new ShowHide({
 | 
			
		||||
    froms: [personPhoneNumber],
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
      <p>{{ $t('pick_social_issue_linked_with_action') }}</p>
 | 
			
		||||
 | 
			
		||||
      <div v-for="si in socialIssues">
 | 
			
		||||
        <input type="radio" v-bind:value="si.id" name="socialIssue" v-model="socialIssuePicked"> {{ si.title.fr }}
 | 
			
		||||
        <input type="radio" v-bind:value="si.id" name="socialIssue" v-model="socialIssuePicked"><span class="badge bg-chill-l-gray text-dark">{{ si.text }}</span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div v-if="hasSocialIssuePicked">
 | 
			
		||||
@@ -222,3 +222,16 @@ export default {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
@import 'ChillMainAssets/module/bootstrap/shared';
 | 
			
		||||
@import 'ChillPersonAssets/chill/scss/mixins';
 | 
			
		||||
@import 'ChillMainAssets/chill/scss/chill_variables';
 | 
			
		||||
span.badge {
 | 
			
		||||
   @include badge_social($social-issue-color);
 | 
			
		||||
   font-size: 95%;
 | 
			
		||||
   margin-bottom: 5px;
 | 
			
		||||
   margin-right: 1em;
 | 
			
		||||
   margin-left: 1em;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -15,4 +15,5 @@ chill_person:
 | 
			
		||||
        phonenumber: hidden
 | 
			
		||||
        country_of_birth: hidden
 | 
			
		||||
        marital_status: hidden
 | 
			
		||||
        spoken_languages: hidden
 | 
			
		||||
        spoken_languages: hidden
 | 
			
		||||
        civility: hidden
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
    window.accompanyingCourse = {{ json|json_encode|raw }};
 | 
			
		||||
  </script>
 | 
			
		||||
 | 
			
		||||
  {{ parent() }}
 | 
			
		||||
  {{ encore_entry_script_tags('vue_accourse_work_create') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,7 @@
 | 
			
		||||
                            <div class="wl-row">
 | 
			
		||||
                                <div class="wl-col title"><h3>{{ 'Participants'|trans }}</h3></div>
 | 
			
		||||
                                <div class="wl-col list">
 | 
			
		||||
                                    {% for p in accompanying_period.participations %}
 | 
			
		||||
                                    {% for p in accompanying_period.getCurrentParticipations %}
 | 
			
		||||
                                        <span class="wl-item badge-person">
 | 
			
		||||
                                            <a href="{{ path('chill_person_accompanying_period_list', { person_id: p.person.id }) }}">
 | 
			
		||||
                                                {{ p.person|chill_entity_render_string }}
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@
 | 
			
		||||
            {%- endif -%}
 | 
			
		||||
        </div>
 | 
			
		||||
        {%- if options['addInfo'] -%}
 | 
			
		||||
            {% set gender = (person.gender == 'woman') ? 'fa-venus' : 
 | 
			
		||||
            {% set gender = (person.gender == 'woman') ? 'fa-venus' :
 | 
			
		||||
                (person.gender == 'man') ? 'fa-mars' : (person.gender == 'neuter') ? 'fa-neuter' : 'fa-genderless' %}
 | 
			
		||||
            {% set genderTitle = (person.gender == 'woman') ? 'woman' :
 | 
			
		||||
                (person.gender == 'man') ? 'man' : (person.gender == 'neuter') ? 'neuter' : 'Not given'|trans %}
 | 
			
		||||
@@ -123,13 +123,15 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="item-col">
 | 
			
		||||
        <ul class="list-content fa-ul">
 | 
			
		||||
            {% set multiline = (options['address_multiline']) ? true : false %}
 | 
			
		||||
            {{ person.getLastAddress|chill_entity_render_box({
 | 
			
		||||
                'render': 'list',
 | 
			
		||||
                'with_picto': true,
 | 
			
		||||
                'multiline': multiline,
 | 
			
		||||
                'with_valid_from': false
 | 
			
		||||
            }) }}
 | 
			
		||||
            {% if person.getCurrentPersonAddress is not null %}
 | 
			
		||||
                {% set multiline = (options['address_multiline']) ? true : false %}
 | 
			
		||||
                {{ person.getCurrentPersonAddress|chill_entity_render_box({
 | 
			
		||||
                    'render': 'list',
 | 
			
		||||
                    'with_picto': true,
 | 
			
		||||
                    'multiline': multiline,
 | 
			
		||||
                    'with_valid_from': false
 | 
			
		||||
                }) }}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <li>
 | 
			
		||||
                {% if person.mobilenumber %}
 | 
			
		||||
                    <i class="fa fa-li fa-mobile"></i><a href="{{ 'tel:' ~ person.mobilenumber }}">
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,37 @@
 | 
			
		||||
 | 
			
		||||
    {% include 'ChillPersonBundle:AccompanyingPeriod:_list.html.twig' %}
 | 
			
		||||
 | 
			
		||||
    {% if accompanying_periods_old|length > 0 %}
 | 
			
		||||
        <style>
 | 
			
		||||
                button[aria-expanded="true"] > span.folded,
 | 
			
		||||
                button[aria-expanded="false"] > span.unfolded { display: none; }
 | 
			
		||||
                button[aria-expanded="false"] > span.folded,
 | 
			
		||||
                button[aria-expanded="true"] > span.unfolded { display: inline; }
 | 
			
		||||
            </style>
 | 
			
		||||
            <div class="accordion" id="nonCurrent">
 | 
			
		||||
                <div class="accordion-item">
 | 
			
		||||
                    <h2 class="accordion-header" id="heading_{{ household.id }}">
 | 
			
		||||
                        <button
 | 
			
		||||
                            class="accordion-button collapsed"
 | 
			
		||||
                            type="button"
 | 
			
		||||
                            data-bs-toggle="collapse"
 | 
			
		||||
                            data-bs-target="#collapse_{{ household.id }}"
 | 
			
		||||
                            aria-expanded="false"
 | 
			
		||||
                            aria-controls="collapse_{{ household.id }}">
 | 
			
		||||
                            <span class="folded">{{ 'household.Show accompanying periods of past or future memberships'|trans({'length': accompanying_periods_old|length}) }}</span>
 | 
			
		||||
                            <span class="unfolded text-secondary">{{ 'household.Hide memberships'|trans }}</span>
 | 
			
		||||
                        </button>
 | 
			
		||||
                    </h2>
 | 
			
		||||
                    <div id="collapse_{{ household.id }}"
 | 
			
		||||
                        class="accordion-collapse collapse"
 | 
			
		||||
                        aria-labelledby="heading_{{ household.id }}"
 | 
			
		||||
                        data-bs-parent="#nonCurrent">
 | 
			
		||||
                        {% include 'ChillPersonBundle:AccompanyingPeriod:_list.html.twig' with {'accompanying_periods' : accompanying_periods_old} %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
    <ul class="record_actions sticky-form-buttons">
 | 
			
		||||
        <li class="cancel">
 | 
			
		||||
            <a href="{{ path ('chill_person_household_summary', {'household_id' : household.id } ) }}" class="btn btn-cancel">
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,11 @@
 | 
			
		||||
 | 
			
		||||
                            {%- for m in members -%}
 | 
			
		||||
                                <span
 | 
			
		||||
                                    class="badge-member{%- if m.holder %} holder{% endif -%}{%- if m.position.ordering >= 2 %} child{% endif -%}"
 | 
			
		||||
                                    title="{{ m.position.label.fr }}">
 | 
			
		||||
                                    class="badge-member{%- if m.holder %} holder{% endif -%}"
 | 
			
		||||
                                    {% if m.position is not null %}
 | 
			
		||||
                                        title="{{ m.position.label.fr }}"
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                >
 | 
			
		||||
                                    <a href="{{ path('chill_person_view', { person_id: m.person.id}) }}">
 | 
			
		||||
                                        {%- if m.holder %}
 | 
			
		||||
                                            <span class="fa-stack fa-holder" title="{{ 'household.holder'|trans }}">
 | 
			
		||||
 
 | 
			
		||||
@@ -106,81 +106,95 @@
 | 
			
		||||
 | 
			
		||||
    <h2 class="my-5">{{ 'household.Household members'|trans }}</h2>
 | 
			
		||||
 | 
			
		||||
    {% for p in positions %}
 | 
			
		||||
    <div class="mb-5">
 | 
			
		||||
      <h3>{{ p.label|localize_translatable_string }}
 | 
			
		||||
          {% if false == p.shareHousehold %}
 | 
			
		||||
              <i class="chill-help-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true"
 | 
			
		||||
                 title="{{ 'household.Those members does not share address'|trans }}"></i>
 | 
			
		||||
          {% endif %}
 | 
			
		||||
      </h3>
 | 
			
		||||
    {% for p in positions|merge([ '_none' ]) %}
 | 
			
		||||
 | 
			
		||||
      {%- set members = household.currentMembersByPosition(p) %}
 | 
			
		||||
        {% if p == '_none' %}
 | 
			
		||||
            {% set members = household.currentMembersWithoutPosition %}
 | 
			
		||||
            {% set old_members = household.nonCurrentMembersWithoutPosition %}
 | 
			
		||||
        {% else %}
 | 
			
		||||
            {%- set members = household.currentMembersByPosition(p) %}
 | 
			
		||||
            {% set old_members = household.nonCurrentMembersByPosition(p) %}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
      {% macro customButtons(member, household) %}
 | 
			
		||||
          <li>
 | 
			
		||||
              <a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'persons': [ member.person.id ], 'allow_leave_without_household': true } ) }}"
 | 
			
		||||
                 class="btn btn-sm btn-unlink" title="{{ 'household.person.leave'|trans }}"></a>
 | 
			
		||||
          </li>
 | 
			
		||||
          <li>
 | 
			
		||||
              <a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'persons': [ member.person.id ], 'household': household.id} ) }}"
 | 
			
		||||
                 class="btn btn-sm btn-misc" title="{{ 'household.Change position'|trans }}"><i class="fa fa-arrows-h"></i></a>
 | 
			
		||||
          </li>
 | 
			
		||||
      {% endmacro %}
 | 
			
		||||
        {% if not (p == '_none' and members|length == 0 and old_members|length == 0) %}
 | 
			
		||||
            <div class="mb-5">
 | 
			
		||||
                {% if p != '_none' %}
 | 
			
		||||
                  <h3>{{ p.label|localize_translatable_string }}
 | 
			
		||||
                      {% if false == p.shareHousehold %}
 | 
			
		||||
                          <i class="chill-help-tooltip" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true"
 | 
			
		||||
                             title="{{ 'household.Those members does not share address'|trans }}"></i>
 | 
			
		||||
                      {% endif %}
 | 
			
		||||
                  </h3>
 | 
			
		||||
                {% else %}
 | 
			
		||||
                    <h3 class="chill-entity">{{ 'household.Members without position'|trans }}</h3>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
 | 
			
		||||
      {% if members|length > 0 %}
 | 
			
		||||
        <div class="flex-table list-household-members">
 | 
			
		||||
          {% for m in members %}
 | 
			
		||||
              {% include '@ChillPerson/Household/_render_member.html.twig' with {
 | 
			
		||||
                  'member': m,
 | 
			
		||||
                  'customButtons': { 'after': _self.customButtons(m, household) }
 | 
			
		||||
              } %}
 | 
			
		||||
          {% endfor %}
 | 
			
		||||
        </div>
 | 
			
		||||
      {% else %}
 | 
			
		||||
        <p class="chill-no-data-statement">{{ 'household.Any persons into this position'|trans }}</p>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
 | 
			
		||||
      {% set members = household.nonCurrentMembersByPosition(p) %}
 | 
			
		||||
      {% if members|length > 0 %}
 | 
			
		||||
              {% macro customButtons(member, household) %}
 | 
			
		||||
                  <li>
 | 
			
		||||
                      <a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'persons': [ member.person.id ], 'allow_leave_without_household': true } ) }}"
 | 
			
		||||
                         class="btn btn-sm btn-unlink" title="{{ 'household.person.leave'|trans }}"></a>
 | 
			
		||||
                  </li>
 | 
			
		||||
                  <li>
 | 
			
		||||
                      <a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'persons': [ member.person.id ], 'household': household.id} ) }}"
 | 
			
		||||
                         class="btn btn-sm btn-misc" title="{{ 'household.Change position'|trans }}"><i class="fa fa-arrows-h"></i></a>
 | 
			
		||||
                  </li>
 | 
			
		||||
              {% endmacro %}
 | 
			
		||||
 | 
			
		||||
        <style>
 | 
			
		||||
            button[aria-expanded="true"] > span.folded,
 | 
			
		||||
            button[aria-expanded="false"] > span.unfolded { display: none; }
 | 
			
		||||
            button[aria-expanded="false"] > span.folded,
 | 
			
		||||
            button[aria-expanded="true"] > span.unfolded { display: inline; }
 | 
			
		||||
        </style>
 | 
			
		||||
        <div class="accordion" id="nonCurrent">
 | 
			
		||||
            <div class="accordion-item">
 | 
			
		||||
                <h2 class="accordion-header" id="heading_{{ p.id }}">
 | 
			
		||||
                    <button
 | 
			
		||||
                        class="accordion-button collapsed"
 | 
			
		||||
                        type="button"
 | 
			
		||||
                        data-bs-toggle="collapse"
 | 
			
		||||
                        data-bs-target="#collapse_{{ p.id }}"
 | 
			
		||||
                        aria-expanded="false"
 | 
			
		||||
                        aria-controls="collapse_{{ p.id }}">
 | 
			
		||||
                        <span class="folded">{{ 'household.Show future or past memberships'|trans({'length': members|length}) }}</span>
 | 
			
		||||
                        <span class="unfolded text-secondary">{{ 'household.Hide memberships'|trans }}</span>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </h2>
 | 
			
		||||
                <div id="collapse_{{ p.id }}"
 | 
			
		||||
                    class="accordion-collapse collapse"
 | 
			
		||||
                    aria-labelledby="heading_{{ p.id }}"
 | 
			
		||||
                    data-bs-parent="#nonCurrent">
 | 
			
		||||
                    <div class="flex-table my-0 list-household-members">
 | 
			
		||||
                        {% for m in members %}
 | 
			
		||||
                            {% include '@ChillPerson/Household/_render_member.html.twig' with { 'member': m } %}
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
              {% if members|length > 0 %}
 | 
			
		||||
                <div class="flex-table list-household-members">
 | 
			
		||||
                  {% for m in members %}
 | 
			
		||||
                      {% include '@ChillPerson/Household/_render_member.html.twig' with {
 | 
			
		||||
                          'member': m,
 | 
			
		||||
                          'customButtons': { 'after': _self.customButtons(m, household) }
 | 
			
		||||
                      } %}
 | 
			
		||||
                  {% endfor %}
 | 
			
		||||
                </div>
 | 
			
		||||
              {% else %}
 | 
			
		||||
                <p class="chill-no-data-statement">{{ 'household.Any persons into this position'|trans }}</p>
 | 
			
		||||
              {% endif %}
 | 
			
		||||
 | 
			
		||||
              {% if old_members|length > 0 %}
 | 
			
		||||
                <style>
 | 
			
		||||
                    button[aria-expanded="true"] > span.folded,
 | 
			
		||||
                    button[aria-expanded="false"] > span.unfolded { display: none; }
 | 
			
		||||
                    button[aria-expanded="false"] > span.folded,
 | 
			
		||||
                    button[aria-expanded="true"] > span.unfolded { display: inline; }
 | 
			
		||||
                </style>
 | 
			
		||||
                <div class="accordion" id="nonCurrent">
 | 
			
		||||
                    <div class="accordion-item">
 | 
			
		||||
                        <h2 class="accordion-header" id="heading_{{ p == '_none' ? '_none' : p.id }}">
 | 
			
		||||
                            <button
 | 
			
		||||
                                class="accordion-button collapsed"
 | 
			
		||||
                                type="button"
 | 
			
		||||
                                data-bs-toggle="collapse"
 | 
			
		||||
                                data-bs-target="#collapse_{{ p == '_none' ? '_none' : p.id }}"
 | 
			
		||||
                                aria-expanded="false"
 | 
			
		||||
                                aria-controls="collapse_{{ p == '_none' ? '_none' : p.id }}">
 | 
			
		||||
                                <span class="folded">{{ 'household.Show future or past memberships'|trans({'length': old_members|length}) }}</span>
 | 
			
		||||
                                <span class="unfolded text-secondary">{{ 'household.Hide memberships'|trans }}</span>
 | 
			
		||||
                            </button>
 | 
			
		||||
                        </h2>
 | 
			
		||||
                        <div id="collapse_{{ p == '_none' ? '_none' : p.id }}"
 | 
			
		||||
                            class="accordion-collapse collapse"
 | 
			
		||||
                            aria-labelledby="heading_{{ p == '_none' ? '_none' : p.id }}"
 | 
			
		||||
                            data-bs-parent="#nonCurrent">
 | 
			
		||||
                            <div class="flex-table my-0 list-household-members">
 | 
			
		||||
                                {% for m in old_members %}
 | 
			
		||||
                                    {% include '@ChillPerson/Household/_render_member.html.twig' with { 'member': m } %}
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
              {% endif %}
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <ul class="record_actions">
 | 
			
		||||
      <li>
 | 
			
		||||
        <a href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'household': household.id }) }}"
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,9 @@
 | 
			
		||||
 | 
			
		||||
    <fieldset>
 | 
			
		||||
        <legend><h2>{{ 'General information'|trans }}</h2></legend>
 | 
			
		||||
        {%- if form.civility is defined -%}
 | 
			
		||||
        {{ form_row(form.civility, {'label' : 'Civility'}) }}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        {{ form_row(form.firstName, {'label' : 'First name'}) }}
 | 
			
		||||
        {{ form_row(form.lastName, {'label' : 'Last name'}) }}
 | 
			
		||||
        {% if form.altNames is defined %}
 | 
			
		||||
@@ -95,6 +98,8 @@
 | 
			
		||||
            <div id="personEmail">
 | 
			
		||||
                {{ form_row(form.email, {'label': 'Email'}) }}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        {%- if form.acceptEmail is defined -%}
 | 
			
		||||
            <div id="personAcceptEmail">
 | 
			
		||||
                {{ form_row(form.acceptEmail, {'label' : 'Accept emails ?'}) }}
 | 
			
		||||
            </div>
 | 
			
		||||
@@ -135,12 +140,11 @@
 | 
			
		||||
            </button>
 | 
			
		||||
        </li>
 | 
			
		||||
    </ul>
 | 
			
		||||
 | 
			
		||||
    {{ form_end(form) }}
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block js %}
 | 
			
		||||
    {{ encore_entry_link_tags('page_person') }}
 | 
			
		||||
    {{ encore_entry_script_tags('page_person') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@
 | 
			
		||||
                        'addLink': true,
 | 
			
		||||
                        'addInfo': true,
 | 
			
		||||
                        'addAge': true,
 | 
			
		||||
                        'addAltNames': false,
 | 
			
		||||
                        'addAltNames': true,
 | 
			
		||||
                        'addCenter': true,
 | 
			
		||||
                        'address_multiline': false,
 | 
			
		||||
                        'customButtons': { 'after': _self.button_person(person) }
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,15 @@ This view should receive those arguments:
 | 
			
		||||
            <figure class="person-details">
 | 
			
		||||
                <h2 class="chill-red">{{ 'General information'|trans }}</h2>
 | 
			
		||||
                <dl>
 | 
			
		||||
                    {% if person.civility is not null %}
 | 
			
		||||
                        <dt>{{ 'Civility'|trans }} :</dt>
 | 
			
		||||
                        <dd>
 | 
			
		||||
                        {% if person.civility.name|length > 0 %}
 | 
			
		||||
                        {{ person.civility.name|first }}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        </dd>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
 | 
			
		||||
                    <dt>{{ 'First name'|trans }} :</dt>
 | 
			
		||||
                    <dd>{{ person.firstName }}</dd>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -193,8 +193,6 @@ class HouseholdMemberControllerTest extends WebTestCase
 | 
			
		||||
 | 
			
		||||
        $form = $crawler->selectButton('Enregistrer')
 | 
			
		||||
            ->form();
 | 
			
		||||
        $form['household_member[endDate]'] = (new \DateTime('tomorrow'))
 | 
			
		||||
            ->format('Y-m-d');
 | 
			
		||||
 | 
			
		||||
        $crawler = $client->submit($form);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,8 @@
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Chill is a software for social workers
 | 
			
		||||
 * 
 | 
			
		||||
 * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, 
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
 | 
			
		||||
 * <http://www.champs-libres.coop>, <info@champs-libres.coop>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
@@ -38,57 +38,57 @@ use Generator;
 | 
			
		||||
class PersonTest extends \PHPUnit\Framework\TestCase
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Test the creation of an accompanying, its closure and the access to 
 | 
			
		||||
     * Test the creation of an accompanying, its closure and the access to
 | 
			
		||||
     * the current accompaniying period via the getCurrentAccompanyingPeriod
 | 
			
		||||
     * function.
 | 
			
		||||
     */
 | 
			
		||||
    public function testGetCurrentAccompanyingPeriod()
 | 
			
		||||
    {
 | 
			
		||||
        $d = new \DateTime('yesterday'); 
 | 
			
		||||
        $d = new \DateTime('yesterday');
 | 
			
		||||
        $p = new Person();
 | 
			
		||||
        $p->addAccompanyingPeriod(new AccompanyingPeriod($d));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $period = $p->getCurrentAccompanyingPeriod();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $this->assertInstanceOf(AccompanyingPeriod::class, $period);
 | 
			
		||||
        $this->assertTrue($period->isOpen());
 | 
			
		||||
        $this->assertEquals($d, $period->getOpeningDate());
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        //close and test
 | 
			
		||||
        $period->setClosingDate(new \DateTime('tomorrow'));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $shouldBeNull = $p->getCurrentAccompanyingPeriod();
 | 
			
		||||
        $this->assertNull($shouldBeNull);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test if the getAccompanyingPeriodsOrdered function return a list of
 | 
			
		||||
     * periods ordered ascendency.
 | 
			
		||||
     */
 | 
			
		||||
    public function testAccompanyingPeriodOrderWithUnorderedAccompanyingPeriod()
 | 
			
		||||
    {       
 | 
			
		||||
    {
 | 
			
		||||
        $d = new \DateTime("2013/2/1");
 | 
			
		||||
        $p = new Person();
 | 
			
		||||
        $p->addAccompanyingPeriod(new AccompanyingPeriod($d));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $e = new \DateTime("2013/3/1");
 | 
			
		||||
        $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e);
 | 
			
		||||
        $p->close($period);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $f = new \DateTime("2013/1/1");
 | 
			
		||||
        $p->open(new AccompanyingPeriod($f));
 | 
			
		||||
        
 | 
			
		||||
        $g = new \DateTime("2013/4/1"); 
 | 
			
		||||
 | 
			
		||||
        $g = new \DateTime("2013/4/1");
 | 
			
		||||
        $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($g);
 | 
			
		||||
        $p->close($period);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $r = $p->getAccompanyingPeriodsOrdered();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $date = $r[0]->getOpeningDate()->format('Y-m-d');
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($date, '2013-01-01');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test if the getAccompanyingPeriodsOrdered function, for periods
 | 
			
		||||
     * starting at the same time order regarding to the closing date.
 | 
			
		||||
@@ -97,25 +97,25 @@ class PersonTest extends \PHPUnit\Framework\TestCase
 | 
			
		||||
        $d = new \DateTime("2013/2/1");
 | 
			
		||||
        $p = new Person();
 | 
			
		||||
        $p->addAccompanyingPeriod(new AccompanyingPeriod($d));
 | 
			
		||||
        
 | 
			
		||||
        $g = new \DateTime("2013/4/1"); 
 | 
			
		||||
 | 
			
		||||
        $g = new \DateTime("2013/4/1");
 | 
			
		||||
        $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($g);
 | 
			
		||||
        $p->close($period);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $f = new \DateTime("2013/2/1");
 | 
			
		||||
        $p->open(new AccompanyingPeriod($f));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $e = new \DateTime("2013/3/1");
 | 
			
		||||
        $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e);
 | 
			
		||||
        $p->close($period);
 | 
			
		||||
 | 
			
		||||
        $r = $p->getAccompanyingPeriodsOrdered();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $date = $r[0]->getClosingDate()->format('Y-m-d');
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($date, '2013-03-01');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test if the function checkAccompanyingPeriodIsNotCovering returns
 | 
			
		||||
     * the good constant when two periods are collapsing : a period
 | 
			
		||||
@@ -125,23 +125,23 @@ class PersonTest extends \PHPUnit\Framework\TestCase
 | 
			
		||||
        $d = new \DateTime("2013/2/1");
 | 
			
		||||
        $p = new Person();
 | 
			
		||||
        $p->addAccompanyingPeriod(new AccompanyingPeriod($d));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $e = new \DateTime("2013/3/1");
 | 
			
		||||
        $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e);
 | 
			
		||||
        $p->close($period);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $f = new \DateTime("2013/1/1");
 | 
			
		||||
        $p->open(new AccompanyingPeriod($f));
 | 
			
		||||
        
 | 
			
		||||
        $g = new \DateTime("2013/4/1"); 
 | 
			
		||||
 | 
			
		||||
        $g = new \DateTime("2013/4/1");
 | 
			
		||||
        $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($g);
 | 
			
		||||
        $p->close($period);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $r = $p->checkAccompanyingPeriodsAreNotCollapsing();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($r['result'], Person::ERROR_PERIODS_ARE_COLLAPSING);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test if the function checkAccompanyingPeriodIsNotCovering returns
 | 
			
		||||
     * the good constant when two periods are collapsing : a period is open
 | 
			
		||||
@@ -151,68 +151,19 @@ class PersonTest extends \PHPUnit\Framework\TestCase
 | 
			
		||||
        $d = new \DateTime("2013/2/1");
 | 
			
		||||
        $p = new Person();
 | 
			
		||||
        $p->addAccompanyingPeriod(new AccompanyingPeriod($d));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $e = new \DateTime("2013/3/1");
 | 
			
		||||
        $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e);
 | 
			
		||||
        $p->close($period);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $f = new \DateTime("2013/1/1");
 | 
			
		||||
        $p->open(new AccompanyingPeriod($f));
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $r = $p->checkAccompanyingPeriodsAreNotCollapsing();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($r['result'], Person::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function dateProvider(): Generator
 | 
			
		||||
    {
 | 
			
		||||
        yield [(DateTime::createFromFormat('Y-m-d', '2021-01-05'))->settime(0, 0)];
 | 
			
		||||
        yield [(DateTime::createFromFormat('Y-m-d', '2021-02-05'))->settime(0, 0)];
 | 
			
		||||
        yield [(DateTime::createFromFormat('Y-m-d', '2021-03-05'))->settime(0, 0)];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @dataProvider dateProvider
 | 
			
		||||
     */
 | 
			
		||||
    public function testGetLastAddress(DateTime $date)
 | 
			
		||||
    {
 | 
			
		||||
        $p = new Person($date);
 | 
			
		||||
 | 
			
		||||
        // Make sure that there is no last address.
 | 
			
		||||
        $this::assertNull($p->getLastAddress());
 | 
			
		||||
 | 
			
		||||
        // Take an arbitrary date before the $date in parameter.
 | 
			
		||||
        $addressDate = clone $date;
 | 
			
		||||
 | 
			
		||||
        // 1. Smoke test: Test that the first address added is the last one.
 | 
			
		||||
        $address1 = (new Address())->setValidFrom($addressDate->sub(new DateInterval('PT180M')));
 | 
			
		||||
        $p->addAddress($address1);
 | 
			
		||||
 | 
			
		||||
        $this::assertCount(1, $p->getAddresses());
 | 
			
		||||
        $this::assertSame($address1, $p->getLastAddress());
 | 
			
		||||
 | 
			
		||||
        // 2. Add an older address, which should not be the last address.
 | 
			
		||||
        $addressDate2 = clone $addressDate;
 | 
			
		||||
        $address2 = (new Address())->setValidFrom($addressDate2->sub(new DateInterval('PT30M')));
 | 
			
		||||
        $p->addAddress($address2);
 | 
			
		||||
 | 
			
		||||
        $this::assertCount(2, $p->getAddresses());
 | 
			
		||||
        $this::assertSame($address1, $p->getLastAddress());
 | 
			
		||||
 | 
			
		||||
        // 3. Add a newer address, which should be the last address.
 | 
			
		||||
        $addressDate3 = clone $addressDate;
 | 
			
		||||
        $address3 = (new Address())->setValidFrom($addressDate3->add(new DateInterval('PT30M')));
 | 
			
		||||
        $p->addAddress($address3);
 | 
			
		||||
 | 
			
		||||
        $this::assertCount(3, $p->getAddresses());
 | 
			
		||||
        $this::assertSame($address3, $p->getLastAddress());
 | 
			
		||||
 | 
			
		||||
        // 4. Get the last address from a specific date.
 | 
			
		||||
        $this::assertEquals($address1, $p->getLastAddress($addressDate));
 | 
			
		||||
        $this::assertEquals($address2, $p->getLastAddress($addressDate2));
 | 
			
		||||
        $this::assertEquals($address3, $p->getLastAddress($addressDate3));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIsSharingHousehold()
 | 
			
		||||
    {
 | 
			
		||||
        $person = new Person();
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ namespace Chill\PersonBundle\Widget;
 | 
			
		||||
use Chill\MainBundle\Templating\Widget\WidgetInterface;
 | 
			
		||||
use Doctrine\ORM\EntityRepository;
 | 
			
		||||
use Doctrine\ORM\Query\Expr;
 | 
			
		||||
use Doctrine\DBAL\Types\Type;
 | 
			
		||||
use Doctrine\DBAL\Types\Types;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
 | 
			
		||||
use Symfony\Component\Security\Core\User\UserInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
 | 
			
		||||
@@ -33,11 +33,11 @@ use Chill\CustomFieldsBundle\Entity\CustomField;
 | 
			
		||||
use Twig\Environment;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * add a widget with person list. 
 | 
			
		||||
 * 
 | 
			
		||||
 * add a widget with person list.
 | 
			
		||||
 *
 | 
			
		||||
 * The configuration is defined by `PersonListWidgetFactory`
 | 
			
		||||
 * 
 | 
			
		||||
 * If the options 'custom_fields' is used, the custom fields entity will be 
 | 
			
		||||
 *
 | 
			
		||||
 * If the options 'custom_fields' is used, the custom fields entity will be
 | 
			
		||||
 * queried from the db and transmitted to the view under the `customFields` variable.
 | 
			
		||||
 */
 | 
			
		||||
class PersonListWidget implements WidgetInterface
 | 
			
		||||
@@ -48,33 +48,33 @@ class PersonListWidget implements WidgetInterface
 | 
			
		||||
     * @var PersonRepository
 | 
			
		||||
     */
 | 
			
		||||
    protected $personRepository;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The entity manager
 | 
			
		||||
     *
 | 
			
		||||
     * @var EntityManager
 | 
			
		||||
     */
 | 
			
		||||
    protected $entityManager;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * the authorization helper
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @var AuthorizationHelper;
 | 
			
		||||
     */
 | 
			
		||||
    protected $authorizationHelper;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var TokenStorage
 | 
			
		||||
     */
 | 
			
		||||
    protected $tokenStorage;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var UserInterface
 | 
			
		||||
     */
 | 
			
		||||
    protected $user;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
            PersonRepository $personRepostory,
 | 
			
		||||
            EntityManager $em,
 | 
			
		||||
@@ -88,26 +88,26 @@ class PersonListWidget implements WidgetInterface
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @param type $place
 | 
			
		||||
     * @param array $context
 | 
			
		||||
     * @param array $config
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function render(Environment $env, $place, array $context, array $config)
 | 
			
		||||
    { 
 | 
			
		||||
    {
 | 
			
		||||
        $numberOfItems = $config['number_of_items'] ?? 20;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $qb = $this->personRepository
 | 
			
		||||
                ->createQueryBuilder('person');
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // show only the person from the authorized centers
 | 
			
		||||
        $and = $qb->expr()->andX();
 | 
			
		||||
        $centers = $this->authorizationHelper
 | 
			
		||||
                ->getReachableCenters($this->getUser(), new Role(PersonVoter::SEE));
 | 
			
		||||
        $and->add($qb->expr()->in('person.center', ':centers'));
 | 
			
		||||
        $qb->setParameter('centers', $centers);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // add the "only active" query
 | 
			
		||||
        if (\array_key_exists('only_active', $config) && $config['only_active'] === true) {
 | 
			
		||||
@@ -123,37 +123,37 @@ class PersonListWidget implements WidgetInterface
 | 
			
		||||
                    (new Expr())->between(':now', 'ap.openingDate', 'ap.closingDate')
 | 
			
		||||
                );
 | 
			
		||||
            $and->add($or);
 | 
			
		||||
            $qb->setParameter('now', new \DateTime(), Type::DATE);
 | 
			
		||||
            $qb->setParameter('now', new \DateTime(), Types::DATE_MUTABLE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (\array_key_exists('filtering_class', $config) && $config['filtering_class'] !== NULL) {
 | 
			
		||||
            $filteringClass = new $config['filtering_class'];
 | 
			
		||||
            if ( ! $filteringClass instanceof PersonListWidget\PersonFilteringInterface) {
 | 
			
		||||
                throw new \UnexpectedValueException(sprintf("the class %s does not "
 | 
			
		||||
                        . "implements %s", $config['filtering_class'], 
 | 
			
		||||
                        . "implements %s", $config['filtering_class'],
 | 
			
		||||
                        PersonListWidget\PersonFilteringInterface::class));
 | 
			
		||||
            }
 | 
			
		||||
            $ids = $filteringClass->getPersonIds($this->entityManager, 
 | 
			
		||||
            $ids = $filteringClass->getPersonIds($this->entityManager,
 | 
			
		||||
                    $this->getUser());
 | 
			
		||||
            $in = (new Expr())->in('person.id', ':ids');
 | 
			
		||||
            $and->add($in);
 | 
			
		||||
            $qb->setParameter('ids', $ids);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // adding the where clause to the query
 | 
			
		||||
        $qb->where($and);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // ordering the query by lastname, firstname
 | 
			
		||||
        $qb->addOrderBy('person.lastName', 'ASC')
 | 
			
		||||
                ->addOrderBy('person.firstName', 'ASC');
 | 
			
		||||
        
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $qb->setFirstResult(0)->setMaxResults($numberOfItems);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $persons = $qb->getQuery()->getResult();
 | 
			
		||||
        
 | 
			
		||||
        // get some custom field when the view is overriden and we want to 
 | 
			
		||||
 | 
			
		||||
        // get some custom field when the view is overriden and we want to
 | 
			
		||||
        // show some custom field in the overriden view.
 | 
			
		||||
        $cfields = array();
 | 
			
		||||
        if (isset($config['custom_fields'])) {
 | 
			
		||||
@@ -166,39 +166,39 @@ class PersonListWidget implements WidgetInterface
 | 
			
		||||
                    $cfields[$cf->getSlug()] = $cf;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $env->render(
 | 
			
		||||
            'ChillPersonBundle:Widget:homepage_person_list.html.twig',
 | 
			
		||||
            array(
 | 
			
		||||
                'persons' => $persons,
 | 
			
		||||
                'customFields' => $cfields
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                )
 | 
			
		||||
            );
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @return UserInterface
 | 
			
		||||
     * @throws \RuntimeException
 | 
			
		||||
     */
 | 
			
		||||
    private function getUser()
 | 
			
		||||
    {
 | 
			
		||||
        $token = $this->tokenStorage->getToken();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if ($token === null) {
 | 
			
		||||
            throw new \RuntimeException("the token should not be null");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $user = $token->getUser();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (!$user instanceof UserInterface || $user == null) {
 | 
			
		||||
            throw new \RuntimeException("the user should implement UserInterface. "
 | 
			
		||||
                    . "Are you logged in ?");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ use Chill\MainBundle\Entity\User;
 | 
			
		||||
/**
 | 
			
		||||
 * Interface to implement on classes called in configuration for
 | 
			
		||||
 * PersonListWidget (`person_list`), under the key `filtering_class` :
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * ```
 | 
			
		||||
 * widgets:
 | 
			
		||||
 *      homepage:
 | 
			
		||||
@@ -35,41 +35,41 @@ use Chill\MainBundle\Entity\User;
 | 
			
		||||
 *              # where \FQDN\To\Class implements PersonFiltering
 | 
			
		||||
 *              class_filtering: \FQDN\To\Class
 | 
			
		||||
 * ```
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
interface PersonFilteringInterface 
 | 
			
		||||
interface PersonFilteringInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Return an array of persons id to show.
 | 
			
		||||
     * 
 | 
			
		||||
     * Those ids are inserted into the query like this (where ids is the array 
 | 
			
		||||
     *
 | 
			
		||||
     * Those ids are inserted into the query like this (where ids is the array
 | 
			
		||||
     * returned by this class) :
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * ```
 | 
			
		||||
     * SELECT p FROM ChillPersonBundle:Persons p 
 | 
			
		||||
     * SELECT p FROM ChillPersonBundle:Persons p
 | 
			
		||||
     * WHERE p.id IN (:ids)
 | 
			
		||||
     * AND 
 | 
			
		||||
     * AND
 | 
			
		||||
     * -- security/authorization statement: restraint person to authorized centers
 | 
			
		||||
     * p.center in :authorized_centers
 | 
			
		||||
     * ```
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * Example of use : filtering based on custom field data :
 | 
			
		||||
     * ```
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
    class HomepagePersonFiltering implements PersonFilteringInterface {
 | 
			
		||||
 | 
			
		||||
    public function getPersonIds(EntityManager $em, User $user)
 | 
			
		||||
    {
 | 
			
		||||
        $rsmBuilder = new ResultSetMappingBuilder($em);
 | 
			
		||||
        $rsmBuilder->addScalarResult('id', 'id', Type::BIGINT);
 | 
			
		||||
        
 | 
			
		||||
        $rsmBuilder->addScalarResult('id', 'id', Types::BIGINT);
 | 
			
		||||
 | 
			
		||||
        $personTable = $em->getClassMetadata('ChillPersonBundle:Person')
 | 
			
		||||
                ->getTableName();
 | 
			
		||||
        $personIdColumn = $em->getClassMetadata('ChillPersonBundle:Person')
 | 
			
		||||
                ->getColumnName('id');
 | 
			
		||||
        $personCfDataColumn = $em->getClassMetadata('ChillPersonBundle:Person')
 | 
			
		||||
                ->getColumnName('cfData');
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        return $em->createNativeQuery(sprintf("SELECT %s FROM %s WHERE "
 | 
			
		||||
                . "jsonb_exists(%s, 'school-2fb5440e-192c-11e6-b2fd-74d02b0c9b55')",
 | 
			
		||||
                $personIdColumn, $personTable, $personCfDataColumn), $rsmBuilder)
 | 
			
		||||
@@ -77,7 +77,7 @@ interface PersonFilteringInterface
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
     * ```
 | 
			
		||||
     * 
 | 
			
		||||
     *
 | 
			
		||||
     * @param EntityManager $em
 | 
			
		||||
     * @return int[] an array of persons id to show
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,9 @@ services:
 | 
			
		||||
 | 
			
		||||
    Chill\PersonBundle\Form\PersonType:
 | 
			
		||||
        arguments:
 | 
			
		||||
            - '%chill_person.person_fields%'
 | 
			
		||||
            - '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
 | 
			
		||||
            $personFieldsConfiguration: '%chill_person.person_fields%'
 | 
			
		||||
            $configAltNamesHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
 | 
			
		||||
            $translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
 | 
			
		||||
        tags:
 | 
			
		||||
            - { name: form.type, alias: '@chill.person.form.person_creation' }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@ class Version20150607231010 extends AbstractMigration
 | 
			
		||||
        . 'recorded.';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param Schema $schema
 | 
			
		||||
     */
 | 
			
		||||
@@ -28,36 +27,26 @@ class Version20150607231010 extends AbstractMigration
 | 
			
		||||
            'Migration can only be executed safely on \'postgresql\'.'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // retrieve center for setting a default center
 | 
			
		||||
        $centers = $this->connection->fetchAll('SELECT id FROM centers');
 | 
			
		||||
 | 
			
		||||
        if (count($centers) > 0) {
 | 
			
		||||
            $defaultCenterId = $centers[0]['id'];
 | 
			
		||||
        } else { // if no center, performs other checks
 | 
			
		||||
            //check if there are data in person table
 | 
			
		||||
            $nbPeople = $this->connection->fetchColumn('SELECT count(*) FROM person');
 | 
			
		||||
 | 
			
		||||
            if ($nbPeople > 0) {
 | 
			
		||||
                // we have data ! We have to create a center !
 | 
			
		||||
                $newCenterId = $this->connection->fetchColumn('SELECT nextval(\'centers_id_seq\');');
 | 
			
		||||
                $this->addSql(
 | 
			
		||||
                    'INSERT INTO centers (id, name) VALUES (:id, :name)',
 | 
			
		||||
                    ['id' => $newCenterId, 'name' => 'Auto-created center']
 | 
			
		||||
                );
 | 
			
		||||
                $defaultCenterId = $newCenterId;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->addSql('ALTER TABLE person ADD center_id INT');
 | 
			
		||||
 | 
			
		||||
        // retrieve center for setting a default center
 | 
			
		||||
        $stmt = $this->connection->executeQuery("SELECT id FROM centers ORDER BY id ASC LIMIT 1");
 | 
			
		||||
        $center = $stmt->fetchOne();
 | 
			
		||||
 | 
			
		||||
        if ($center !== false) {
 | 
			
		||||
            $defaultCenterId = $center['id'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($defaultCenterId)) {
 | 
			
		||||
            $this->addSql('UPDATE person SET center_id = :id', array('id' => $defaultCenterId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // this will fail if a person is defined, which should not happen any more at this
 | 
			
		||||
        // time of writing (2021-11-09)
 | 
			
		||||
        $this->addSql('ALTER TABLE person ALTER center_id SET NOT NULL');
 | 
			
		||||
        $this->addSql('ALTER TABLE person '
 | 
			
		||||
                . 'ADD CONSTRAINT FK_person_center FOREIGN KEY (center_id) '
 | 
			
		||||
                . 'REFERENCES centers (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
 | 
			
		||||
        $this->addSql('ALTER TABLE person ALTER center_id SET NOT NULL');
 | 
			
		||||
        $this->addSql('CREATE INDEX IDX_person_center ON person (center_id)');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ final class Version20200310090632 extends AbstractMigration
 | 
			
		||||
        $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
 | 
			
		||||
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_person_closingmotive ADD parent_id INT DEFAULT NULL');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_person_closingmotive.name IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_person_closingmotive ADD CONSTRAINT FK_92351ECE727ACA70 FOREIGN KEY (parent_id) REFERENCES chill_person_closingmotive (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
 | 
			
		||||
        $this->addSql('CREATE INDEX IDX_92351ECE727ACA70 ON chill_person_closingmotive (parent_id)');
 | 
			
		||||
        $this->addsql("ALTER TABLE chill_person_closingmotive ADD ordering DOUBLE PRECISION DEFAULT '0' NOT NULL;");
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,5 @@ final class Version20210419112619 extends AbstractMigration
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema) : void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_closingmotive.name IS \'(DC2Type:json_array)\'');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,33 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\Migrations\Person;
 | 
			
		||||
 | 
			
		||||
use Doctrine\DBAL\Schema\Schema;
 | 
			
		||||
use Doctrine\Migrations\AbstractMigration;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add Civility to Person
 | 
			
		||||
 */
 | 
			
		||||
final class Version20211108100849 extends AbstractMigration
 | 
			
		||||
{
 | 
			
		||||
    public function getDescription(): string
 | 
			
		||||
    {
 | 
			
		||||
        return 'Add Civility to Person';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function up(Schema $schema): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_person_person ADD civility_id INT DEFAULT NULL');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_person_person ADD CONSTRAINT FK_BF210A1423D6A298 FOREIGN KEY (civility_id) REFERENCES chill_main_civility (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
 | 
			
		||||
        $this->addSql('CREATE INDEX IDX_BF210A1423D6A298 ON chill_person_person (civility_id)');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_person_person DROP CONSTRAINT FK_BF210A1423D6A298');
 | 
			
		||||
        $this->addSql('DROP INDEX IDX_BF210A1423D6A298');
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_person_person DROP civility_id');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -26,6 +26,12 @@ household:
 | 
			
		||||
            many {Montrer # anciennes ou futures appartenances}
 | 
			
		||||
            other {Montrer # anciennes ou futures appartenances}
 | 
			
		||||
        }
 | 
			
		||||
    Show accompanying periods of past or future memberships: >-
 | 
			
		||||
        {length, plural,
 | 
			
		||||
            one {Montrer les parcours d'une ancienne appartenance}
 | 
			
		||||
            many {Montrer # parcours des anciennes ou futures appartenances}
 | 
			
		||||
            other {Montrer # parcours des 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.
 | 
			
		||||
@@ -77,6 +83,7 @@ household:
 | 
			
		||||
    Household history for %name%: Historique des ménages pour {name}
 | 
			
		||||
    Household shared: Ménages domiciliés
 | 
			
		||||
    Household not shared: Ménage non domiciliés
 | 
			
		||||
    Members without position: Membres non positionnés
 | 
			
		||||
    Never in any household: Membre d'aucun ménage
 | 
			
		||||
    Membership currently running: En cours
 | 
			
		||||
    from: Depuis
 | 
			
		||||
 
 | 
			
		||||
@@ -47,8 +47,8 @@ Phonenumber: 'Numéro de téléphone'
 | 
			
		||||
phonenumber: numéro de téléphone
 | 
			
		||||
Mobilenumber: 'Numéro de téléphone portable'
 | 
			
		||||
mobilenumber: numéro de téléphone portable
 | 
			
		||||
Accept short text message ?: Accepte les SMS?
 | 
			
		||||
Accept short text message: Accepte les SMS
 | 
			
		||||
Accept short text message ?: La personne a donné l'autorisation d'utiliser ce no de téléphone pour l'envoi de rappel par SMS
 | 
			
		||||
Accept short text message: La personne a donné l'autorisation d'utiliser ce no de téléphone pour l'envoi de rappel par SMS
 | 
			
		||||
Other phonenumber: Autre numéro de téléphone
 | 
			
		||||
Description: description
 | 
			
		||||
Add new phone: Ajouter un numéro de téléphone
 | 
			
		||||
@@ -80,6 +80,8 @@ Married: Marié(e)
 | 
			
		||||
'Contact information': 'Informations de contact'
 | 
			
		||||
'Administrative information': Administratif
 | 
			
		||||
File number: Dossier n°
 | 
			
		||||
Civility: Civilité
 | 
			
		||||
choose civility: --
 | 
			
		||||
 | 
			
		||||
# dédoublonnage
 | 
			
		||||
Old person: Doublon
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,19 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 *  License, or (at your option) any later version.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
@@ -74,7 +74,7 @@ class Report implements HasCenterInterface, HasScopeInterface
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     * @ORM\Column(type="json_array")
 | 
			
		||||
     * @ORM\Column(type="json")
 | 
			
		||||
     */
 | 
			
		||||
    private $cFData;
 | 
			
		||||
 | 
			
		||||
@@ -233,7 +233,7 @@ class Report implements HasCenterInterface, HasScopeInterface
 | 
			
		||||
    {
 | 
			
		||||
        return $this->cFGroup;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Center
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -452,7 +452,7 @@ final class SingleTaskController extends AbstractController
 | 
			
		||||
     * @return Response
 | 
			
		||||
     * @Route(
 | 
			
		||||
     *   "/{_locale}/task/single-task/list/my",
 | 
			
		||||
     *   name="chill_task_single_my_tasks"
 | 
			
		||||
     *   name="chill_task_singletask_my_tasks"
 | 
			
		||||
     * )
 | 
			
		||||
     */
 | 
			
		||||
    public function myTasksAction()
 | 
			
		||||
@@ -480,7 +480,7 @@ final class SingleTaskController extends AbstractController
 | 
			
		||||
            ]
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return $this->render('@ChillTask/SingleTask/List/index.html.twig', [
 | 
			
		||||
        return $this->render('@ChillTask/SingleTask/List/index_my_tasks.html.twig', [
 | 
			
		||||
            'tasks' => $tasks,
 | 
			
		||||
            'paginator' => $paginator,
 | 
			
		||||
            'filter_order' => $filterOrder,
 | 
			
		||||
 
 | 
			
		||||
@@ -88,27 +88,27 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
        if ($ended > 0) {
 | 
			
		||||
            $this->addItemInMenu(
 | 
			
		||||
                $menu,
 | 
			
		||||
                $user,
 | 
			
		||||
                '%number% tasks over deadline',
 | 
			
		||||
                'My tasks over deadline',
 | 
			
		||||
                SingleTaskRepository::DATE_STATUS_ENDED,
 | 
			
		||||
                $ended,
 | 
			
		||||
                -15);
 | 
			
		||||
                -15,
 | 
			
		||||
                ['new', 'in_progress'],
 | 
			
		||||
                ['alert']
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($warning > 0) {
 | 
			
		||||
            $this->addItemInMenu(
 | 
			
		||||
                $menu,
 | 
			
		||||
                $user,
 | 
			
		||||
                '%number% tasks near deadline',
 | 
			
		||||
                'My tasks near deadline',
 | 
			
		||||
                SingleTaskRepository::DATE_STATUS_WARNING,
 | 
			
		||||
                $warning,
 | 
			
		||||
                -14);
 | 
			
		||||
                -14,
 | 
			
		||||
                ['new', 'in_progress'],
 | 
			
		||||
                ['warning']
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $menu->addChild("My tasks", [
 | 
			
		||||
                'route' => 'chill_task_single_my_tasks'
 | 
			
		||||
                'route' => 'chill_task_singletask_my_tasks'
 | 
			
		||||
            ])
 | 
			
		||||
            ->setExtras([
 | 
			
		||||
                'order' => -10,
 | 
			
		||||
@@ -117,20 +117,20 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function addItemInMenu(MenuItem $menu, User $u, $message, $title, $status, $number, $order)
 | 
			
		||||
    protected function addItemInMenu(MenuItem $menu, $message, $number, $order, array $states = [], array $status = [])
 | 
			
		||||
    {
 | 
			
		||||
        if ($number > 0) {
 | 
			
		||||
            $menu->addChild(
 | 
			
		||||
                $this->translator->transChoice($message, $number),
 | 
			
		||||
                [
 | 
			
		||||
                    'route' => 'chill_task_singletask_list',
 | 
			
		||||
                    'route' => 'chill_task_singletask_my_tasks',
 | 
			
		||||
                    'routeParameters' => [
 | 
			
		||||
                        'user_id' => $u->getId(),
 | 
			
		||||
                        'status' => [
 | 
			
		||||
                            $status
 | 
			
		||||
                            ],
 | 
			
		||||
                        'hide_form' => true,
 | 
			
		||||
                        'title' => $this->translator->trans($title)
 | 
			
		||||
                        'f' => [
 | 
			
		||||
                            'checkboxes' => [
 | 
			
		||||
                                'states' => $states,
 | 
			
		||||
                                'status' => $status
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                ]
 | 
			
		||||
            ])
 | 
			
		||||
                ->setExtras([
 | 
			
		||||
 
 | 
			
		||||
@@ -284,8 +284,8 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
 | 
			
		||||
                               $qb->expr()->andX(
 | 
			
		||||
                                   $qb->expr()->not($qb->expr()->isNull('t.endDate')),
 | 
			
		||||
                                   $qb->expr()->not($qb->expr()->isNull('t.warningInterval')),
 | 
			
		||||
                                   $qb->expr()->gte('t.endDate - t.warningInterval', ':now'),
 | 
			
		||||
                                   $qb->expr()->lt('t.endDate', ':now')
 | 
			
		||||
                                   $qb->expr()->lte('t.endDate - t.warningInterval', ':now'),
 | 
			
		||||
                                   $qb->expr()->gt('t.endDate', ':now')
 | 
			
		||||
                               )
 | 
			
		||||
                           );
 | 
			
		||||
                       $qb
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
 | 
			
		||||
use Symfony\Component\Security\Core\Role\Role;
 | 
			
		||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
 | 
			
		||||
use Doctrine\DBAL\Types\Type;
 | 
			
		||||
use Doctrine\DBAL\Types\Types;
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -120,13 +120,13 @@ class SingleTaskRepository extends EntityRepository
 | 
			
		||||
                throw new \UnexpectedValueException("params 'center' should be an instance of ".Center::class);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (\array_key_exists('unassigned', $params) and $params['unassigned'] === true) {
 | 
			
		||||
            if (\array_key_exists('user', $params) and !empty($params['user'])) {
 | 
			
		||||
                throw new \UnexpectedValueException("You should not require for "
 | 
			
		||||
                    . "unassigned tasks and tasks assigned to some user.");
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            $qb->andWhere($qb->expr()->isNull('st.assignee'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -139,7 +139,7 @@ class SingleTaskRepository extends EntityRepository
 | 
			
		||||
            $qb->andWhere($qb->expr()->eq('st.circle', ':scope'));
 | 
			
		||||
            $qb->setParameter('scope', $params['scope']);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (\array_key_exists('types', $params) && $params['types'] !== NULL) {
 | 
			
		||||
            if (count($params['types']) > 0) {
 | 
			
		||||
                $qb->andWhere($qb->expr()->in('st.type', ':types'));
 | 
			
		||||
@@ -154,7 +154,7 @@ class SingleTaskRepository extends EntityRepository
 | 
			
		||||
        if (\array_key_exists('is_closed', $params)) {
 | 
			
		||||
            $qb->andWhere($this->buildIsClosed($qb, !$params['is_closed']));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function addTypeFilter(QueryBuilder $qb, $params)
 | 
			
		||||
@@ -191,7 +191,7 @@ class SingleTaskRepository extends EntityRepository
 | 
			
		||||
                    ->add($this->buildNowIsAfterStartDate($qb, true))
 | 
			
		||||
                    ;
 | 
			
		||||
        }
 | 
			
		||||
        $qb->setParameter('now', new \DateTime('today'), Type::DATE);
 | 
			
		||||
        $qb->setParameter('now', new \DateTime('today'), Types::DATE_MUTABLE);
 | 
			
		||||
        $qb->andWhere($andWhere);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 | 
			
		||||
        {{ chill_pagination(paginator) }}
 | 
			
		||||
 | 
			
		||||
        {% if is_granted('CHILL_TASK_TASK_CREATE_FOR_COURSE', person) %}
 | 
			
		||||
        {% if is_granted('CHILL_TASK_TASK_CREATE_FOR_COURSE', accompanyingCourse) %}
 | 
			
		||||
            <ul class="record_actions sticky-form-buttons">
 | 
			
		||||
                <li>
 | 
			
		||||
                    {% if accompanyingCourse is not null %}
 | 
			
		||||
@@ -37,8 +37,10 @@
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block css %}
 | 
			
		||||
    {{ parent() }}
 | 
			
		||||
    {{ encore_entry_link_tags('page_task_list') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% block js %}
 | 
			
		||||
    {{ parent() }}
 | 
			
		||||
    {{ encore_entry_script_tags('page_task_list') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{% extends 'ChillMainBundle::layout.html.twig' %}
 | 
			
		||||
 | 
			
		||||
{% block title 'My tasks'|trans %}
 | 
			
		||||
{% block title 'Tasks'|trans %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <div class="col-md-10 col-xxl">
 | 
			
		||||
@@ -25,8 +25,10 @@
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block css %}
 | 
			
		||||
    {{ parent() }}
 | 
			
		||||
    {{ encore_entry_link_tags('page_task_list') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% block js %}
 | 
			
		||||
    {{ parent() }}
 | 
			
		||||
    {{ encore_entry_script_tags('page_task_list') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
{% extends 'ChillMainBundle::layout.html.twig' %}
 | 
			
		||||
 | 
			
		||||
{% block title 'My tasks'|trans %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <div class="col-md-10 col-xxl">
 | 
			
		||||
 | 
			
		||||
        <h1>{{ block('title') }}</h1>
 | 
			
		||||
 | 
			
		||||
        {{ filter_order|chill_render_filter_order_helper }}
 | 
			
		||||
 | 
			
		||||
        {% if tasks|length == 0 %}
 | 
			
		||||
            <p class="chill-no-data-statement">{{ 'Any tasks'|trans }}</p>
 | 
			
		||||
        {% else %}
 | 
			
		||||
            <div class="flex-table chill-task-list">
 | 
			
		||||
                {% for task in tasks %}
 | 
			
		||||
                    {% include 'ChillTaskBundle:SingleTask/List:index_item.html.twig' with { 'showContext' : true} %}
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        {{ chill_pagination(paginator) }}
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block css %}
 | 
			
		||||
    {{ parent() }}
 | 
			
		||||
    {{ encore_entry_link_tags('page_task_list') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% block js %}
 | 
			
		||||
    {{ parent() }}
 | 
			
		||||
    {{ encore_entry_script_tags('page_task_list') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@@ -19,8 +19,7 @@ final class Version20190307111314 extends AbstractMigration
 | 
			
		||||
 | 
			
		||||
        $this->addSql('CREATE SEQUENCE chill_third_party_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
 | 
			
		||||
        $this->addSql('CREATE TABLE chill_third_party (id INT NOT NULL, name VARCHAR(255) NOT NULL, telephone VARCHAR(64) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, comment TEXT DEFAULT NULL, type JSON DEFAULT NULL, PRIMARY KEY(id))');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_third_party.type IS \'(DC2Type:json_array)\'');
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema): void
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user