diff --git a/composer.json b/composer.json index fb0d2dcd1..b5af76421 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "knplabs/knp-time-bundle": "^1.12", "symfony/intl": "4.*", "symfony/swiftmailer-bundle": "^3.5", - "league/csv": "^9.6", + "league/csv": "^9.7.1", "phpoffice/phpspreadsheet": "^1.16", "symfony/browser-kit": "^5.2", "symfony/css-selector": "^5.2", diff --git a/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivityNotifications.php b/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivityNotifications.php index 7835a0db4..9ef67a7ac 100644 --- a/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivityNotifications.php +++ b/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivityNotifications.php @@ -22,6 +22,7 @@ class LoadActivityNotifications extends AbstractFixture implements DependentFixt 'entityRef' => 'activity_gerard depardieu', 'sender' => 'center a_social', 'addressees' => [ + 'center a_social', 'center a_administrative', 'center a_direction', 'multi_center' diff --git a/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php b/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php new file mode 100644 index 000000000..1836f599b --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php @@ -0,0 +1,24 @@ +getRelatedEntityClass() == Activity::class; + } + + public function getTemplate() + { + return '@ChillActivity/Activity/showInNotification.html.twig'; + } + + public function getTemplateData(Notification $notification) + { + return ['notification' => $notification]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/showInNotification.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/showInNotification.html.twig new file mode 100644 index 000000000..5128e9a64 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/showInNotification.html.twig @@ -0,0 +1,4 @@ + +{{ dump(notification) }} + +Go to Activity diff --git a/src/Bundle/ChillActivityBundle/config/services.yaml b/src/Bundle/ChillActivityBundle/config/services.yaml index 411ad1b5a..86168101a 100644 --- a/src/Bundle/ChillActivityBundle/config/services.yaml +++ b/src/Bundle/ChillActivityBundle/config/services.yaml @@ -1,4 +1,4 @@ -services: +services: chill.activity.security.authorization.activity_voter: class: Chill\ActivityBundle\Security\Authorization\ActivityVoter arguments: @@ -6,7 +6,7 @@ services: tags: - { name: security.voter } - { name: chill.role } - + chill.activity.security.authorization.activity_stats_voter: class: Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter arguments: @@ -14,8 +14,8 @@ services: tags: - { name: security.voter } - { name: chill.role } - - + + chill.activity.timeline: class: Chill\ActivityBundle\Timeline\TimelineActivityProvider arguments: @@ -33,3 +33,8 @@ services: autoconfigure: true resource: '../Menu/' tags: ['chill.menu_builder'] + + Chill\ActivityBundle\Notification\: + autowire: true + autoconfigure: true + resource: '../Notification' diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php index 25b7e5860..ca28b1d14 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php @@ -1141,7 +1141,7 @@ class CRUDController extends AbstractController */ protected function getPaginatorFactory(): PaginatorFactory { - return $this->container->get('chill_main.paginator_factory'); + return $this->container->get(PaginatorFactory::class); } /** diff --git a/src/Bundle/ChillMainBundle/Controller/NotificationController.php b/src/Bundle/ChillMainBundle/Controller/NotificationController.php new file mode 100644 index 000000000..c76e19d9e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/NotificationController.php @@ -0,0 +1,59 @@ +security = $security; + } + + + /** + * @Route("/show", name="chill_main_notification_show") + */ + public function showAction( + NotificationRepository $notificationRepository, NotificationRenderer $notificationRenderer, + PaginatorFactory $paginatorFactory) + { + $currentUser = $this->security->getUser(); + + $notificationsNbr = $notificationRepository->countAllForAttendee(($currentUser)); + $paginator = $paginatorFactory->create($notificationsNbr); + + $notifications = $notificationRepository->findAllForAttendee( + $currentUser, + $limit=$paginator->getItemsPerPage(), + $offset= $paginator->getCurrentPage()->getFirstItemNumber()); + + $templateData = array(); + foreach ($notifications as $notification) { + $data = [ + 'template' => $notificationRenderer->getTemplate($notification), + 'template_data' => $notificationRenderer->getTemplateData($notification), + 'notification' => $notification + ]; + $templateData[] = $data; + } + + return $this->render('@ChillMain/Notification/show.html.twig', [ + 'datas' => $templateData, + 'notifications' => $notifications, + 'paginator' => $paginator, + ]); + } +} diff --git a/src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php b/src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php new file mode 100644 index 000000000..d6f383b73 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php @@ -0,0 +1,44 @@ +renderers[] = $accompanyingPeriodNotificationRenderer; + $this->renderers[] = $activityNotificationRenderer; + } + + private function getRenderer(Notification $notification) + { + foreach ($this->renderers as $renderer) { + if($renderer->supports($notification)) { + return $renderer; + } + } + + throw new \Exception('No renderer for '. $notification); + } + + public function getTemplate(Notification $notification) + { + return $this->getRenderer($notification)->getTemplate(); + } + + public function getTemplateData(Notification $notification) + { + return $this->getRenderer($notification)->getTemplateData($notification); + } +} diff --git a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php index 844459f8b..5ffda75dc 100644 --- a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php @@ -23,6 +23,8 @@ use Chill\MainBundle\Entity\Notification; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ObjectRepository; +use Chill\MainBundle\Entity\User; +use Doctrine\ORM\Query; final class NotificationRepository implements ObjectRepository { @@ -59,8 +61,54 @@ final class NotificationRepository implements ObjectRepository return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } + private function queryAllForAttendee(User $addressee, bool $countQuery=False): Query + { + $qb = $this->repository->createQueryBuilder('n'); + + $select = 'n'; + if($countQuery) { + $select = 'count(n)'; + } + + $qb + ->select($select) + ->join('n.addressees', 'a') + ->where('a = :addressee') + ->setParameter('addressee', $addressee); + + return $qb->getQuery(); + } + + /** + * @return int + */ + public function countAllForAttendee(User $addressee): int // TODO passer à attendees avec S + { + $query = $this->queryAllForAttendee($addressee, $countQuery=True); + + return $query->getSingleScalarResult(); + } + + + /** + * @return Notification[] + */ + public function findAllForAttendee(User $addressee, $limit = null, $offset = null): array // TODO passer à attendees avec S + { + $query = $this->queryAllForAttendee($addressee); + + if($limit) { + $query = $query->setMaxResults($limit); + } + + if($offset) { + $query = $query->setFirstResult($offset); + } + + return $query->getResult(); + } + public function getClassName() { return Notification::class; } - } diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig new file mode 100644 index 000000000..34aeee049 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig @@ -0,0 +1,42 @@ +{% extends "@ChillMain/layout.html.twig" %} + +{% block content %} +
+
+

{{ "Notifications list" | trans }}

+ + + {%for data in datas %} + {% set notification = data.notification %} + +
+
{{ 'Message'|trans }}
+
{{ notification.message }}
+
+ +
+
{{ 'Date'|trans }}
+
{{ notification.date | date('long') }}
+
+ + +
+
{{ 'Sender'|trans }}
+
{{ notification.sender }}
+
+ +
+
{{ 'Addressees'|trans }}
+
{{ notification.addressees |join(', ') }}
+
+ +
+
{{ 'Entity'|trans }}
+
+ {% include data.template with data.template_data %} +
+
+ {% endfor %} +
+
+{% endblock content %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig b/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig index 23be470e0..3cc05e434 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig @@ -54,9 +54,10 @@
{# Flash messages ! #} - {% if app.session.flashbag.all()|length > 0 %} + {% if app.session.flashbag.keys()|length > 0 %}
+ {% for flashMessage in app.session.flashbag.get('success') %}
{{ flashMessage|raw }} diff --git a/src/Bundle/ChillMainBundle/Resources/views/layoutWithVerticalMenu.html.twig b/src/Bundle/ChillMainBundle/Resources/views/layoutWithVerticalMenu.html.twig index a5b43a9e1..be582ab3b 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/layoutWithVerticalMenu.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/layoutWithVerticalMenu.html.twig @@ -1,5 +1,5 @@ {# - * Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, + * Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, / * * This program is free software: you can redistribute it and/or modify @@ -28,7 +28,7 @@
{# Flash messages ! #} - {% if app.session.flashbag.all()|length > 0 %} + {% if app.session.flashbag.keys()|length > 0 %}
{% for flashMessage in app.session.flashbag.get('success') %} diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php index e480ab145..fcf82fca9 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php @@ -26,7 +26,7 @@ use Symfony\Component\HttpFoundation\Request; /** * This class provide a set of tests for exports. - * + * * The tests provided by this class will check basic things, like * the type of value are conform to the expected, etc. * @@ -34,45 +34,45 @@ use Symfony\Component\HttpFoundation\Request; */ abstract class AbstractExportTest extends WebTestCase { - + use PrepareClientTrait; - + /** * Create an instance of the report to test - * + * * @return \Chill\MainBundle\Export\ExportInterface an instance of the export to test */ public abstract function getExport(); - + /** * Create possible combinaison of data (produced by the form). - * + * * This data will be used to generate data providers using this data. - * + * * @return array an array of data. Example : `array( array(), array('fields' => array(1,2,3), ...)` where an empty array and `array(1,2,3)` are possible values */ public abstract function getFormData(); - + /** - * get the possible modifiers which could apply in combination to this + * get the possible modifiers which could apply in combination to this * export. - * . - * - * @return array of string[] an array which contains an array of possible modifiers. Example : `array( array('modifier_1', 'modifier_2'), array('modifier_1'), ...)` + * . + * + * @return array of string[] an array which contains an array of possible modifiers. Example : `array( array('modifier_1', 'modifier_2'), array('modifier_1'), ...)` */ abstract public function getModifiersCombination(); - + /** * Return an array usable as ACL - * + * * If this method is overridden, the returned result must be an array * with this form : - * + * * ``` * array( - * array( - * 'center' => //center instance + * array( + * 'center' => //center instance * 'circles' => array(// array of circles instances ) * ) * ); @@ -84,15 +84,15 @@ abstract class AbstractExportTest extends WebTestCase if (static::$kernel === null) { static::bootKernel(); } - + $em = static::$kernel->getContainer() ->get('doctrine.orm.entity_manager'); - + $centers = $em->getRepository('ChillMainBundle:Center') ->findAll(); $circles = $em->getRepository('ChillMainBundle:Scope') ->findAll(); - + if (count($centers) === 0) { throw new \RuntimeException("No center found. Did you forget to " . "run `doctrine:fixtures:load` command before ?"); @@ -101,7 +101,7 @@ abstract class AbstractExportTest extends WebTestCase throw new \RuntimeException("No circle found. Did you forget to " . "run `doctrine:fixtures:load` command before ?"); } - + return array([ 'center' => $centers[0], 'circles' => [ @@ -115,204 +115,203 @@ abstract class AbstractExportTest extends WebTestCase public function testGetType() { $export = $this->getExport(); - - $this->assertInternalType('string', $export->getType(), + + $this->assertInternalType('string', $export->getType(), "Assert that the `getType` method return a string"); $this->assertNotEmpty($export->getType(), "Assert that the `getType` method" . " does not return an empty string."); } - + /** * Test that the description is not empty */ public function testGetDescription() { $export = $this->getExport(); - - $this->assertInternalType('string', $export->getDescription(), + + $this->assertInternalType('string', $export->getDescription(), "Assert that the `getDescription` method return a string"); - $this->assertNotEmpty($export->getDescription(), + $this->assertNotEmpty($export->getDescription(), "Assert that the `getDescription` method does not return an empty " . "string."); } - + /** * create data for `ìnitiateQuery` method */ public function dataProviderInitiateQuery() { $acl = $this->getAcl(); - + foreach($this->getModifiersCombination() as $modifiers) { - + foreach($this->getFormData() as $data) { - + yield array($modifiers, $acl, $data); } } } - + public function dataProviderGetQueryKeys() { foreach($this->getFormData() as $data) { yield array($data); } } - + /** - * + * * test that the query returned is a QueryBuilder or a NativeQuery. - * + * * If the query is a QueryBuilder, test that select and from is not empty. - * - * If the query is a native sql, test the query is not empty (length is + * + * If the query is a native sql, test the query is not empty (length is * > 0). - * + * * @dataProvider dataProviderInitiateQuery */ public function testInitiateQuery($modifiers, $acl, $data) { - var_dump($data); $query = $this->getExport()->initiateQuery($modifiers, $acl, $data); - + $this->assertTrue($query instanceof QueryBuilder || $query instanceof NativeQuery, - sprintf("Assert that the returned query is an instance of %s or %s", + sprintf("Assert that the returned query is an instance of %s or %s", QueryBuilder::class, Query::class)); - + if ($query instanceof QueryBuilder) { - + $this->assertGreaterThanOrEqual(1, count($query->getDQLPart('select')), "assert there is at least one 'select' part"); - + $this->assertGreaterThanOrEqual(1, count($query->getDQLPart('from')), "assert there is at least one 'from' part"); - + } elseif ($query instanceof NativeQuery) { - $this->assertNotEmpty($query->getSQL(), + $this->assertNotEmpty($query->getSQL(), "check that the SQL query is not empty"); } } - + /** * Test that supportsModifier return : - * + * * - an array of string, if the query is a QueryBuilder ; * - nothing, if the query is a native SQL - * + * * @dataProvider dataProviderInitiateQuery */ public function testSupportsModifier($modifiers, $acl, $data) { $export = $this->getExport(); $query = $export->initiateQuery($modifiers, $acl, $data); - + if ($query instanceof QueryBuilder) { $this->assertContainsOnly('string', $export->supportsModifiers(), "Test that the `supportsModifiers` method returns only strings"); } elseif ($query instanceof NativeQuery) { - $this->assertTrue($export->supportsModifiers() === null || + $this->assertTrue($export->supportsModifiers() === null || count($export->supportsModifiers()) === 0, "Test that the `supportsModifier` methods returns null or an empty array"); } } - + /** * Test required role is an instance of Role */ public function testRequiredRole() { $role = $this->getExport()->requiredRole(); - - $this->assertInstanceOf(Role::class, $role, + + $this->assertInstanceOf(Role::class, $role, sprintf("test that the returned value of `requiredRole` is an instance " . "of %s", Role::class)); } - + /** * Test the formatters type are string */ public function testGetAllowedFormattersType() { $formattersTypes = $this->getExport()->getAllowedFormattersTypes(); - + $this->assertContainsOnly("string", $formattersTypes, "Test that the method `getAllowedFormattersTypes` returns an array of string"); } - + /** * Test that the query keys are strings - * + * * @param array $data * @dataProvider dataProviderGetQueryKeys */ public function testGetQueryKeys(array $data) { $queryKeys = $this->getExport()->getQueryKeys($data); - + $this->assertContainsOnly("string", $queryKeys, "test that the query keys returned by `getQueryKeys` are only strings"); $this->assertGreaterThanOrEqual(1, count($queryKeys), "test that there are at least one query key returned"); } - + /** - * - * Test that - * + * + * Test that + * * - the results have a correct form (are arrays or traversable) * - each key in a row are present in getQueryKeys ; * - each returned object of the `getLabels` method is callable * - each result can be converted to string using this callable * - each of this callable can provide a string for '_header' - * + * * @param string[] $modifiers * @param array $acl * @param array $data - * + * * @dataProvider dataProviderInitiateQuery */ public function testGetResultsAndLabels($modifiers, $acl, array $data) { // it is more convenient to group the `getResult` and `getLabels` test // due to the fact that testing both methods use the same tools. - + $queryKeys = $this->getExport()->getQueryKeys($data); $query = $this->getExport()->initiateQuery($modifiers, $acl, $data); - + // limit the result for the query for performance reason (only for QueryBuilder, // not possible in NativeQuery) if ($query instanceof QueryBuilder) { $query->setMaxResults(1); - } - + } + $results = $this->getExport()->getResult($query, $data); - - $this->assertInternalType('array', $results, + + $this->assertInternalType('array', $results, "assert that the returned result is an array"); - + if (count($results) === 0) { $this->markTestIncomplete("The result is empty. We cannot process tests " . "on results"); } - + // testing the result $result = $results[0]; - + $this->assertTrue( $result instanceof \Traversable || is_array($result), "test that each row in the result is traversable or an array"); - + foreach ($result as $key => $value) { $this->assertContains($key, $queryKeys, "test that each key is present in `getQueryKeys`"); - + $closure = $this->getExport()->getLabels($key, array($value), $data); - + $this->assertTrue(is_callable($closure, false), "test that the `getLabels` for key is a callable"); $this->assertTrue(is_string((string) call_user_func($closure, $value)), sprintf("test that the callable return by `getLabels` for key %s " . "is a string or an be converted to a string", $key)); - + $this->assertTrue( // conditions is_string((string) call_user_func($closure, '_header')) @@ -322,13 +321,13 @@ abstract class AbstractExportTest extends WebTestCase sprintf("Test that the callable return by `getLabels` for key %s " . "can provide an header", $key) ); - } - + } + } - + /** - * Test that the translated title of the export is present the list, + * Test that the translated title of the export is present the list, * and that the list of exports (under `/fr/exports/`) is still successfull */ public function testListExportPage() @@ -338,17 +337,17 @@ abstract class AbstractExportTest extends WebTestCase $export = $this->getExport(); $prophet= new \Prophecy\Prophet; $container = static::$kernel->getContainer(); - + // store the locale in a request $request = new Request(); $request->setLocale('fr'); $container->get('request_stack')->push($request); // translate the title $title = $container->get('translator')->trans($export->getTitle()); - + // performs the request to /fr/exports $crawler = $client->request('GET', '/fr/exports/'); - + // and finally make tests $this->assertTrue($client->getResponse()->isSuccessful(), "test that the response of /fr/exports/ is successful"); diff --git a/src/Bundle/ChillMainBundle/config/routes.yaml b/src/Bundle/ChillMainBundle/config/routes.yaml index 240a44590..697ec8ec0 100644 --- a/src/Bundle/ChillMainBundle/config/routes.yaml +++ b/src/Bundle/ChillMainBundle/config/routes.yaml @@ -34,6 +34,10 @@ chill_password_recover: resource: "@ChillMainBundle/config/routes/password_recover.yaml" prefix: "public/{_locale}/password" +chill_main_notification: + resource: "@ChillMainBundle/config/routes/notification.yaml" + prefix: "{_locale}/notification" + chill_crud: resource: "@ChillMainBundle" type: CRUD diff --git a/src/Bundle/ChillMainBundle/config/routes/notification.yaml b/src/Bundle/ChillMainBundle/config/routes/notification.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/src/Bundle/ChillMainBundle/config/services/controller.yaml b/src/Bundle/ChillMainBundle/config/services/controller.yaml index 6021e3d72..11a9bc274 100644 --- a/src/Bundle/ChillMainBundle/config/services/controller.yaml +++ b/src/Bundle/ChillMainBundle/config/services/controller.yaml @@ -33,3 +33,8 @@ services: $logger: '@Psr\Log\LoggerInterface' $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface' tags: ['controller.service_arguments'] + + Chill\MainBundle\Controller\NotificationController: + arguments: + $security: '@Symfony\Component\Security\Core\Security' + tags: ['controller.service_arguments'] diff --git a/src/Bundle/ChillMainBundle/config/services/notification.yaml b/src/Bundle/ChillMainBundle/config/services/notification.yaml index fa4fa15c6..c8d970c5d 100644 --- a/src/Bundle/ChillMainBundle/config/services/notification.yaml +++ b/src/Bundle/ChillMainBundle/config/services/notification.yaml @@ -4,7 +4,11 @@ services: $logger: '@Psr\Log\LoggerInterface' $twig: '@Twig\Environment' $mailer: '@swiftmailer.mailer.default' - # $mailerTransporter: '@swiftmailer.transport' + # $mailerTransporter: '@swiftmailer.transport' $router: '@Symfony\Component\Routing\RouterInterface' $translator: '@Symfony\Component\Translation\TranslatorInterface' $routeParameters: '%chill_main.notifications%' + + Chill\MainBundle\Notification\NotificationRenderer: + autoconfigure: true + autowire: true diff --git a/src/Bundle/ChillMainBundle/config/services/pagination.yaml b/src/Bundle/ChillMainBundle/config/services/pagination.yaml index f6282a39f..e85352728 100644 --- a/src/Bundle/ChillMainBundle/config/services/pagination.yaml +++ b/src/Bundle/ChillMainBundle/config/services/pagination.yaml @@ -2,13 +2,15 @@ services: chill_main.paginator_factory: class: Chill\MainBundle\Pagination\PaginatorFactory public: true + autowire: true + autoconfigure: true arguments: - "@request_stack" - "@router" - "%chill_main.pagination.item_per_page%" Chill\MainBundle\Pagination\PaginatorFactory: '@chill_main.paginator_factory' - + chill_main.paginator.twig_extensions: class: Chill\MainBundle\Pagination\ChillPaginationTwig tags: diff --git a/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml new file mode 100644 index 000000000..d5dedbb9f --- /dev/null +++ b/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml @@ -0,0 +1,6 @@ +years_old: >- + {age, plural, + one {# an} + many {# ans} + other {# ans} + } diff --git a/src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php b/src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php index 4f2b2098a..b68997ed6 100644 --- a/src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php +++ b/src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php @@ -1,25 +1,7 @@ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ namespace Chill\PersonBundle\Command; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -28,39 +10,28 @@ use Doctrine\ORM\EntityManagerInterface; use Chill\PersonBundle\Entity\Person; use Symfony\Component\Console\Exception\RuntimeException; use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Command\Command; -class ChillPersonMoveCommand extends ContainerAwareCommand +final class ChillPersonMoveCommand extends Command { - /** - * - * @var PersonMove - */ - protected $mover; - - /** - * - * @var EntityManagerInterface - */ - protected $em; - - /** - * - * @var LoggerInterface - */ - protected $chillLogger; - + private PersonMove $mover; + + private EntityManagerInterface $em; + + private LoggerInterface $chillLogger; + public function __construct( - PersonMove $mover, + PersonMove $mover, EntityManagerInterface $em, LoggerInterface $chillLogger ) { parent::__construct('chill:person:move'); - + $this->mover = $mover; $this->em = $em; $this->chillLogger = $chillLogger; } - + protected function configure() { $this @@ -73,14 +44,14 @@ class ChillPersonMoveCommand extends ContainerAwareCommand ->addOption('delete-entity', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, "entity to delete", []) ; } - + protected function interact(InputInterface $input, OutputInterface $output) { if (FALSE === $input->hasOption('dump-sql') && FALSE === $input->hasOption('force')) { $msg = "You must use \"--dump-sql\" or \"--force\""; throw new RuntimeException($msg); } - + foreach (["from", "to"] as $name) { if (empty($input->getOption($name))) { throw new RuntimeException("You must set a \"$name\" option"); @@ -90,7 +61,7 @@ class ChillPersonMoveCommand extends ContainerAwareCommand throw new RuntimeException("The id in \"$name\" field does not contains " . "only digits: $id"); } - } + } } protected function execute(InputInterface $input, OutputInterface $output) @@ -99,16 +70,16 @@ class ChillPersonMoveCommand extends ContainerAwareCommand $from = $repository->find($input->getOption('from')); $to = $repository->find($input->getOption('to')); $deleteEntities = $input->getOption('delete-entity'); - + if ($from === NULL) { throw new RuntimeException(sprintf("Person \"from\" with id %d not found", $input->getOption('from'))); } if ($to === NULL) { throw new RuntimeException(sprintf("Person \"to\" with id %d not found", $input->getOption('to'))); } - + $sqls = $this->mover->getSQL($from, $to, $deleteEntities); - + if ($input->getOption('dump-sql')) { foreach($sqls as $sql) { $output->writeln($sql); @@ -125,25 +96,25 @@ class ChillPersonMoveCommand extends ContainerAwareCommand $connection->executeQuery($sql); } $connection->commit(); - + $this->chillLogger->notice("Move a person from command line succeeded", $ctxt); } } - + protected function buildLoggingContext(Person $from, Person $to, $deleteEntities, $sqls) { $ctxt = [ 'from' => $from->getId(), 'to' => $to->getId() ]; - + foreach ($deleteEntities as $key => $de) { $ctxt['delete_entity_'.$key] = $de; } foreach ($sqls as $key => $sql) { $ctxt['sql_'.$key] = $sql; } - + return $ctxt; } diff --git a/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php b/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php index d5e4c597c..35f596295 100644 --- a/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php +++ b/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php @@ -1,22 +1,5 @@ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - namespace Chill\PersonBundle\Command; use Chill\MainBundle\Templating\TranslatableStringHelper; @@ -40,65 +23,37 @@ use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\Form\FormFactory; +use Symfony\Component\Form\FormFactoryInterface; -/** - * Class ImportPeopleFromCSVCommand - * - * @package Chill\PersonBundle\Command - * @author Julien Fastré - */ -class ImportPeopleFromCSVCommand extends Command +final class ImportPeopleFromCSVCommand extends Command { + private InputInterface $input; + + private OutputInterface $output; + + private LoggerInterface $logger; + + private TranslatableStringHelper $helper; + + private EntityManagerInterface $em; + + private EventDispatcherInterface $eventDispatcher; + /** - * @var InputInterface + * The line currently read */ - protected $input; - + private int $line; + /** - * @var OutputInterface + * Where key are column names, and value the custom field slug */ - protected $output; - - /** - * @var \Psr\Log\LoggerInterface - */ - protected $logger; - - /** - * @var \Chill\MainBundle\Templating\TranslatableStringHelper - */ - protected $helper; - - /** - * @var \Doctrine\Persistence\ObjectManager - */ - protected $em; - - /** - * @var EventDispatcherInterface - */ - protected $eventDispatcher; - - /** - * the line currently read - * - * @var int - */ - protected $line; - - /** - * @var array where key are column names, and value the custom field slug - */ - protected $customFieldMapping = array(); - - /** - * @var CustomFieldProvider - */ - protected $customFieldProvider; - + private array $customFieldMapping = []; + + private CustomFieldProvider $customFieldProvider; + /** * Contains an array of information searched in the file. - * + * * position 0: the information key (which will be used in this process) * position 1: the helper * position 2: the default value @@ -121,19 +76,16 @@ class ImportPeopleFromCSVCommand extends Command ['locality', 'The column header for locality', 'locality'], ['center', 'The column header for center', 'center'] ); - + /** * Different possible format to interpret a date * * @var string */ protected static $defaultDateInterpreter = "%d/%m/%Y|%e/%m/%y|%d/%m/%Y|%e/%m/%Y"; - - /** - * @var FormFactory - */ - protected $formFactory; - + + private FormFactoryInterface $formFactory; + /** * ImportPeopleFromCSVCommand constructor. * @@ -150,7 +102,7 @@ class ImportPeopleFromCSVCommand extends Command EntityManagerInterface $em, CustomFieldProvider $customFieldProvider, EventDispatcherInterface $eventDispatcher, - FormFactory $formFactory + FormFactoryInterface $formFactory ) { $this->logger = $logger; $this->helper = $helper; @@ -158,10 +110,10 @@ class ImportPeopleFromCSVCommand extends Command $this->customFieldProvider = $customFieldProvider; $this->eventDispatcher = $eventDispatcher; $this->formFactory = $formFactory; - + parent::__construct('chill:person:import'); } - + /** * */ @@ -171,10 +123,10 @@ class ImportPeopleFromCSVCommand extends Command ->addArgument('csv_file', InputArgument::REQUIRED, "The CSV file to import") ->setDescription("Import people from a csv file") ->setHelp(<<addArgument('locale', InputArgument::REQUIRED, + ->addArgument('locale', InputArgument::REQUIRED, "The locale to use in displaying translatable strings from entities") ->addOption( - 'force-center', - null, + 'force-center', + null, InputOption::VALUE_REQUIRED, "The id of the center" ) @@ -197,10 +149,10 @@ EOF "Persist people in the database (default is not to persist people)" ) ->addOption( - 'delimiter', - 'd', - InputOption::VALUE_OPTIONAL, - "The delimiter character of the csv file", + 'delimiter', + 'd', + InputOption::VALUE_OPTIONAL, + "The delimiter character of the csv file", ",") ->addOption( 'enclosure', @@ -236,33 +188,33 @@ EOF "The path of the file to load the matching between label in CSV and answers" ) ; - + // mapping columns foreach (self::$mapping as $m) { $this->addOptionShortcut($m[0], $m[1], $m[2]); } - + // other information $this->addOptionShortcut('birthdate_format', 'Format preference for ' - . 'birthdate. See help for date formats preferences.', + . 'birthdate. See help for date formats preferences.', self::$defaultDateInterpreter); $this->addOptionShortcut('opening_date_format', 'Format preference for ' . 'opening date. See help for date formats preferences.', self::$defaultDateInterpreter); $this->addOptionShortcut('closing_date_format', 'Format preference for ' - . 'closing date. See help for date formats preferences.', + . 'closing date. See help for date formats preferences.', self::$defaultDateInterpreter); - + // mapping column to custom fields - $this->addOption('custom-field', NULL, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + $this->addOption('custom-field', NULL, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, "Mapping a column to a custom fields key. Example: 1=cf_slug"); $this->addOption('skip-interactive-field-mapping', null, InputOption::VALUE_NONE, "Do not ask for interactive mapping"); } - + /** * This function is a shortcut to addOption. - * + * * @param string $name * @param string $description * @param string $default @@ -271,10 +223,10 @@ EOF protected function addOptionShortcut($name, $description, $default) { $this->addOption($name, null, InputOption::VALUE_OPTIONAL, $description, $default); - + return $this; } - + /** * @param InputInterface $input * @param OutputInterface $output @@ -285,17 +237,17 @@ EOF $this->input = $input; $this->output = $output; $this->logger = new ConsoleLogger($output); - + $csv = $this->openCSV(); - + // getting the first row if (($row = fgetcsv( - $csv, - $input->getOption('length'), - $input->getOption('delimiter'), - $input->getOption('enclosure'), + $csv, + $input->getOption('length'), + $input->getOption('delimiter'), + $input->getOption('enclosure'), $input->getOption('escape'))) !== false) { - + try { $this->matchColumnToCustomField($row); } finally { @@ -303,33 +255,33 @@ EOF fclose($csv); } } - + // load the matching between csv and label $this->loadAnswerMatching(); } - + /** * @param $row */ protected function matchColumnToCustomField($row) { - + $cfMappingsOptions = $this->input->getOption('custom-field'); /* @var $em \Doctrine\Persistence\ObjectManager */ $em = $this->em; - + foreach($cfMappingsOptions as $cfMappingStringOption) { list($rowNumber, $cfSlug) = preg_split('|=|', $cfMappingStringOption); - + // check that the column exists, getting the column name $column = $row[$rowNumber]; - + if (empty($column)) { $message = "The column with row $rowNumber is empty."; $this->logger->error($message); throw new \RuntimeException($message); } - + // check a custom field exists try { $customField = $em->createQuery("SELECT cf " @@ -357,15 +309,15 @@ EOF . "Stopping this command."); throw new \RuntimeException("The custom field with slug $cfSlug could not be found. " . "Stopping this command."); - } - + } + $this->logger->notice(sprintf("Matched custom field %s (question : '%s') on column %d (displayed in the file as '%s')", $customField->getSlug(), $this->helper->localize($customField->getName()), $rowNumber, $column)); - + $this->customFieldMapping[$rowNumber] = $customField; } } - + /** * Load the mapping between answer in CSV and value in choices from a json file */ @@ -383,7 +335,7 @@ EOF } } } - + /** * */ @@ -392,14 +344,14 @@ EOF if ($this->input->hasOption('dump-choice-matching') && !empty($this->input->getOption('dump-choice-matching'))) { $this->logger->debug("Dump the matching between answer and choices"); $str = json_encode($this->cacheAnswersMapping, JSON_PRETTY_PRINT); - + $fs = new Filesystem(); $filename = $this->input->getOption('dump-choice-matching'); - + $fs->dumpFile($filename, $str); } } - + /** * @param InputInterface $input * @param OutputInterface $output @@ -410,22 +362,22 @@ EOF { $this->input = $input; $this->output = $output; - + $this->logger->debug("Setting locale to ".$input->getArgument('locale')); setlocale(LC_TIME, $input->getArgument('locale')); - + // opening csv as resource $csv = $this->openCSV(); - + $num = 0; $line = $this->line = 1; - + try { while (($row = fgetcsv( - $csv, - $input->getOption('length'), - $input->getOption('delimiter'), - $input->getOption('enclosure'), + $csv, + $input->getOption('length'), + $input->getOption('delimiter'), + $input->getOption('enclosure'), $input->getOption('escape'))) !== false) { $this->logger->debug("Processing line ".$this->line); if ($line === 1 ) { @@ -434,11 +386,11 @@ EOF $headers = $this->processingHeaders($row); } else { $person = $this->createPerson($row, $headers); - + if (count($this->customFieldMapping) > 0) { $this->processingCustomFields($person, $row); } - + $event = new Event(); $event->person = $person; $event->rawHeaders = $rawHeaders; @@ -449,21 +401,21 @@ EOF $event->input = $this->input; $event->output = $this->output; $event->helperSet = $this->getHelperSet(); - + $this->eventDispatcher->dispatch('chill_person.person_import', $event); - - if ($this->input->getOption('force') === TRUE + + if ($this->input->getOption('force') === TRUE && $event->skipPerson === false) { $this->em->persist($person); } - + $num ++; } $line ++; $this->line++; } - + if ($this->input->getOption('force') === true) { $this->logger->debug('persisting entitites'); $this->em->flush(); @@ -475,9 +427,9 @@ EOF $this->dumpAnswerMatching(); } } - + /** - * + * * @return resource * @throws \RuntimeException */ @@ -485,23 +437,23 @@ EOF { $fs = new Filesystem(); $filename = $this->input->getArgument('csv_file'); - + if (!$fs->exists($filename)) { throw new \RuntimeException("The file does not exists or you do not " . "have the right to read it."); } - + $resource = fopen($filename, 'r'); - + if ($resource == FALSE) { throw new \RuntimeException("The file '$filename' could not be opened."); } - + return $resource; } - + /** - * + * * @param type $firstRow * @return array where keys are column number, and value is information mapped */ @@ -510,11 +462,11 @@ EOF $availableOptions = array_map(function($m) { return $m[0]; }, self::$mapping); $matchedColumnHeaders = array(); $headers = array(); - + foreach($availableOptions as $option) { $matchedColumnHeaders[$option] = $this->input->getOption($option); } - + foreach($firstRow as $key => $content) { $content = trim($content); if (in_array($content, $matchedColumnHeaders)) { @@ -524,11 +476,11 @@ EOF } else { $this->logger->notice("Column with content '$content' is ignored"); } - } - + } + return $headers; } - + /** * * @param array $row @@ -541,21 +493,21 @@ EOF // trying to get the opening date $openingDateString = trim($row[array_search('opening_date', $headers)]); $openingDate = $this->processDate($openingDateString, $this->input->getOption('opening_date_format')); - + $person = $openingDate instanceof \DateTime ? new Person($openingDate) : new Person(); // add the center $center = $this->getCenter($row, $headers); - + if ($center === null) { throw new \Exception("center not found"); } - + $person->setCenter($center); - + foreach($headers as $column => $info) { - + $value = trim($row[$column]); - + switch($info) { case 'firstname': $person->setFirstName($value); @@ -582,12 +534,12 @@ EOF $person->setEmail($value); break; case 'phonenumber': - $person->setPhonenumber($value); + $person->setPhonenumber($value); break; case 'mobilenumber': $person->setMobilenumber($value); break; - + // we just keep the column number for those data case 'postalcode': $postalCodeValue = $value; @@ -600,33 +552,33 @@ EOF break; } } - + // handle address if (\in_array('postalcode', $headers)) { - + if (! empty($postalCodeValue)) { - + $address = new Address(); $postalCode = $this->guessPostalCode($postalCodeValue, $localityValue ?? ''); - + if ($postalCode === null) { throw new \Exception("The locality is not found"); } - + $address->setPostcode($postalCode); - + if (\in_array('street1', $headers)) { $address->setStreetAddress1($street1Value); } $address->setValidFrom(new \DateTime('today')); - + $person->addAddress($address); } } - + return $person; } - + /** * @param $row * @param $headers @@ -640,7 +592,7 @@ EOF } else { $columnCenter = \array_search('center', $headers); $centerName = \trim($row[$columnCenter]); - + try { return $this->em->createQuery('SELECT c FROM ChillMainBundle:Center c ' . 'WHERE c.name = :center_name') @@ -654,7 +606,7 @@ EOF } } } - + /** * @param $centerName * @return Center|mixed|null|object @@ -664,68 +616,68 @@ EOF if (!\array_key_exists('_center_picked', $this->cacheAnswersMapping)) { $this->cacheAnswersMapping['_center_picked'] = []; } - + if (\array_key_exists($centerName, $this->cacheAnswersMapping['_center_picked'])) { $id = $this->cacheAnswersMapping['_center_picked'][$centerName]; - + return $this->em->getRepository(Center::class) ->find($id); } - + $centers = $this->em->createQuery("SELECT c FROM ChillMainBundle:Center c " . "ORDER BY SIMILARITY(c.name, :center_name) DESC") ->setParameter('center_name', $centerName) ->setMaxResults(10) ->getResult() ; - + if (count($centers) > 1) { if (\strtolower($centers[0]->getName()) === \strtolower($centerName)) { return $centers[0]; } } - + $centersByName = []; - $names = \array_map(function(Center $c) use (&$centersByName) { + $names = \array_map(function(Center $c) use (&$centersByName) { $n = $c->getName(); $centersByName[$n] = $c; return $n; - + }, $centers); $names[] = "none of them"; - + $helper = $this->getHelper('question'); - $question = new ChoiceQuestion(sprintf("Which center match the name \"%s\" ? (default to \"%s\")", $centerName, $names[0]), + $question = new ChoiceQuestion(sprintf("Which center match the name \"%s\" ? (default to \"%s\")", $centerName, $names[0]), $names, 0); - + $answer = $helper->ask($this->input, $this->output, $question); - + if ($answer === 'none of them') { $questionCreate = new ConfirmationQuestion("Would you like to create it ?", false); $create = $helper->ask($this->input, $this->output, $questionCreate); - + if ($create) { $center = (new Center()) ->setName($centerName) ; - + if ($this->input->getOption('force') === TRUE) { $this->em->persist($center); $this->em->flush(); } - + return $center; } } - + $center = $centersByName[$answer]; - + $this->cacheAnswersMapping['_center_picked'][$centerName] = $center->getId(); - + return $center; } - + /** * @param $postalCode * @param $locality @@ -736,15 +688,15 @@ EOF if (!\array_key_exists('_postal_code_picked', $this->cacheAnswersMapping)) { $this->cacheAnswersMapping['_postal_code_picked'] = []; } - + if (\array_key_exists($postalCode, $this->cacheAnswersMapping['_postal_code_picked'])) { if (\array_key_exists($locality, $this->cacheAnswersMapping['_postal_code_picked'][$postalCode])) { $id = $this->cacheAnswersMapping['_postal_code_picked'][$postalCode][$locality]; - + return $this->em->getRepository(PostalCode::class)->find($id); } } - + $postalCodes = $this->em->createQuery("SELECT pc FROM ".PostalCode::class." pc " . "WHERE pc.code = :postal_code " . "ORDER BY SIMILARITY(pc.name, :locality) DESC " @@ -754,48 +706,48 @@ EOF ->setParameter('locality', $locality) ->getResult() ; - + if (count($postalCodes) >= 1) { - if ($postalCodes[0]->getCode() === $postalCode + if ($postalCodes[0]->getCode() === $postalCode && $postalCodes[0]->getName() === $locality) { return $postalCodes[0]; } } - + if (count($postalCodes) === 0) { return null; } - + $postalCodeByName = []; $names = \array_map(function(PostalCode $pc) use (&$postalCodeByName) { $n = $pc->getName(); $postalCodeByName[$n] = $pc; - + return $n; }, $postalCodes); $names[] = 'none of them'; - + $helper = $this->getHelper('question'); $question = new ChoiceQuestion(sprintf("Which postal code match the " - . "name \"%s\" with postal code \"%s\" ? (default to \"%s\")", - $locality, $postalCode, $names[0]), + . "name \"%s\" with postal code \"%s\" ? (default to \"%s\")", + $locality, $postalCode, $names[0]), $names, 0); - + $answer = $helper->ask($this->input, $this->output, $question); - + if ($answer === 'none of them') { return null; } - + $pc = $postalCodeByName[$answer]; - - $this->cacheAnswersMapping['_postal_code_picked'][$postalCode][$locality] = + + $this->cacheAnswersMapping['_postal_code_picked'][$postalCode][$locality] = $pc->getId(); - + return $pc; } - + /** * @param Person $person * @param $value @@ -804,29 +756,29 @@ EOF protected function processBirthdate(Person $person, $value) { if (empty($value)) { return; } - + $date = $this->processDate($value, $this->input->getOption('birthdate_format')); - + if ($date instanceof \DateTime) { // we correct birthdate if the date is in the future // the most common error is to set date 100 years to late (ex. 2063 instead of 1963) if ($date > new \DateTime('yesterday')) { $date = $date->sub(new \DateInterval('P100Y')); } - + $person->setBirthdate($date); - + return; } - + // if we arrive here, we could not process the date $this->logger->warning(sprintf( "Line %d : the birthdate could not be interpreted. Was %s.", $this->line, $value)); - + } - + /** * @param Person $person * @param $value @@ -835,39 +787,39 @@ EOF protected function processClosingDate(Person $person, $value) { if (empty($value)) { return; } - + // we skip if the opening date is now (or after yesterday) /* @var $period \Chill\PersonBundle\Entity\AccompanyingPeriod */ $period = $person->getCurrentAccompanyingPeriod(); - + if ($period->getOpeningDate() > new \DateTime('yesterday')) { $this->logger->debug(sprintf("skipping a closing date because opening date is after yesterday (%s)", $period->getOpeningDate()->format('Y-m-d'))); return; } - - + + $date = $this->processDate($value, $this->input->getOption('closing_date_format')); - + if ($date instanceof \DateTime) { // we correct birthdate if the date is in the future // the most common error is to set date 100 years to late (ex. 2063 instead of 1963) if ($date > new \DateTime('yesterday')) { $date = $date->sub(new \DateInterval('P100Y')); } - + $period->setClosingDate($date); $person->close(); return; } - + // if we arrive here, we could not process the date $this->logger->warning(sprintf( "Line %d : the closing date could not be interpreted. Was %s.", $this->line, $value)); } - + /** * @param Person $person * @param $row @@ -875,27 +827,27 @@ EOF */ protected function processingCustomFields(Person $person, $row) { - + /* @var $cfProvider \Chill\CustomFieldsBundle\Service\CustomFieldProvider */ $cfProvider = $this->customFieldProvider; $cfData = array(); - + /* @var $$customField \Chill\CustomFieldsBundle\Entity\CustomField */ foreach($this->customFieldMapping as $rowNumber => $customField) { $builder = $this->formFactory->createBuilder(); $cfProvider->getCustomFieldByType($customField->getType()) ->buildForm($builder, $customField); $form = $builder->getForm(); - + // get the type of the form $type = get_class($form->get($customField->getSlug()) ->getConfig()->getType()->getInnerType()); - $this->logger->debug(sprintf("Processing a form of type %s", + $this->logger->debug(sprintf("Processing a form of type %s", $type)); switch ($type) { case \Symfony\Component\Form\Extension\Core\Type\TextType::class: - $cfData[$customField->getSlug()] = + $cfData[$customField->getSlug()] = $this->processTextType($row[$rowNumber], $form, $customField); break; case \Symfony\Component\Form\Extension\Core\Type\ChoiceType::class: @@ -903,12 +855,12 @@ EOF $cfData[$customField->getSlug()] = $this->processChoiceType($row[$rowNumber], $form, $customField); } - + } - + $person->setCFData($cfData); } - + /** * Process a text type on a custom field * @@ -917,24 +869,24 @@ EOF * @return type */ protected function processTextType( - $value, - \Symfony\Component\Form\FormInterface $form, + $value, + \Symfony\Component\Form\FormInterface $form, \Chill\CustomFieldsBundle\Entity\CustomField $cf ) { $form->submit(array($cf->getSlug() => $value)); - + $value = $form->getData()[$cf->getSlug()]; - + $this->logger->debug(sprintf("Found value : %s for custom field with question " . "'%s'", $value, $this->helper->localize($cf->getName()))); - + return $value; } - + protected $cacheAnswersMapping = array(); - - + + /** * Process a custom field choice. * @@ -949,14 +901,14 @@ EOF */ protected function processChoiceType( $value, - \Symfony\Component\Form\FormInterface $form, + \Symfony\Component\Form\FormInterface $form, \Chill\CustomFieldsBundle\Entity\CustomField $cf ) { // getting the possible answer and their value : $view = $form->get($cf->getSlug())->createView(); $answers = $this->collectChoicesAnswers($view->vars['choices']); - + // if we do not have any answer on the question, throw an error. if (count($answers) === 0) { $message = sprintf( @@ -964,34 +916,34 @@ EOF $this->helper->localize($cf->getName()), $cf->getSlug() ); - + $this->logger->error($message, array( 'method' => __METHOD__, 'slug' => $cf->getSlug(), 'question' => $this->helper->localize($cf->getName()) )); - + throw new \RuntimeException($message); } - + if ($view->vars['required'] === false) { $answers[null] = '** no answer'; } - + // the answer does not exists in cache. Try to find it, or asks the user if (!isset($this->cacheAnswersMapping[$cf->getSlug()][$value])) { - + // try to find the answer (with array_keys and a search value $values = array_keys( - array_map(function($label) { return trim(strtolower($label)); }, $answers), + array_map(function($label) { return trim(strtolower($label)); }, $answers), trim(strtolower($value)), true ); - + if (count($values) === 1) { // we could guess an answer ! $this->logger->info("This question accept multiple answers"); - $this->cacheAnswersMapping[$cf->getSlug()][$value] = + $this->cacheAnswersMapping[$cf->getSlug()][$value] = $view->vars['multiple'] == false ? $values[0] : array($values[0]); $this->logger->info(sprintf("Guessed that value '%s' match with key '%s' " . "because the CSV and the label are equals.", @@ -1021,20 +973,20 @@ EOF array_keys($matchingTableRowAnswer) ); $question->setErrorMessage("This choice is not possible"); - + if ($view->vars['multiple']) { $this->logger->debug("this question is multiple"); $question->setMultiselect(true); } - + $selected = $this->getHelper('question')->ask($this->input, $this->output, $question); - $this->output->writeln(sprintf('You have selected "%s"', - is_array($answers[$matchingTableRowAnswer[$selected]]) ? + $this->output->writeln(sprintf('You have selected "%s"', + is_array($answers[$matchingTableRowAnswer[$selected]]) ? implode(',', $answers[$matchingTableRowAnswer[$selected]]) : $answers[$matchingTableRowAnswer[$selected]]) ); - + // recording value in cache $this->cacheAnswersMapping[$cf->getSlug()][$value] = $matchingTableRowAnswer[$selected]; $this->logger->debug(sprintf("Setting the value '%s' in cache for customfield '%s' and answer '%s'", @@ -1045,19 +997,19 @@ EOF $value)); } } - + $form->submit(array($cf->getSlug() => $this->cacheAnswersMapping[$cf->getSlug()][$value])); $value = $form->getData()[$cf->getSlug()]; - + $this->logger->debug(sprintf( - "Found value : %s for custom field with question '%s'", - is_array($value) ? implode(',', $value) : $value, + "Found value : %s for custom field with question '%s'", + is_array($value) ? implode(',', $value) : $value, $this->helper->localize($cf->getName())) ); - + return $value; } - + /** * Recursive method to collect the possibles answer from a ChoiceType (or * its inherited types). @@ -1069,7 +1021,7 @@ EOF private function collectChoicesAnswers($choices) { $answers = array(); - + /* @var $choice \Symfony\Component\Form\ChoiceList\View\ChoiceView */ foreach($choices as $choice) { if ($choice instanceof \Symfony\Component\Form\ChoiceList\View\ChoiceView) { @@ -1085,10 +1037,10 @@ EOF )); } } - + return $answers; } - + /** * @param $value * @param $formats @@ -1097,13 +1049,13 @@ EOF protected function processDate($value, $formats) { $possibleFormats = explode("|", $formats); - + foreach($possibleFormats as $format) { $this->logger->debug("Trying format $format", array(__METHOD__)); $dateR = strptime($value, $format); - + if (is_array($dateR) && $dateR['unparsed'] === '') { - $string = sprintf("%04d-%02d-%02d %02d:%02d:%02d", + $string = sprintf("%04d-%02d-%02d %02d:%02d:%02d", ($dateR['tm_year']+1900), ($dateR['tm_mon']+1), ($dateR['tm_mday']), @@ -1112,19 +1064,19 @@ EOF ($dateR['tm_sec'])); $date = \DateTime::createFromFormat("Y-m-d H:i:s", $string); $this->logger->debug(sprintf("Interpreting %s as date %s", $value, $date->format("Y-m-d H:i:s"))); - + return $date; } } - + // if we arrive here, we could not process the date $this->logger->debug(sprintf( "Line %d : a date could not be interpreted. Was %s.", $this->line, $value)); - + return false; } - - + + } diff --git a/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php b/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php new file mode 100644 index 000000000..e42f0ef26 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php @@ -0,0 +1,63 @@ +importer = $socialWorkMetadata; + } + + protected function configure() + { + $this + ->setName('chill:person:import-socialwork') + ->addOption('filepath', 'f', InputOption::VALUE_REQUIRED, 'The file to import.') + ->addOption('language', 'l', InputOption::VALUE_OPTIONAL, 'The default language'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $filepath = $input->getOption('filepath'); + + try { + $csv = Reader::createFromPath($filepath); + } catch (Throwable $e) { + throw new Exception('Error while loading CSV.',0, $e); + } + + $csv->setDelimiter(';'); + + return true === $this->importer->import($csv) ? + 0: + 1; + } +} diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonController.php b/src/Bundle/ChillPersonBundle/Controller/PersonController.php index 747299e59..f33e9cce8 100644 --- a/src/Bundle/ChillPersonBundle/Controller/PersonController.php +++ b/src/Bundle/ChillPersonBundle/Controller/PersonController.php @@ -252,18 +252,6 @@ final class PersonController extends AbstractController */ $person = $form->getData(); - $periods = $person->getAccompanyingPeriodsOrdered(); - $period = $periods[0]; - $period->setOpeningDate($form['creation_date']->getData()); -// $person = new Person($form['creation_date']->getData()); -// -// $person->setFirstName($form['firstName']->getData()) -// ->setLastName($form['lastName']->getData()) -// ->setGender($form['gender']->getData()) -// ->setBirthdate($form['birthdate']->getData()) -// ->setCenter($form['center']->getData()) -// ; - return $person; } @@ -364,7 +352,6 @@ final class PersonController extends AbstractController 'lastName' => $form['lastName']->getData(), 'birthdate' => $form['birthdate']->getData(), 'gender' => $form['gender']->getData(), - 'creation_date' => $form['creation_date']->getData(), 'form' => $form->createView())); } diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodNotifications.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodNotifications.php new file mode 100644 index 000000000..d1c6f4da7 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodNotifications.php @@ -0,0 +1,39 @@ + 'Hello !', + 'entityClass' => AccompanyingPeriod::class, + 'entityRef' => LoadAccompanyingPeriod::ACCOMPANYING_PERIOD, + 'sender' => 'center a_social', + 'addressees' => [ + 'center a_social', + 'center a_administrative', + 'center a_direction', + 'multi_center' + ], + ] + ]; + + public function getDependencies() + { + return [ + LoadAccompanyingPeriod::class, + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php index e8697ff28..556d832c1 100644 --- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php @@ -183,6 +183,14 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con private function addAPerson(array $person, ObjectManager $manager) { $p = new Person(); + $p->addAccompanyingPeriod( + new AccompanyingPeriod( + (new \DateTime()) + ->sub( + new \DateInterval('P'.\random_int(0, 180).'D') + ) + ) + ); foreach ($person as $key => $value) { switch ($key) { diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index 03b05bbc8..c9ad36319 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -69,11 +69,11 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $loader->load('services/search.yaml'); $loader->load('services/menu.yaml'); $loader->load('services/privacyEvent.yaml'); - $loader->load('services/command.yaml'); $loader->load('services/actions.yaml'); $loader->load('services/form.yaml'); $loader->load('services/alt_names.yaml'); $loader->load('services/household.yaml'); + $loader->load('services/notification.yaml'); // We can get rid of this file when the service 'chill.person.repository.person' is no more used. // We should use the PersonRepository service instead of a custom service name. $loader->load('services/repository.yaml'); diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index c4b36a348..695970bfa 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -379,12 +379,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI */ private Collection $householdAddresses; - /** - * Person constructor. - * - * @param \DateTime|null $opening - */ - public function __construct(\DateTime $opening = null) + public function __construct() { $this->accompanyingPeriodParticipations = new ArrayCollection(); $this->spokenLanguages = new ArrayCollection(); @@ -393,12 +388,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI $this->otherPhoneNumbers = new ArrayCollection(); $this->householdParticipations = new ArrayCollection(); $this->householdAddresses = new ArrayCollection(); - - if ($opening === null) { - $opening = new \DateTime(); - } - - $this->open(new AccompanyingPeriod($opening)); $this->genderComment = new CommentEmbeddable(); $this->maritalStatusComment = new CommentEmbeddable(); } @@ -734,9 +723,13 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this->birthdate; } - public function getAge(): int + public function getAge(): ?int { - return date_diff($this->birthdate, date_create('now'))->format("%y"); + if ($this->birthdate instanceof \DateTimeInterface) { + return date_diff($this->birthdate, date_create('now'))->format("%y"); + } + + return null; } /** diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php index 5a877e264..e08a6de77 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php @@ -23,7 +23,7 @@ class Evaluation private $title = []; /** - * @ORM\Column(type="dateinterval") + * @ORM\Column(type="dateinterval", nullable=true) */ private $delay; diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php index 35abe392f..506a736d8 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php @@ -47,7 +47,7 @@ class SocialAction private $children; /** - * @ORM\Column(type="dateinterval") + * @ORM\Column(type="dateinterval", nullable=true) */ private $defaultNotificationDelay; diff --git a/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php b/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php index e3583071f..c001185ca 100644 --- a/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php +++ b/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php @@ -26,7 +26,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; use Symfony\Component\Form\Extension\Core\Type\HiddenType; -use Symfony\Component\Form\Extension\Core\Type\DateType; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\CenterType; use Chill\PersonBundle\Form\Type\GenderType; use Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer; @@ -80,9 +80,6 @@ final class CreationPersonType extends AbstractType 'property_path' => 'birthdate' )) ->add('gender', HiddenType::class) - ->add('creation_date', HiddenType::class, array( - 'mapped' => false - )) ->add('form_status', HiddenType::class, array( 'mapped' => false, 'data' => $options['form_status'] @@ -99,25 +96,18 @@ final class CreationPersonType extends AbstractType $builder->get('birthdate') ->addModelTransformer($dateToStringTransformer); - $builder->get('creation_date') - ->addModelTransformer($dateToStringTransformer); $builder->get('center') ->addModelTransformer($this->centerTransformer); } else { $builder ->add('firstName') ->add('lastName') - ->add('birthdate', DateType::class, array('required' => false, - 'widget' => 'single_text', 'format' => 'dd-MM-yyyy')) + ->add('birthdate', ChillDateType::class, [ + 'required' => false, + ]) ->add('gender', GenderType::class, array( 'required' => true, 'placeholder' => null )) - ->add('creation_date', DateType::class, array( - 'required' => true, - 'widget' => 'single_text', - 'format' => 'dd-MM-yyyy', - 'mapped' => false, - 'data' => new \DateTime())) ->add('form_status', HiddenType::class, array( 'data' => $options['form_status'], 'mapped' => false diff --git a/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php new file mode 100644 index 000000000..ca76fbd5c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php @@ -0,0 +1,24 @@ +getRelatedEntityClass() == AccompanyingPeriod::class; + } + + public function getTemplate() + { + return 'ChillPersonBundle:AccompanyingPeriod:showInNotification.html.twig'; + } + + public function getTemplateData(Notification $notification) + { + return ['notification' => $notification]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php index f554e4e8c..d0f11c2d3 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php @@ -5,8 +5,9 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\Persistence\ObjectRepository; -final class EvaluationRepository +final class EvaluationRepository implements ObjectRepository { private EntityRepository $repository; @@ -14,4 +15,40 @@ final class EvaluationRepository { $this->repository = $entityManager->getRepository(Evaluation::class); } + + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?Evaluation + { + return $this->repository->find($id, $lockMode, $lockVersion); + } + + /** + * @return array + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria, ?array $orderBy = null): ?Evaluation + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return class-string + */ + public function getClassName(): string + { + return Evaluation::class; + } + + } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php index e6aac493f..3c96c837c 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php @@ -18,31 +18,27 @@ final class GoalRepository implements ObjectRepository $this->repository = $entityManager->getRepository(Goal::class); } - public function find($id) + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?Goal { - return $this->repository->find($id); + return $this->repository->find($id, $lockMode, $lockVersion); } - public function findAll() + /** + * @return array + */ + public function findAll(): array { return $this->repository->findAll(); } - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) + /** + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array { return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } - public function findOneBy(array $criteria) - { - return $this->findOneBy($criteria); - } - - public function getClassName() - { - return Goal::class; - } - /** * * @return Goal[] @@ -91,4 +87,16 @@ final class GoalRepository implements ObjectRepository return $qb; } + public function findOneBy(array $criteria, ?array $orderBy = null): ?Goal + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return class-string + */ + public function getClassName(): string + { + return Goal::class; + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php index 6dc6a887c..33bd8f7ef 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php @@ -19,31 +19,19 @@ final class ResultRepository implements ObjectRepository $this->repository = $entityManager->getRepository(Result::class); } - public function find($id) + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?Result { - return $this->repository->find($id); + return $this->repository->find($id, $lockMode, $lockVersion); } - public function findAll() + /** + * @return array + */ + public function findAll(): array { return $this->repository->findAll(); } - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) - { - return $this->repository->findBy($criteria, $orderBy, $limit, $offset); - } - - public function findOneBy(array $criteria) - { - return $this->findOneBy($criteria); - } - - public function getClassName() - { - return Result::class; - } - /** * * @return Result[] @@ -133,5 +121,25 @@ final class ResultRepository implements ObjectRepository ->getSingleScalarResult() ; } -} + /** + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria, ?array $orderBy = null): ?Result + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return class-string + */ + public function getClassName(): string + { + return Result::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php index 3c86d2397..a81789c0a 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php @@ -5,8 +5,9 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\Persistence\ObjectRepository; -final class SocialActionRepository +final class SocialActionRepository implements ObjectRepository { private EntityRepository $repository; @@ -14,4 +15,38 @@ final class SocialActionRepository { $this->repository = $entityManager->getRepository(SocialAction::class); } + + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?SocialAction + { + return $this->repository->find($id, $lockMode, $lockVersion); + } + + /** + * @return array + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria, ?array $orderBy = null): ?SocialAction + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return class-string + */ + public function getClassName(): string + { + return SocialAction::class; + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php index 4dc5ecc67..b0324ba33 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php @@ -1,9 +1,10 @@ repository = $entityManager->getRepository(SocialIssue::class); } - /** - * {@inheritDoc} - */ - public function find($id) + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?SocialIssue { - return $this->repository->find($id); + return $this->repository->find($id, $lockMode, $lockVersion); } /** - * {@inheritDoc} + * @return array */ - public function findAll() + public function findAll(): array { return $this->repository->findAll(); } /** - * {@inheritDoc} + * @return array */ - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array { return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } - /** - * {@inheritDoc} - */ - public function findOneBy(array $criteria) + public function findOneBy(array $criteria, ?array $orderBy = null): ?SocialIssue { - return $this->findOneBy($criteria); + return $this->repository->findOneBy($criteria, $orderBy); } /** - * {@inheritDoc} + * @return class-string */ - public function getClassName() + public function getClassName(): string { return SocialIssue::class; } diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/showInNotification.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/showInNotification.html.twig new file mode 100644 index 000000000..d92de8cba --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/showInNotification.html.twig @@ -0,0 +1,3 @@ + + Go to Acc. period. + diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig index cc79d1a82..1c75bcac1 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig @@ -63,7 +63,7 @@ {%- if options['addAge'] -%} - {{ person.age ~ ((person.age > 1) ? ' ans' : ' an') }} + {{ 'years_old'|trans({ 'age': person.age }) }} {%- endif -%}

diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig index 76a6e18b1..a8c2d6356 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig @@ -1,7 +1,6 @@ {# * Copyright (C) 2014, Champs Libres Cooperative SCRLFS, - * - * This program is free software: you can redistribute it and/or modify + * * 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. diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/create_review.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/create_review.html.twig index 9e715c640..532ba9ecd 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/create_review.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/create_review.html.twig @@ -68,7 +68,11 @@
{{ person|chill_entity_render_string }}
{{ 'Date of birth'|trans }}
-
{{ birthdate|format_date('long')|default( 'Unknown date of birth'|trans ) }}
+ {% if birthdate is empty %} +
{{ 'Unknown date of birth'|trans }}
+ {% else %} +
{{ birthdate|format_date('long') }}
+ {% endif %}
{{ 'Gender'|trans }}
{{ gender|trans }}
@@ -83,8 +87,13 @@ {{ form_rest(form) }} - +
    +
  • + +
  • +
{{ form_end(form) }} +
diff --git a/src/Bundle/ChillPersonBundle/Service/Import/ChillImporter.php b/src/Bundle/ChillPersonBundle/Service/Import/ChillImporter.php new file mode 100644 index 000000000..4360a7cfa --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Service/Import/ChillImporter.php @@ -0,0 +1,10 @@ +socialIssueRepository = $socialIssueRepository; + $this->socialActionRepository = $socialActionRepository; + $this->goalRepository = $goalRepository; + $this->resultRepository = $resultRepository; + $this->evaluationRepository = $evaluationRepository; + $this->entityManager = $entityManager; + } + + public function import(iterable $dataset): bool + { + foreach ($dataset as $row) { + $this->import1( + array_map( + static fn (string $column): ?string => '' === $column ? null : $column, + array_map('trim', $row) + ) + ); + } + + return true; + } + + private function import1(array $row): void + { + // Structure: + // Index 0: SocialIssue.parent + // Index 1: SocialIssue + // Index 2: SocialAction.parent + // Index 3: SocialAction + // Index 4: Goal + // Index 5: Result + // Index 6: Evaluation + + $socialIssue = $this->handleSocialIssue($row[0], $row[1]); + + $socialAction = $this->handleSocialAction($row[2], $row[3], $socialIssue); + + $goal = $this->handleGoal($row[4], $socialAction); + + $result = $this->handleResult($row[5], $socialAction, $goal); + + $eval = $this->handleEvaluation($row[6], $socialAction); + + $this->entityManager->flush(); + } + + private function handleSocialIssue(?string $socialIssueTitle = null, ?string $socialIssueChildrenTitle = null): SocialIssue + { + if (null !== $socialIssueChildrenTitle) { + /** @var SocialIssue $socialIssueChildren */ + $socialIssueChildren = $this->getOrCreateEntity($this->socialIssueRepository, 'title', ['fr' => $socialIssueChildrenTitle]); + $socialIssueChildren->setTitle(['fr' => $socialIssueChildrenTitle]); + + $this->entityManager->persist($socialIssueChildren); + } + + /** @var SocialIssue $socialIssue */ + $socialIssue = $this->getOrCreateEntity($this->socialIssueRepository, 'title', ['fr' => $socialIssueTitle]); + $socialIssue->setTitle(['fr' => $socialIssueTitle]); + + if (null !== $socialIssueChildrenTitle) { + $socialIssue->addChild($socialIssueChildren); + } + + $this->entityManager->persist($socialIssue); + + return null === $socialIssueChildrenTitle ? $socialIssue : $socialIssueChildren; + } + + private function handleSocialAction(?string $socialActionTitle, ?string $socialActionChildrenTitle, SocialIssue $socialIssue): SocialAction + { + if (null !== $socialActionChildrenTitle) { + /** @var SocialAction $socialActionChildren */ + $socialActionChildren = $this->getOrCreateEntity($this->socialActionRepository, 'title', ['fr' => $socialActionChildrenTitle]); + $socialActionChildren->setTitle(['fr' => $socialActionChildrenTitle]); + + $this->entityManager->persist($socialActionChildren); + } + + /** @var SocialIssue $socialIssue */ + $socialAction = $this->getOrCreateEntity($this->socialActionRepository, 'title', ['fr' => $socialActionTitle]); + $socialAction->setTitle(['fr' => $socialActionTitle]); + + if (null !== $socialActionChildrenTitle) { + $socialActionChildren->setIssue($socialIssue); + $this->entityManager->persist($socialActionChildren); + + $socialAction->addChild($socialActionChildren); + } else { + $socialAction->setIssue($socialIssue); + } + + $this->entityManager->persist($socialAction); + + return null === $socialActionChildrenTitle ? $socialAction : $socialActionChildren; + } + + private function handleGoal(?string $goalTitle = null, ?SocialAction $socialAction = null): ?Goal + { + if (null === $goalTitle) { + return null; + } + + /** @var Goal $goal */ + $goal = $this->getOrCreateEntity($this->goalRepository, 'title', ['fr' => $goalTitle]); + $goal->setTitle(['fr' => $goalTitle]); + + if (null !== $socialAction) { + $socialAction->addGoal($goal); + $goal->addSocialAction($socialAction); + + $this->entityManager->persist($socialAction); + } + + $this->entityManager->persist($goal); + + return $goal; + } + + private function handleResult(?string $resultTitle = null, ?SocialAction $socialAction = null, ?Goal $goal = null): ?Result + { + if (null === $resultTitle) { + return null; + } + + /** @var Result $result */ + $result = $this->getOrCreateEntity($this->resultRepository, 'title', ['fr' => $resultTitle]); + $result->setTitle(['fr' => $resultTitle]); + + if (null !== $goal) { + $result->addGoal($goal); + $goal->addResult($result); + + $this->entityManager->persist($goal); + } else { + $result->addSocialAction($socialAction); + } + + $result->addSocialAction($socialAction); + $socialAction->addResult($result); + + $this->entityManager->persist($result); + $this->entityManager->persist($socialAction); + + return $result; + } + + private function handleEvaluation(?string $evaluationTitle, SocialAction $socialAction): ?Evaluation + { + if (null === $evaluationTitle) { + return null; + } + + /** @var Evaluation $eval */ + $eval = $this->getOrCreateEntity($this->evaluationRepository, 'title', ['fr' => $evaluationTitle]); + $eval->setTitle(['fr' => $evaluationTitle]); + $eval->setSocialAction($socialAction); + + $this->entityManager->persist($eval); + + return $eval; + } + + private function findByJson(ObjectRepository $repository, string $field, array $jsonCriterias): array + { + $qb = $this + ->entityManager + ->createQueryBuilder() + ->select('s') + ->from($repository->getClassName(), 's'); + + $expr = $qb->expr(); + + $temporaryJsonCriterias = $jsonParameters = []; + + foreach ($jsonCriterias as $key => $value) { + $temporaryJsonCriterias[] = [$field, $key, $value, sprintf(':placeholder_%s_%s', $field, $key)]; + } + + $jsonParameters = array_reduce( + $temporaryJsonCriterias, + static function (array $carry, array $row): array + { + [,, $value, $placeholder] = $row; + + return array_merge( + $carry, + [ + $placeholder => sprintf('"%s"', $value), + ] + ); + }, + [] + ); + + $jsonPredicates = array_map( + static function (array $row) use ($expr): Comparison + { + [$field, $key,, $placeholder] = $row; + + $left = sprintf( + "GET_JSON_FIELD_BY_KEY(s.%s, '%s')", + $field, + $key + ); + + return $expr + ->eq( + $left, + $placeholder + ); + }, + $temporaryJsonCriterias + ); + + $query = $qb + ->select('s') + ->where(...$jsonPredicates) + ->setParameters($jsonParameters) + ->getQuery(); + + return $query->getResult(); + } + + private function getOrCreateEntity(ObjectRepository $repository, string $field, array $jsonCriterias = []) + { + $results = $this + ->findByJson( + $repository, + $field, + $jsonCriterias + ); + + switch (true) { + case count($results) === 0: + $entity = $repository->getClassName(); + $entity = new $entity(); + break; + case count($results) === 1; + $entity = current($results); + break; + case count($results) > 1; + throw new Exception( + sprintf( + 'More than one entity(%s) found.', + $repository->getClassName() + ) + ); + } + + return $entity; + } + + +} diff --git a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadataInterface.php b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadataInterface.php new file mode 100644 index 000000000..015ef7485 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadataInterface.php @@ -0,0 +1,9 @@ +person->getId().'/accompanying-period'), 'the server redirects to /accompanying-period page'); $this->assertGreaterThan(0, $this->client->followRedirect() - ->filter('.success')->count(), + ->filter('.alert-success')->count(), "a 'success' element is shown"); } @@ -186,7 +186,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase * with : dateClosing: 2014-01-01 * with : the last closing motive in list * Then the response should redirect to period view - * And the next page should have a `.error` element present in page + * And the next page should have a `.alert-danger` element present in page * * @todo */ @@ -207,8 +207,8 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stays on the /close page'); $this->assertGreaterThan(0, $crawlerResponse - ->filter('.error')->count(), - "an '.error' element is shown"); + ->filter('.alert-danger')->count(), + "an '.alert-danger' element is shown"); } /** @@ -240,7 +240,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase '/fr/person/'.$this->person->getId().'/accompanying-period'), 'the server redirects to /accompanying-period page'); $this->assertGreaterThan(0, $this->client->followRedirect() - ->filter('.success')->count(), + ->filter('.alert-success')->count(), "a 'success' element is shown"); } @@ -275,7 +275,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawler->filter('.error')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -310,7 +310,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawler->filter('.error')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -351,7 +351,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawlerResponse->filter('.error')->count(), + $this->assertGreaterThan(0, $crawlerResponse->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -382,7 +382,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawler->filter('.error')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -424,7 +424,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawlerResponse->filter('.error')->count(), + $this->assertGreaterThan(0, $crawlerResponse->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -465,7 +465,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawlerResponse->filter('.error')->count(), + $this->assertGreaterThan(0, $crawlerResponse->filter('.alert-danger')->count(), "an 'error' element is shown"); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php index 57444dfaa..f7dd055c6 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php @@ -93,8 +93,6 @@ class PersonControllerCreateTest extends WebTestCase 'The page contains a "gender" input'); $this->assertTrue($form->has(self::BIRTHDATE_INPUT), 'The page has a "date of birth" input'); - $this->assertTrue($form->has(self::CREATEDATE_INPUT), - 'The page contains a "creation date" input'); $genderType = $form->get(self::GENDER_INPUT); $this->assertEquals('radio', $genderType->getType(), @@ -107,10 +105,6 @@ class PersonControllerCreateTest extends WebTestCase 'gender has "femme" option'); $this->assertFalse($genderType->hasValue(), 'The gender input is not checked'); - $today = new \DateTime(); - $this->assertEquals($today->format('d-m-Y'), $form->get(self::CREATEDATE_INPUT) - ->getValue(), 'The creation date input has the current date by default'); - return $form; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php index ffdd65f35..8a8c04538 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php @@ -102,7 +102,6 @@ class PersonControllerUpdateTest extends WebTestCase public function testHiddenFielsArePresent() { $crawler = $this->client->request('GET', $this->editUrl); - $configurables = array('placeOfBirth', 'phonenumber', 'email', 'countryOfBirth', 'nationality', 'spokenLanguages', 'maritalStatus'); $form = $crawler->selectButton('Enregistrer')->form(); //; @@ -190,7 +189,7 @@ class PersonControllerUpdateTest extends WebTestCase 'the value '.$field.' is updated in db'); $crawler = $this->client->followRedirect(); - $this->assertGreaterThan(0, $crawler->filter('.success')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-success')->count(), 'a element .success is shown'); if($field == 'birthdate' or $field == 'memo' or $field == 'countryOfBirth' or $field == 'nationality' @@ -245,7 +244,7 @@ class PersonControllerUpdateTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the page is not redirected to /general'); - $this->assertGreaterThan(0, $crawler->filter('.error')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-danger')->count(), 'a element .error is shown'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php index 7e9337d14..dbef739d1 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php @@ -45,11 +45,12 @@ class PersonTest extends \PHPUnit\Framework\TestCase public function testGetCurrentAccompanyingPeriod() { $d = new \DateTime('yesterday'); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $period = $p->getCurrentAccompanyingPeriod(); - $this->assertInstanceOf('Chill\PersonBundle\Entity\AccompanyingPeriod', $period); + $this->assertInstanceOf(AccompanyingPeriod::class, $period); $this->assertTrue($period->isOpen()); $this->assertEquals($d, $period->getOpeningDate()); @@ -67,7 +68,8 @@ class PersonTest extends \PHPUnit\Framework\TestCase public function testAccompanyingPeriodOrderWithUnorderedAccompanyingPeriod() { $d = new \DateTime("2013/2/1"); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $e = new \DateTime("2013/3/1"); $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e); @@ -93,7 +95,8 @@ class PersonTest extends \PHPUnit\Framework\TestCase */ public function testAccompanyingPeriodOrderSameDateOpening() { $d = new \DateTime("2013/2/1"); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $g = new \DateTime("2013/4/1"); $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($g); @@ -120,7 +123,8 @@ class PersonTest extends \PHPUnit\Framework\TestCase */ public function testDateCoveringWithCoveringAccompanyingPeriod() { $d = new \DateTime("2013/2/1"); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $e = new \DateTime("2013/3/1"); $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e); @@ -145,7 +149,8 @@ class PersonTest extends \PHPUnit\Framework\TestCase */ public function testNotOpenAFileReOpenedLater() { $d = new \DateTime("2013/2/1"); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $e = new \DateTime("2013/3/1"); $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e); diff --git a/src/Bundle/ChillPersonBundle/Tests/Timeline/TimelineAccompanyingPeriodTest.php b/src/Bundle/ChillPersonBundle/Tests/Timeline/TimelineAccompanyingPeriodTest.php index 4fb3c7f06..9b2a32c85 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Timeline/TimelineAccompanyingPeriodTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Timeline/TimelineAccompanyingPeriodTest.php @@ -20,9 +20,11 @@ namespace Chill\PersonBundle\Tests\Timeline; -use Symfony\Bundle\SecurityBundle\Tests\Functional\WebTestCase; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Doctrine\ORM\EntityManagerInterface; +use Chill\MainBundle\Test\PrepareClientTrait; /** * This class tests entries are shown for closing and opening @@ -31,22 +33,20 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod; * @author Julien Fastré * @author Champs Libres */ -class TimelineAccompanyingPeriodTest extends \Chill\PersonBundle\Tests\Controller\AccompanyingPeriodControllerTest +class TimelineAccompanyingPeriodTest extends WebTestCase { - public function testEntriesAreShown() + use PrepareClientTrait; + + /** + * @dataProvider provideDataPersonWithAccompanyingPeriod + */ + public function testEntriesAreShown($personId) { - $this->generatePeriods(array( - [ - 'openingDate' => '2014-01-01', - 'closingDate' => '2014-12-31', - 'closingMotive' => $this->getRandomClosingMotive() - ] - )); + $client = $this->getClientAuthenticated(); + + $crawler = $client->request('GET', "/en/person/{$personId}/timeline"); - $crawler = $this->client->request('GET', '/en/person/' - .$this->person->getId().'/timeline'); - - $this->assertTrue($this->client->getResponse()->isSuccessful(), + $this->assertTrue($client->getResponse()->isSuccessful(), "the timeline page loads sucessfully"); $this->assertGreaterThan(0, $crawler->filter('.timeline div')->count(), "the timeline page contains multiple div inside a .timeline element"); @@ -57,5 +57,34 @@ class TimelineAccompanyingPeriodTest extends \Chill\PersonBundle\Tests\Controlle $crawler->Filter('.timeline')->text(), "the text 'Une période d'accompagnement a été fermée' is present"); } + + public function provideDataPersonWithAccompanyingPeriod() + { + self::bootKernel(); + + $qb = self::$container->get(EntityManagerInterface::class) + ->createQueryBuilder() + ; + $personIds = $qb + ->from(Person::class, 'p') + ->join('p.accompanyingPeriodParticipations', 'part') + ->join('part.accompanyingPeriod', 'period') + ->join('p.center', 'center') + ->select('p.id') + ->where($qb->expr()->isNotNull('period.closingDate')) + ->andWhere($qb->expr()->eq('center.name', ':center')) + ->setParameter('center', 'Center A') + ->setMaxResults(1000) + ->getQuery() + ->getResult() + ; + + \shuffle($personIds); + + yield [ \array_pop($personIds)['id'] ]; + yield [ \array_pop($personIds)['id'] ]; + yield [ \array_pop($personIds)['id'] ]; + + } } diff --git a/src/Bundle/ChillPersonBundle/config/services.yaml b/src/Bundle/ChillPersonBundle/config/services.yaml index a23ba39b3..aedea4396 100644 --- a/src/Bundle/ChillPersonBundle/config/services.yaml +++ b/src/Bundle/ChillPersonBundle/config/services.yaml @@ -6,12 +6,24 @@ services: autowire: true autoconfigure: true + Chill\PersonBundle\Service\: + resource: '../Service/' + autowire: true + autoconfigure: true + Chill\PersonBundle\Serializer\Normalizer\: resource: '../Serializer/Normalizer/' autowire: true tags: - { name: 'serializer.normalizer', priority: 64 } + Chill\PersonBundle\Command\: + resource: '../Command/' + autowire: true + autoconfigure: true + tags: + - { name: console.command } + chill.person.form.type.select2maritalstatus: class: Chill\PersonBundle\Form\Type\Select2MaritalStatusType arguments: @@ -49,7 +61,7 @@ services: tags: - { name: security.voter } - { name: chill.role } - + chill.person.birthdate_validation: class: Chill\PersonBundle\Validator\Constraints\BirthdateValidator arguments: diff --git a/src/Bundle/ChillPersonBundle/config/services/command.yaml b/src/Bundle/ChillPersonBundle/config/services/command.yaml deleted file mode 100644 index 685a348dd..000000000 --- a/src/Bundle/ChillPersonBundle/config/services/command.yaml +++ /dev/null @@ -1,19 +0,0 @@ -services: - Chill\PersonBundle\Command\ChillPersonMoveCommand: - arguments: - $em: '@Doctrine\ORM\EntityManagerInterface' - $mover: '@Chill\PersonBundle\Actions\Remove\PersonMove' - $chillLogger: '@chill.main.logger' - tags: - - { name: console.command } - - Chill\PersonBundle\Command\ImportPeopleFromCSVCommand: - arguments: - $logger: '@logger' - $helper: '@Chill\MainBundle\Templating\TranslatableStringHelper' - $em: '@Doctrine\ORM\EntityManagerInterface' - $customFieldProvider: '@Chill\CustomFieldsBundle\Service\CustomFieldProvider' - $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' - $formFactory: '@form.factory' - tags: - - { name: console.command } diff --git a/src/Bundle/ChillPersonBundle/config/services/notification.yaml b/src/Bundle/ChillPersonBundle/config/services/notification.yaml new file mode 100644 index 000000000..58187defb --- /dev/null +++ b/src/Bundle/ChillPersonBundle/config/services/notification.yaml @@ -0,0 +1,3 @@ +services: + Chill\PersonBundle\Notification\AccompanyingPeriodNotificationRenderer: + autowire: true diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210623142046.php b/src/Bundle/ChillPersonBundle/migrations/Version20210623142046.php new file mode 100644 index 000000000..8baffb867 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210623142046.php @@ -0,0 +1,29 @@ +addSql('ALTER TABLE chill_person_social_action ALTER defaultnotificationdelay DROP NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_person_social_action ALTER defaultNotificationDelay SET NOT NULL'); + } +} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210624131722.php b/src/Bundle/ChillPersonBundle/migrations/Version20210624131722.php new file mode 100644 index 000000000..ebeb33e50 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210624131722.php @@ -0,0 +1,29 @@ +addSql('ALTER TABLE chill_person_social_work_evaluation ALTER delay DROP NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_person_social_work_evaluation ALTER delay SET NOT NULL'); + } +} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210624131723.php b/src/Bundle/ChillPersonBundle/migrations/Version20210624131723.php new file mode 100644 index 000000000..d36d23733 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210624131723.php @@ -0,0 +1,29 @@ +addSql('ALTER TABLE chill_person_social_work_evaluation ALTER notificationdelay DROP NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_person_social_work_evaluation ALTER notificationdelay SET NOT NULL'); + } +}