fix merge conflicts

This commit is contained in:
Julie Lenaerts 2022-02-28 19:47:22 +01:00
commit a6ac6ee6d8
119 changed files with 2545 additions and 979 deletions

View File

@ -11,6 +11,20 @@ and this project adheres to
## Unreleased ## Unreleased
<!-- write down unreleased development here --> <!-- write down unreleased development here -->
* [docstore] Add an API entrypoint for StoredObject (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Add the possibility of uploading existing documents to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Add title to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Order social issues by the field "ordering" (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/388)
* [Person/Household list] when listing other simultaneous members of an household, exclude the members on person, not on members (avoid to show two membersship with the same person)
* [Person] Display suffixText in RenderPerson, PersonText.vue, RenderPersonBox.vue (was made for displaying "enfant confie") (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/441)
## Test releases
### test release 2022-02-21
* [notifications] Word 'un' changed to number '1' for notifications in user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/483)
* [documents] 'gabarit' changed to 'modèle' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/405)
* [person_resources] Menu name and order changed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/460)
* workflow: fix sending notifications * workflow: fix sending notifications
* [thirdparty] Extend the thirdparty search to thirdparty children (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/448) * [thirdparty] Extend the thirdparty search to thirdparty children (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/448)
* [person]: AddPersons: allow creation of person or thirdparty only (no users) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422) * [person]: AddPersons: allow creation of person or thirdparty only (no users) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
@ -18,6 +32,7 @@ and this project adheres to
* [person]: AddPersons: add suggestion of name when creating new person or thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422) * [person]: AddPersons: add suggestion of name when creating new person or thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
* [main] Address: fix small bug: when modifying an address without street (isNoAddress), also check errors if street is an empty string as back-end change null value to empty string for street (and streetNumber) * [main] Address: fix small bug: when modifying an address without street (isNoAddress), also check errors if street is an empty string as back-end change null value to empty string for street (and streetNumber)
* [main] Address: stronger client-side validation of addresses (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/449) * [main] Address: stronger client-side validation of addresses (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/449)
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
* [person] accompanying course: filter suggested entities by open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/415) * [person] accompanying course: filter suggested entities by open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/415)
[activity] can click through the cross icon for removing person in concerned group (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476) [activity] can click through the cross icon for removing person in concerned group (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
[activity] correct associated persons by considering only open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476) [activity] correct associated persons by considering only open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
@ -25,12 +40,16 @@ and this project adheres to
* [Household]: Add end date in HouseholdMember form for 'enfant hors menage' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/434) * [Household]: Add end date in HouseholdMember form for 'enfant hors menage' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/434)
* [homepage_widget]: If no sender then display as 'notification automatique' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/435) * [homepage_widget]: If no sender then display as 'notification automatique' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/435)
* [parcours]: Order social activities and only display most recent three in parcours resumé (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/481) * [parcours]: Order social activities and only display most recent three in parcours resumé (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/481)
* [3party]: 3party: redirect to parent when contact (child) is opened in view page
* [parcours / addresses]: launch an event when a person change address (either through changing household or because the household is associated to a new address). If the person is localising a course, the course location go back to a temporarily address. * [parcours / addresses]: launch an event when a person change address (either through changing household or because the household is associated to a new address). If the person is localising a course, the course location go back to a temporarily address.
* [thirdparty]: address/phonenumber/email/fonction displayed in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/401) * [thirdparty]: address/phonenumber/email/fonction displayed in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/401)
* [thirdparty_contact]: in search results the 'qualité' is displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/465) * [thirdparty_contact]: in search results the 'qualité' is displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/465)
* [bug]: fix confidential toggle of address in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/460) * [bug]: fix confidential toggle of address in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/460)
## Test releases ## Test releases
* Creation of PickCivilityType, and implementation in PersonType and ThirdpartyType
* [renderbox]: Fix display of address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/462)
* [renderbox]: Add email in personRenderBox, this was not yet displayed.
### test release 2022-02-14 ### test release 2022-02-14
@ -77,7 +96,19 @@ and this project adheres to
* [AddAddress] disable multiselect search, and rely only on most pertinent Cities and Street computed backend * [AddAddress] disable multiselect search, and rely only on most pertinent Cities and Street computed backend
* [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413 * [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc. * [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
## Test releases
=======
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
>>>>>>> issue422_and_others_on_AddPersons
=======
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
>>>>>>> b0d50d315c8e00959a967badac9cf5057ab2b4bc
### test release 2021-01-31 ### test release 2021-01-31
* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409) * [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="v4.15.0@a1b5e489e6fcebe40cb804793d964e99fc347820"> <files psalm-version="4.18.0.0">
<file src="src/Bundle/ChillActivityBundle/Controller/ActivityReasonCategoryController.php"> <file src="src/Bundle/ChillActivityBundle/Controller/ActivityReasonCategoryController.php">
<UndefinedClass occurrences="4"> <UndefinedClass occurrences="4">
<code>'ChillActivityBundle:ActivityReasonCategory'</code> <code>'ChillActivityBundle:ActivityReasonCategory'</code>
@ -47,6 +47,11 @@
<code>Activity[]|array</code> <code>Activity[]|array</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillActivityBundle/Resources/test/Fixtures/App/app/AppKernel.php">
<DuplicateClass occurrences="1">
<code>AppKernel</code>
</DuplicateClass>
</file>
<file src="src/Bundle/ChillActivityBundle/Resources/test/Fixtures/App/app/autoload.php"> <file src="src/Bundle/ChillActivityBundle/Resources/test/Fixtures/App/app/autoload.php">
<MissingFile occurrences="1"> <MissingFile occurrences="1">
<code>require __DIR__ . '/../../../../../vendor/autoload.php'</code> <code>require __DIR__ . '/../../../../../vendor/autoload.php'</code>
@ -164,72 +169,18 @@
<code>type</code> <code>type</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Export/Export/CountActivityTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Export/Export/ListActivityTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Export/Export/StatActivityDurationSumTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php"> <file src="src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
<UndefinedDocblockClass occurrences="1"> <UndefinedDocblockClass occurrences="1">
<code>\Chill\PersonBundle\Export\Filter\PersonHavingActivityBetweenDateFilter</code> <code>\Chill\PersonBundle\Export\Filter\PersonHavingActivityBetweenDateFilter</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillActivityBundle/Tests/Form/ActivityTypeTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Form/Type/TranslatableActivityReasonTest.php"> <file src="src/Bundle/ChillActivityBundle/Tests/Form/Type/TranslatableActivityReasonTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
<TooFewArguments occurrences="2"/> <TooFewArguments occurrences="2"/>
<UndefinedDocblockClass occurrences="1"> <UndefinedDocblockClass occurrences="1">
<code>Prophecy\Prophet</code> <code>Prophecy\Prophet</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillActivityBundle/Tests/Form/Type/TranslatableActivityTypeTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillActivityBundle/Tests/Security/Authorization/ActivityVoterTest.php"> <file src="src/Bundle/ChillActivityBundle/Tests/Security/Authorization/ActivityVoterTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
<UndefinedDocblockClass occurrences="1"> <UndefinedDocblockClass occurrences="1">
<code>type</code> <code>type</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
@ -254,11 +205,6 @@
<code>$asideActivityCategory</code> <code>$asideActivityCategory</code>
</ParamNameMismatch> </ParamNameMismatch>
</file> </file>
<file src="src/Bundle/ChillAsideActivityBundle/src/Tests/Controller/AsideActivityControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillBudgetBundle/Calculator/CalculatorManager.php"> <file src="src/Bundle/ChillBudgetBundle/Calculator/CalculatorManager.php">
<InvalidStaticInvocation occurrences="3"> <InvalidStaticInvocation occurrences="3">
<code>$calculator::getAlias()</code> <code>$calculator::getAlias()</code>
@ -288,11 +234,6 @@
<code>'ChillCalendarBundle:Calendar'</code> <code>'ChillCalendarBundle:Calendar'</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillCalendarBundle/Tests/Controller/CalendarControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php"> <file src="src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php">
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
<code>'ChillCustomFieldsBundle:CustomFieldsGroup'</code> <code>'ChillCustomFieldsBundle:CustomFieldsGroup'</code>
@ -397,10 +338,6 @@
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsChoiceTest.php"> <file src="src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsChoiceTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedDocblockClass occurrences="4"> <UndefinedDocblockClass occurrences="4">
<code>type</code> <code>type</code>
<code>type</code> <code>type</code>
@ -408,61 +345,6 @@
<code>type</code> <code>type</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsNumberTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillCustomFieldsBundle/Tests/CustomFields/CustomFieldsTextTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillCustomFieldsBundle/Tests/Form/Extension/PostTextIntegerExtensionTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillCustomFieldsBundle/Tests/Form/Extension/PostTextNumberExtensionTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillCustomFieldsBundle/Tests/Service/CustomFieldsHelperTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillCustomFieldsBundle/Tests/Templating/Twig/CustomFieldRenderingTwigTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillCustomFieldsBundle/Tests/Templating/Twig/CustomFieldsGroupRenderingTwigTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Encoder/DocGenEncoderTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Normalizer/DocGenObjectNormalizerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/BaseContextDataTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php">
<UndefinedClass occurrences="1">
<code>'ChillDocStoreBundle:AccompanyingCourseDocument'</code>
</UndefinedClass>
</file>
<file src="src/Bundle/ChillDocStoreBundle/Controller/DocumentCategoryController.php"> <file src="src/Bundle/ChillDocStoreBundle/Controller/DocumentCategoryController.php">
<UndefinedClass occurrences="3"> <UndefinedClass occurrences="3">
<code>'ChillDocStoreBundle:DocumentCategory'</code> <code>'ChillDocStoreBundle:DocumentCategory'</code>
@ -470,11 +352,6 @@
<code>'ChillDocStoreBundle:DocumentCategory'</code> <code>'ChillDocStoreBundle:DocumentCategory'</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php">
<UndefinedClass occurrences="1">
<code>'ChillDocStoreBundle:PersonDocument'</code>
</UndefinedClass>
</file>
<file src="src/Bundle/ChillDocStoreBundle/Entity/Document.php"> <file src="src/Bundle/ChillDocStoreBundle/Entity/Document.php">
<UndefinedDocblockClass occurrences="1"> <UndefinedDocblockClass occurrences="1">
<code>\Chill\PersonBundle\Entity\user</code> <code>\Chill\PersonBundle\Entity\user</code>
@ -570,9 +447,6 @@
<InvalidParamDefault occurrences="1"> <InvalidParamDefault occurrences="1">
<code>type</code> <code>type</code>
</InvalidParamDefault> </InvalidParamDefault>
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="8"> <UndefinedClass occurrences="8">
<code>'ChillEventBundle:Event'</code> <code>'ChillEventBundle:Event'</code>
<code>'ChillEventBundle:Event'</code> <code>'ChillEventBundle:Event'</code>
@ -588,10 +462,6 @@
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillEventBundle/Tests/Search/EventSearchTest.php"> <file src="src/Bundle/ChillEventBundle/Tests/Search/EventSearchTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
<code>'ChillMainBundle:Scope'</code> <code>'ChillMainBundle:Scope'</code>
</UndefinedClass> </UndefinedClass>
@ -891,11 +761,6 @@
<code>OptionsResolverInterface</code> <code>OptionsResolverInterface</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillMainBundle/Notification/Mailer.php">
<UndefinedDocblockClass occurrences="1">
<code>\Symfony\Component\Mailer\Exception\TransportExceptionInterface</code>
</UndefinedDocblockClass>
</file>
<file src="src/Bundle/ChillMainBundle/Pagination/PageGenerator.php"> <file src="src/Bundle/ChillMainBundle/Pagination/PageGenerator.php">
<UndefinedVariable occurrences="1"> <UndefinedVariable occurrences="1">
<code>$current</code> <code>$current</code>
@ -924,11 +789,6 @@
<code>$onlyEnabled</code> <code>$onlyEnabled</code>
</ParamNameMismatch> </ParamNameMismatch>
</file> </file>
<file src="src/Bundle/ChillMainBundle/Resources/test/Fixtures/App/app/AppKernel.php">
<DuplicateClass occurrences="1">
<code>AppKernel</code>
</DuplicateClass>
</file>
<file src="src/Bundle/ChillMainBundle/Resources/test/Fixtures/App/app/autoload.php"> <file src="src/Bundle/ChillMainBundle/Resources/test/Fixtures/App/app/autoload.php">
<MissingFile occurrences="1"> <MissingFile occurrences="1">
<code>require __DIR__ . '/../../../../../vendor/autoload.php'</code> <code>require __DIR__ . '/../../../../../vendor/autoload.php'</code>
@ -1056,158 +916,15 @@
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php"> <file src="src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
<UndefinedDocblockClass occurrences="1"> <UndefinedDocblockClass occurrences="1">
<code>type</code> <code>type</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillMainBundle/Tests/Authorization/ParentRoleHelperTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Controller/AddressControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Controller/UserControllerTest.php">
<MethodSignatureMismatch occurrences="2">
<code>protected function tearDown()</code>
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedDocblockClass occurrences="40">
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>$this-&gt;prophet</code>
<code>Prophecy\Prophet</code>
</UndefinedDocblockClass>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Form/Type/PickCenterTypeTest.php">
<UndefinedClass occurrences="1">
<code>CenterType</code>
</UndefinedClass>
<UndefinedDocblockClass occurrences="1">
<code>CenterType</code>
</UndefinedDocblockClass>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Pagination/PageTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Pagination/PaginatorTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Routing/Loader/RouteLoaderTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Search/AbstractSearchTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Search/SearchProviderTest.php"> <file src="src/Bundle/ChillMainBundle/Tests/Search/SearchProviderTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
<UndefinedDocblockClass occurrences="1"> <UndefinedDocblockClass occurrences="1">
<code>unknown</code> <code>unknown</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Security/Resolver/CenterResolverDispatcherTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Security/Resolver/DefaultScopeResolverTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Security/Resolver/ScopeResolverDispatcherTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/DateNormalizerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/DoctrineExistingEntityNormalizerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Services/MenuComposerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Tests/Templating/Entity/AddressRenderTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php"> <file src="src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php">
<UndefinedDocblockClass occurrences="1"> <UndefinedDocblockClass occurrences="1">
<code>unknown</code> <code>unknown</code>
@ -1412,20 +1129,6 @@
<code>OptionsResolverInterface</code> <code>OptionsResolverInterface</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php">
<UndefinedClass occurrences="6">
<code>$qb</code>
<code>$qb</code>
<code>$qb</code>
<code>QueryBuilder</code>
<code>SocialAction</code>
<code>SocialAction</code>
</UndefinedClass>
<UndefinedVariable occurrences="2">
<code>$action</code>
<code>$orderBy</code>
</UndefinedVariable>
</file>
<file src="src/Bundle/ChillPersonBundle/Resources/test/Fixtures/App/app/AppKernel.php"> <file src="src/Bundle/ChillPersonBundle/Resources/test/Fixtures/App/app/AppKernel.php">
<DuplicateClass occurrences="1"> <DuplicateClass occurrences="1">
<code>AppKernel</code> <code>AppKernel</code>
@ -1505,33 +1208,13 @@
</ParamNameMismatch> </ParamNameMismatch>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/AccompanyingPeriodConfidentialTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/AccompanyingPeriodConfidentialTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public static function setUpBeforeClass()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="2"> <UndefinedClass occurrences="2">
<code>AccompanyingPeriodRepository</code> <code>AccompanyingPeriodRepository</code>
<code>stdClass</code> <code>stdClass</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public static function setUpBeforeClass()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingPeriodControllerTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingPeriodControllerTest.php">
<InvalidThrow occurrences="1"/> <InvalidThrow occurrences="1"/>
<MethodSignatureMismatch occurrences="3">
<code>public function setUp()</code>
<code>public function tearDown()</code>
<code>public static function setUpBeforeClass()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="3"> <UndefinedClass occurrences="3">
<code>'ChillMainBundle:Center'</code> <code>'ChillMainBundle:Center'</code>
<code>'ChillPersonBundle:AccompanyingPeriod\ClosingMotive'</code> <code>'ChillPersonBundle:AccompanyingPeriod\ClosingMotive'</code>
@ -1543,40 +1226,19 @@
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/HouseholdApiControllerTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/Controller/HouseholdApiControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
<code>User</code> <code>User</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/HouseholdControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonAddressControllerTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonAddressControllerTest.php">
<InvalidScope occurrences="1"> <InvalidScope occurrences="1">
<code>$this</code> <code>$this</code>
</InvalidScope> </InvalidScope>
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public static function setUpBeforeClass()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
<code>'ChillPersonBundle:Person'</code> <code>'ChillPersonBundle:Person'</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public static function tearDownAfterClass()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="2"> <UndefinedClass occurrences="2">
<code>'ChillMainBundle:Country'</code> <code>'ChillMainBundle:Country'</code>
<code>'ChillPersonBundle:Person'</code> <code>'ChillPersonBundle:Person'</code>
@ -1586,140 +1248,25 @@
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateWithHiddenFieldsTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="2"> <UndefinedClass occurrences="2">
<code>'ChillMainBundle:Country'</code> <code>'ChillMainBundle:Country'</code>
<code>'ChillPersonBundle:Person'</code> <code>'ChillPersonBundle:Person'</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerViewTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerViewTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
<code>'ChillPersonBundle:Person'</code> <code>'ChillPersonBundle:Person'</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerViewWithHiddenFieldsTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerViewWithHiddenFieldsTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
<code>'ChillPersonBundle:Person'</code> <code>'ChillPersonBundle:Person'</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/RelationshipApiControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Controller/SocialIssueApiControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php"> <file src="src/Bundle/ChillPersonBundle/Tests/Entity/AccompanyingPeriodTest.php">
<AssignmentToVoid occurrences="1"> <AssignmentToVoid occurrences="1">
<code>$participationL</code> <code>$participationL</code>
</AssignmentToVoid> </AssignmentToVoid>
<NullReference occurrences="1">
<code>getEndDate</code>
</NullReference>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AgeAggregatorTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/GenderAggregatorTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/NationalityAggregatorTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Export/Export/CountPersonTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingPeriodFilterTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Export/Filter/BirthdayFilterTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Export/Filter/GenderFilterTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Form/Type/PickPersonTypeTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Household/MembersEditorTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Security/Authorization/PersonVoterTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/AccompanyingPeriodWorkDocGenNormalizerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/HouseholdNormalizerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/PersonDocGenNormalizerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/PersonJsonNormalizerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonValidationTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillPersonBundle/Tests/Workflows/AccompanyingPeriodLifecycle.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file> </file>
<file src="src/Bundle/ChillPersonBundle/Timeline/AbstractTimelineAccompanyingPeriod.php"> <file src="src/Bundle/ChillPersonBundle/Timeline/AbstractTimelineAccompanyingPeriod.php">
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
@ -1820,20 +1367,13 @@
<code>require __DIR__ . '/../../../../../vendor/autoload.php'</code> <code>require __DIR__ . '/../../../../../vendor/autoload.php'</code>
</MissingFile> </MissingFile>
</file> </file>
<file src="src/Bundle/ChillReportBundle/Tests/Controller/ReportControllerNextTest.php">
<MethodSignatureMismatch occurrences="1">
<code>public function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillReportBundle/Tests/Controller/ReportControllerTest.php"> <file src="src/Bundle/ChillReportBundle/Tests/Controller/ReportControllerTest.php">
<InvalidParamDefault occurrences="1"> <InvalidParamDefault occurrences="1">
<code>type</code> <code>type</code>
</InvalidParamDefault> </InvalidParamDefault>
<MethodSignatureMismatch occurrences="2"> <UndefinedDocblockClass occurrences="31">
<code>public function setUp()</code> <code>$client</code>
<code>public static function setUpBeforeClass()</code> <code>$client</code>
</MethodSignatureMismatch>
<UndefinedDocblockClass occurrences="29">
<code>$client</code> <code>$client</code>
<code>$client</code> <code>$client</code>
<code>$client</code> <code>$client</code>
@ -1865,16 +1405,7 @@
<code>type</code> <code>type</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillReportBundle/Tests/DependencyInjection/ChillReportExtensionTest.php">
<UndefinedClass occurrences="1">
<code>Exception</code>
</UndefinedClass>
</file>
<file src="src/Bundle/ChillReportBundle/Tests/Security/Authorization/ReportVoterTest.php"> <file src="src/Bundle/ChillReportBundle/Tests/Security/Authorization/ReportVoterTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public static function setUpBeforeClass()</code>
</MethodSignatureMismatch>
<UndefinedDocblockClass occurrences="3"> <UndefinedDocblockClass occurrences="3">
<code>type</code> <code>type</code>
<code>type</code> <code>type</code>
@ -1882,10 +1413,6 @@
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillReportBundle/Tests/Timeline/TimelineProviderTest.php"> <file src="src/Bundle/ChillReportBundle/Tests/Timeline/TimelineProviderTest.php">
<MethodSignatureMismatch occurrences="2">
<code>public function setUp()</code>
<code>public function tearDown()</code>
</MethodSignatureMismatch>
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
<code>'ChillCustomFieldsBundle:CustomFieldsGroup'</code> <code>'ChillCustomFieldsBundle:CustomFieldsGroup'</code>
</UndefinedClass> </UndefinedClass>
@ -1958,11 +1485,6 @@
<code>CacheItempPoolInterface</code> <code>CacheItempPoolInterface</code>
</UndefinedDocblockClass> </UndefinedDocblockClass>
</file> </file>
<file src="src/Bundle/ChillTaskBundle/Tests/Controller/SingleTaskControllerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillThirdPartyBundle/DependencyInjection/CompilerPass/ThirdPartyTypeCompilerPass.php"> <file src="src/Bundle/ChillThirdPartyBundle/DependencyInjection/CompilerPass/ThirdPartyTypeCompilerPass.php">
<UndefinedClass occurrences="1"> <UndefinedClass occurrences="1">
<code>$taggedService-&gt;getClass()::getKey()</code> <code>$taggedService-&gt;getClass()::getKey()</code>
@ -1988,11 +1510,6 @@
<code>$thirdParty</code> <code>$thirdParty</code>
</ParamNameMismatch> </ParamNameMismatch>
</file> </file>
<file src="src/Bundle/ChillThirdPartyBundle/Tests/Serializer/Normalizer/ThirdPartyJsonDenormalizerTest.php">
<MethodSignatureMismatch occurrences="1">
<code>protected function setUp()</code>
</MethodSignatureMismatch>
</file>
<file src="src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php"> <file src="src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php">
<UndefinedDocblockClass occurrences="6"> <UndefinedDocblockClass occurrences="6">
<code>$object</code> <code>$object</code>

View File

@ -22,7 +22,7 @@ use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Templating\Entity\PersonRender; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
@ -43,7 +43,7 @@ class ActivityContext implements
private NormalizerInterface $normalizer; private NormalizerInterface $normalizer;
private PersonRender $personRender; private PersonRenderInterface $personRender;
private TranslatableStringHelperInterface $translatableStringHelper; private TranslatableStringHelperInterface $translatableStringHelper;
@ -54,7 +54,7 @@ class ActivityContext implements
NormalizerInterface $normalizer, NormalizerInterface $normalizer,
TranslatableStringHelperInterface $translatableStringHelper, TranslatableStringHelperInterface $translatableStringHelper,
EntityManagerInterface $em, EntityManagerInterface $em,
PersonRender $personRender, PersonRenderInterface $personRender,
TranslatorInterface $translator, TranslatorInterface $translator,
BaseContextData $baseContextData BaseContextData $baseContextData
) { ) {

View File

@ -16,8 +16,8 @@
<div class="input-group mb-3"> <div class="input-group mb-3">
<select class="form-select" v-model="template"> <select class="form-select" v-model="template">
<option disabled selected value="">{{ $t('choose_a_template') }}</option> <option disabled selected value="">{{ $t('choose_a_template') }}</option>
<template v-for="t in templates"> <template v-for="t in templates" :key="t.id">
<option v-bind:value="t.id">{{ t.name.fr || 'Aucun nom défini' }}</option> <option :value="t.id" >{{ t.name.fr || 'Aucun nom défini' }}</option>
</template> </template>
</select> </select>
<a v-if="canGenerate" class="btn btn-update btn-sm change-icon" :href="buildUrlGenerate" @click.prevent="clickGenerate($event, buildUrlGenerate)"><i class="fa fa-fw fa-cog"></i></a> <a v-if="canGenerate" class="btn btn-update btn-sm change-icon" :href="buildUrlGenerate" @click.prevent="clickGenerate($event, buildUrlGenerate)"><i class="fa fa-fw fa-cog"></i></a>

View File

@ -2,11 +2,11 @@ docgen:
Generate a document: Génerer un document Generate a document: Génerer un document
Generate: Génerer Generate: Génerer
Document generation: Génération de documents Document generation: Génération de documents
Manage templates and document generation: Gestion des documents générés et de leurs gabarits Manage templates and document generation: Gestion des documents générés et de leurs modèles
Pick template context: Choisir un contexte Pick template context: Choisir un contexte
Context: Contexte Context: Contexte
New template: Nouveau gabarit New template: Nouveau modèle
Edit template: Modifier gabarit Edit template: Modifier modèle
test generate: Tester la génération test generate: Tester la génération
With context: 'Avec le contexte :' With context: 'Avec le contexte :'

View File

@ -17,6 +17,7 @@ use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/** /**
@ -46,6 +47,28 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf
$this->prependRoute($container); $this->prependRoute($container);
$this->prependAuthorization($container); $this->prependAuthorization($container);
$this->prependTwig($container); $this->prependTwig($container);
$this->prependApis($container);
}
protected function prependApis(ContainerBuilder $container)
{
$container->prependExtensionConfig('chill_main', [
'apis' => [
[
'class' => \Chill\DocStoreBundle\Entity\StoredObject::class,
'name' => 'stored_object',
'base_path' => '/api/1.0/docstore/stored-object',
'base_role' => 'ROLE_USER',
'actions' => [
'_entity' => [
'methods' => [
Request::METHOD_POST => true,
],
],
],
],
],
]);
} }
protected function prependAuthorization(ContainerBuilder $container) protected function prependAuthorization(ContainerBuilder $container)

View File

@ -34,19 +34,19 @@ class StoredObject implements AsyncFileInterface, Document
{ {
/** /**
* @ORM\Column(type="datetime", name="creation_date") * @ORM\Column(type="datetime", name="creation_date")
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "write"})
*/ */
private DateTimeInterface $creationDate; private DateTimeInterface $creationDate;
/** /**
* @ORM\Column(type="json", name="datas") * @ORM\Column(type="json", name="datas")
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "write"})
*/ */
private array $datas = []; private array $datas = [];
/** /**
* @ORM\Column(type="text") * @ORM\Column(type="text")
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "write"})
*/ */
private $filename; private $filename;
@ -54,30 +54,32 @@ class StoredObject implements AsyncFileInterface, Document
* @ORM\Id * @ORM\Id
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "write"})
*/ */
private $id; private $id;
/** /**
* @var int[] * @var int[]
* @ORM\Column(type="json", name="iv") * @ORM\Column(type="json", name="iv")
* @Serializer\Groups({"write"})
*/ */
private array $iv = []; private array $iv = [];
/** /**
* @ORM\Column(type="json", name="key") * @ORM\Column(type="json", name="key")
* @Serializer\Groups({"write"})
*/ */
private array $keyInfos = []; private array $keyInfos = [];
/** /**
* @ORM\Column(type="text", name="type") * @ORM\Column(type="text", name="type")
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "write"})
*/ */
private string $type = ''; private string $type = '';
/** /**
* @ORM\Column(type="uuid", unique=true) * @ORM\Column(type="uuid", unique=true)
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "write"})
*/ */
private UuidInterface $uuid; private UuidInterface $uuid;

View File

@ -4,12 +4,12 @@ var initializeDownload = require('./downloader.js');
/** /**
* *
* define a dropzone for chill usage * define a dropzone for chill usage
* *
* An event is launched when dropzone is initialize, allowing to customize events * An event is launched when dropzone is initialize, allowing to customize events
* on dropzone : * on dropzone :
* *
* ``` * ```
* window.addEventListener("chill_dropzone_initialized", (e) => { * window.addEventListener("chill_dropzone_initialized", (e) => {
* // do something with dropzone: * // do something with dropzone:
@ -18,7 +18,7 @@ var initializeDownload = require('./downloader.js');
* }); * });
* }); * });
* ``` * ```
* *
*/ */
// load css // load css
@ -37,7 +37,6 @@ var keyDefinition = {
var searchForZones = function(root) { var searchForZones = function(root) {
var zones = root.querySelectorAll('div[data-stored-object]'); var zones = root.querySelectorAll('div[data-stored-object]');
for(let i=0; i < zones.length; i++) { for(let i=0; i < zones.length; i++) {
initialize(zones[i]); initialize(zones[i]);
} }
@ -48,32 +47,32 @@ var getUploadUrl = function(zoneData, files) {
generateTempUrlPost = zoneData.zone.querySelector('input[data-async-file-upload]').dataset.generateTempUrlPost, generateTempUrlPost = zoneData.zone.querySelector('input[data-async-file-upload]').dataset.generateTempUrlPost,
oReq = new XMLHttpRequest() oReq = new XMLHttpRequest()
; ;
// arg, dropzone, you cannot handle async upload... // arg, dropzone, you cannot handle async upload...
oReq.open("GET", generateTempUrlPost, false); oReq.open("GET", generateTempUrlPost, false);
oReq.send(); oReq.send();
if (oReq.readyState !== XMLHttpRequest.DONE) { if (oReq.readyState !== XMLHttpRequest.DONE) {
throw new Error("Error while fetching url to upload"); throw new Error("Error while fetching url to upload");
} }
zoneData.params = JSON.parse(oReq.responseText); zoneData.params = JSON.parse(oReq.responseText);
return zoneData.params.url; return zoneData.params.url;
}; };
var encryptFile = function(originalFile, zoneData, done) { var encryptFile = function(originalFile, zoneData, done) {
var var
iv = crypto.getRandomValues(new Uint8Array(16)), iv = crypto.getRandomValues(new Uint8Array(16)),
reader = new FileReader(), reader = new FileReader(),
jsKey, rawKey jsKey, rawKey
; ;
zoneData.originalType = originalFile.type; zoneData.originalType = originalFile.type;
reader.onload = e => { reader.onload = e => {
window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ]) window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ])
.then(key => { .then(key => {
jsKey = key; jsKey = key;
// we register the key somwhere // we register the key somwhere
@ -90,34 +89,34 @@ var encryptFile = function(originalFile, zoneData, done) {
rawKey: rawKey, rawKey: rawKey,
iv: iv iv: iv
}; };
done(new File( [ encrypted ], zoneData.suffix)); done(new File( [ encrypted ], zoneData.suffix));
}); });
}; };
reader.readAsArrayBuffer(originalFile); reader.readAsArrayBuffer(originalFile);
}; };
var addBelowButton = (btn, zone, zoneData) => { var addBelowButton = (btn, zone, zoneData) => {
let let
belowZone = zone.querySelector('.chill-dropzone__below-zone'); belowZone = zone.querySelector('.chill-dropzone__below-zone');
if (belowZone === null) { if (belowZone === null) {
belowZone = document.createElement('div'); belowZone = document.createElement('div');
belowZone.classList.add('chill-dropzone__below-zone'); belowZone.classList.add('chill-dropzone__below-zone');
zone.appendChild(belowZone); zone.appendChild(belowZone);
} }
belowZone.appendChild(btn); belowZone.appendChild(btn);
}; };
var createZone = (zone, zoneData) => { var createZone = (zone, zoneData) => {
var var
created = document.createElement('div'), created = document.createElement('div'),
initMessage = document.createElement('div'), initMessage = document.createElement('div'),
initContent = zone.dataset.labelInitMessage, initContent = zone.dataset.labelInitMessage,
dropzoneI; dropzoneI;
created.classList.add('dropzone'); created.classList.add('dropzone');
initMessage.classList.add('dz-message'); initMessage.classList.add('dz-message');
initMessage.appendChild(document.createTextNode(initContent)); initMessage.appendChild(document.createTextNode(initContent));
@ -142,7 +141,7 @@ var createZone = (zone, zoneData) => {
return zoneData.suffix; return zoneData.suffix;
} }
}); });
dropzoneI.on("sending", function(file, xhr, formData) { dropzoneI.on("sending", function(file, xhr, formData) {
formData.append("redirect", zoneData.params.redirect); formData.append("redirect", zoneData.params.redirect);
formData.append("max_file_size", zoneData.params.max_file_size); formData.append("max_file_size", zoneData.params.max_file_size);
@ -150,24 +149,24 @@ var createZone = (zone, zoneData) => {
formData.append("expires", zoneData.params.expires); formData.append("expires", zoneData.params.expires);
formData.append("signature", zoneData.params.signature); formData.append("signature", zoneData.params.signature);
}); });
dropzoneI.on("success", function(file, response) { dropzoneI.on("success", function(file, response) {
zoneData.currentFile = file; zoneData.currentFile = file;
storeDataInForm(zone, zoneData); storeDataInForm(zone, zoneData);
}); });
dropzoneI.on("addedfile", function(file) { dropzoneI.on("addedfile", function(file) {
if (zoneData.hasOwnProperty('currentFile')) { if (zoneData.hasOwnProperty('currentFile')) {
dropzoneI.removeFile(zoneData.currentFile); dropzoneI.removeFile(zoneData.currentFile);
} }
}); });
dropzoneI.on("removedfile", function(file) { dropzoneI.on("removedfile", function(file) {
removeDataInForm(zone, zoneData); removeDataInForm(zone, zoneData);
}); });
zone.insertBefore(created, zone.firstChild); zone.insertBefore(created, zone.firstChild);
let event = new CustomEvent("chill_dropzone_initialized", { let event = new CustomEvent("chill_dropzone_initialized", {
detail: { detail: {
dropzone: dropzoneI, dropzone: dropzoneI,
@ -179,7 +178,7 @@ var createZone = (zone, zoneData) => {
var initialize = function(zone) { var initialize = function(zone) {
var var
allowRemove = zone.dataset.allowRemove, allowRemove = zone.dataset.allowRemove,
zoneData = { zone: zone, suffix: createFilename(), allowRemove: allowRemove, old: null } zoneData = { zone: zone, suffix: createFilename(), allowRemove: allowRemove, old: null }
; ;
@ -204,13 +203,13 @@ var createFilename = () => {
}; };
var storeDataInForm = (zone, zoneData) => { var storeDataInForm = (zone, zoneData) => {
var var
inputKey = zone.querySelector('input[data-stored-object-key]'), inputKey = zone.querySelector('input[data-stored-object-key]'),
inputIv = zone.querySelector('input[data-stored-object-iv]'), inputIv = zone.querySelector('input[data-stored-object-iv]'),
inputObject = zone.querySelector('input[data-async-file-upload]'), inputObject = zone.querySelector('input[data-async-file-upload]'),
inputType = zone.querySelector('input[data-async-file-type]') inputType = zone.querySelector('input[data-async-file-type]')
; ;
inputKey.value = JSON.stringify(zoneData.crypto.rawKey); inputKey.value = JSON.stringify(zoneData.crypto.rawKey);
inputIv.value = JSON.stringify(Array.from(zoneData.crypto.iv)); inputIv.value = JSON.stringify(Array.from(zoneData.crypto.iv));
inputType.value = zoneData.originalType; inputType.value = zoneData.originalType;
@ -220,18 +219,18 @@ var storeDataInForm = (zone, zoneData) => {
}; };
const restoreDataInForm = (zone, zoneData) => { const restoreDataInForm = (zone, zoneData) => {
var var
inputKey = zone.querySelector('input[data-stored-object-key]'), inputKey = zone.querySelector('input[data-stored-object-key]'),
inputIv = zone.querySelector('input[data-stored-object-iv]'), inputIv = zone.querySelector('input[data-stored-object-iv]'),
inputObject = zone.querySelector('input[data-async-file-upload]'), inputObject = zone.querySelector('input[data-async-file-upload]'),
inputType = zone.querySelector('input[data-async-file-type]') inputType = zone.querySelector('input[data-async-file-type]')
; ;
if (zoneData.old === null) { if (zoneData.old === null) {
console.log('should not have restored data'); console.log('should not have restored data');
return; return;
} }
inputKey.value = zoneData.old.key; inputKey.value = zoneData.old.key;
inputIv.value = zoneData.old.iv; inputIv.value = zoneData.old.iv;
inputType.value = zoneData.old.type; inputType.value = zoneData.old.type;
@ -241,21 +240,21 @@ const restoreDataInForm = (zone, zoneData) => {
}; };
const hasDataInForm = (zone, zoneData) => { const hasDataInForm = (zone, zoneData) => {
var var
inputObject = zone.querySelector('input[data-async-file-upload]') inputObject = zone.querySelector('input[data-async-file-upload]')
; ;
return inputObject.value.length > 0; return inputObject.value.length > 0;
}; };
var removeDataInForm = (zone, zoneData) => { var removeDataInForm = (zone, zoneData) => {
var var
inputKey = zone.querySelector('input[data-stored-object-key]'), inputKey = zone.querySelector('input[data-stored-object-key]'),
inputIv = zone.querySelector('input[data-stored-object-iv]'), inputIv = zone.querySelector('input[data-stored-object-iv]'),
inputObject = zone.querySelector('input[data-async-file-upload]'), inputObject = zone.querySelector('input[data-async-file-upload]'),
inputType = zone.querySelector('input[data-async-file-type]') inputType = zone.querySelector('input[data-async-file-type]')
; ;
// store data for future usage // store data for future usage
zoneData.old = { zoneData.old = {
key: inputKey.value, key: inputKey.value,
@ -268,7 +267,7 @@ var removeDataInForm = (zone, zoneData) => {
inputIv.value = ""; inputIv.value = "";
inputType.value = ""; inputType.value = "";
inputObject.value = ""; inputObject.value = "";
insertDownloadButton(zone); insertDownloadButton(zone);
}; };
@ -279,25 +278,25 @@ var insertRemoveButton = (zone, zoneData) => {
labelRemove = zone.dataset.dictRemove, labelRemove = zone.dataset.dictRemove,
labelCancel = 'Restaurer' labelCancel = 'Restaurer'
; ;
removeButton.classList.add('btn', 'btn-delete'); removeButton.classList.add('btn', 'btn-delete');
removeButton.textContent = labelRemove; removeButton.textContent = labelRemove;
cancelButton.classList.add('btn', 'btn-cancel'); cancelButton.classList.add('btn', 'btn-cancel');
cancelButton.textContent = labelCancel; cancelButton.textContent = labelCancel;
removeButton.addEventListener('click', (e) => { removeButton.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
if (zoneData.allowRemove === 'true') { if (zoneData.allowRemove === 'true') {
removeDataInForm(zone, zoneData); removeDataInForm(zone, zoneData);
cancelButton.addEventListener('click', (e) => { cancelButton.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
restoreDataInForm(zone, zoneData); restoreDataInForm(zone, zoneData);
cancelButton.remove(); cancelButton.remove();
zone.querySelector('.dropzone').remove(); zone.querySelector('.dropzone').remove();
initialize(zone); initialize(zone);
}); });
} }
@ -306,16 +305,16 @@ var insertRemoveButton = (zone, zoneData) => {
removeButton.remove(); removeButton.remove();
createZone(zone, zoneData); createZone(zone, zoneData);
}); });
addBelowButton(removeButton, zone, zoneData); addBelowButton(removeButton, zone, zoneData);
// zone.appendChild(removeButton); // zone.appendChild(removeButton);
}; };
const removeDownloadButton = (zone, zoneData) => { const removeDownloadButton = (zone, zoneData) => {
var var
existingButtons = zone.querySelectorAll('a[data-download-button]') existingButtons = zone.querySelectorAll('a[data-download-button]')
; ;
// remove existing // remove existing
existingButtons.forEach(function(b) { existingButtons.forEach(function(b) {
b.remove(); b.remove();
@ -323,7 +322,7 @@ const removeDownloadButton = (zone, zoneData) => {
}; };
var insertDownloadButton = (zone, zoneData) => { var insertDownloadButton = (zone, zoneData) => {
var var
existingButtons = zone.querySelectorAll('a[data-download-button]'), existingButtons = zone.querySelectorAll('a[data-download-button]'),
newButton = document.createElement('a'), newButton = document.createElement('a'),
inputKey = zone.querySelector('input[data-stored-object-key]'), inputKey = zone.querySelector('input[data-stored-object-key]'),
@ -336,18 +335,18 @@ var insertDownloadButton = (zone, zoneData) => {
tempUrlGenerator = zone.dataset.tempUrlGenerator, tempUrlGenerator = zone.dataset.tempUrlGenerator,
tempUrlGeneratorParams = new URLSearchParams() tempUrlGeneratorParams = new URLSearchParams()
; ;
// remove existing // remove existing
existingButtons.forEach(function(b) { existingButtons.forEach(function(b) {
b.remove(); b.remove();
}); });
if (inputObject.value === '') { if (inputObject.value === '') {
return; return;
} }
tempUrlGeneratorParams.append('object_name', inputObject.value); tempUrlGeneratorParams.append('object_name', inputObject.value);
newButton.dataset.downloadButton = true; newButton.dataset.downloadButton = true;
newButton.dataset.key = inputKey.value; newButton.dataset.key = inputKey.value;
newButton.dataset.iv = inputIv.value; newButton.dataset.iv = inputIv.value;
@ -357,7 +356,7 @@ var insertDownloadButton = (zone, zoneData) => {
newButton.dataset.tempUrlGetGenerator = tempUrlGenerator + '?' + tempUrlGeneratorParams.toString(); newButton.dataset.tempUrlGetGenerator = tempUrlGenerator + '?' + tempUrlGeneratorParams.toString();
newButton.classList.add('btn', 'btn-download', 'dz-bt-below-dropzone'); newButton.classList.add('btn', 'btn-download', 'dz-bt-below-dropzone');
newButton.textContent = labelQuietButton; newButton.textContent = labelQuietButton;
addBelowButton(newButton, zone, zoneData); addBelowButton(newButton, zone, zoneData);
//zone.appendChild(newButton); //zone.appendChild(newButton);
initializeDownload(zone); initializeDownload(zone);
@ -370,3 +369,5 @@ window.addEventListener('load', function(e) {
window.addEventListener('collection-add-entry', function(e) { window.addEventListener('collection-add-entry', function(e) {
searchForZones(e.detail.entry); searchForZones(e.detail.entry);
}); });
export { searchForZones };

View File

@ -0,0 +1,157 @@
<template>
<a class="btn btn-create" :title="$t(buttonTitle)" @click="openModal">
<span>{{ $t(buttonTitle) }}</span>
</a>
<teleport to="body">
<div>
<modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false">
<template v-slot:header>
{{ $t('upload_a_document') }}
</template>
<template v-slot:body>
<div id="dropZoneWrapper" ref="dropZoneWrapper">
<div
data-stored-object="data-stored-object"
:data-label-preparing="$t('data_label_preparing')"
:data-label-quiet-button="$t('data_label_quiet_button')"
:data-label-ready="$t('data_label_ready')"
:data-dict-file-too-big="$t('data_dict_file_too_big')"
:data-dict-default-message="$t('data_dict_default_message')"
:data-dict-remove-file="$t('data_dict_remove_file')"
:data-dict-max-files-exceeded="$t('data_dict_max_files_exceeded')"
:data-dict-cancel-upload="$t('data_dict_cancel_upload')"
:data-dict-cancel-upload-confirm="$t('data_dict_cancel_upload_confirm')"
:data-dict-upload-canceled="$t('data_dict_upload_canceled')"
:data-dict-remove="$t('data_dict_remove')"
:data-allow-remove="!options.required"
data-temp-url-generator="/asyncupload/temp_url/generate/GET">
<input
type="hidden"
data-async-file-upload="data-async-file-upload"
data-generate-temp-url-post="/asyncupload/temp_url/generate/post?expires_delay=180&amp;submit_delay=3600"
data-temp-url-get="/asyncupload/temp_url/generate/GET"
:data-max-files="options.maxFiles"
:data-max-post-size="options.maxPostSize"
:v-model="dataAsyncFileUpload"
>
<input
type="hidden"
data-stored-object-key="1"
>
<input
type="hidden"
data-stored-object-iv="1"
>
<input
type="hidden"
data-async-file-type="1"
>
</div>
</div>
</template>
<template v-slot:footer>
<button class="btn btn-create"
@click.prevent="saveDocument">
{{ $t('action.add')}}
</button>
</template>
</modal>
</div>
</teleport>
</template>
<script>
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import { searchForZones } from '../../module/async_upload/uploader';
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
const i18n = {
messages: {
fr: {
upload_a_document: "Téléversez un document",
data_label_preparing: "Chargement...",
data_label_quiet_button: "Téléchargez le fichier existant",
data_label_ready: "Prêt à montrer",
data_dict_file_too_big: "Fichier trop volumineux",
data_dict_default_message: "Glissez votre fichier ou cliquez ici",
data_dict_remove_file: "Enlevez votre fichier pour en téléversez un autre",
data_dict_max_files_exceeded: "Nombre maximum de fichiers atteint. Enlevez les fichiers précédents",
data_dict_cancel_upload: "Annulez le téléversement",
data_dict_cancel_upload_confirm: "Êtes-vous sûr·e de vouloir annuler ce téléversement?",
data_dict_upload_canceled: "Téléversement annulé",
data_dict_remove: "Enlevez le fichier existant",
}
}
};
export default {
name: "AddAsyncUpload",
components: {
Modal
},
i18n,
props: [
'buttonTitle',
'options'
],
emits: ['addDocument'],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-centered modal-md"
},
}
},
updated() {
if (this.modal.showModal){
searchForZones(this.$refs.dropZoneWrapper);
}
},
methods: {
openModal() {
this.modal.showModal = true;
},
saveDocument() {
const dropzone = this.$refs.dropZoneWrapper;
if (dropzone) {
const inputKey = dropzone.querySelector('input[data-stored-object-key]');
const inputIv = dropzone.querySelector('input[data-stored-object-iv]');
const inputObject = dropzone.querySelector('input[data-async-file-upload]');
const inputType = dropzone.querySelector('input[data-async-file-type]');
const url = '/api/1.0/docstore/stored-object.json';
const body = {
filename: inputObject.value,
keyInfos: JSON.parse(inputKey.value),
iv: JSON.parse(inputIv.value),
type: inputType.value,
};
makeFetch('POST', url, body)
.then(r => {
this.$emit("addDocument", r);
this.modal.showModal = false;
})
.catch((error) => {
if (error.name === 'ValidationException') {
for (let v of error.violations) {
this.$toast.open({message: v });
}
} else {
console.error(error);
this.$toast.open({message: 'An error occurred'});
}
});
} else {
this.$toast.open({message: 'An error occurred - drop zone not found'});
}
}
}
}
</script>

View File

@ -1,19 +0,0 @@
{% import '@ChillMain/Workflow/macro_breadcrumb.html.twig' as m %}
<div class="flex-grow-1 {% if add_classes is defined %}{{ add_classes }}{% else %}h2{% endif %}">
<div>
{% if concerne is defined and concerne == true %}
<span class="item-key">{{ 'Concerne'|trans }}: </span>
{% endif %}
{{ 'workflow.Document (n°%doc%)'|trans({'%doc%': document.id}) }}
{% if description is defined and description == true %}
{{ ' — ' ~ document.title }}
{% endif %}
</div>
{% if breadcrumb is defined and breadcrumb == true %}
{{ m.breadcrumb(_context) }}
{% endif %}
</div>

View File

@ -10,16 +10,18 @@
{{ parent() }} {{ parent() }}
{{ encore_entry_script_tags('mod_async_upload') }} {{ encore_entry_script_tags('mod_async_upload') }}
{{ encore_entry_script_tags('mod_docgen_picktemplate') }} {{ encore_entry_script_tags('mod_docgen_picktemplate') }}
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ encore_entry_link_tags('mod_async_upload') }} {{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_link_tags('mod_docgen_picktemplate') }} {{ encore_entry_link_tags('mod_docgen_picktemplate') }}
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="col-md-10 col-xxl"> <div class="document-list">
<h1>{{ 'Documents' }}</h1> <h1>{{ 'Documents' }}</h1>
{% if documents|length == 0 %} {% if documents|length == 0 %}
@ -27,7 +29,7 @@
{% else %} {% else %}
<div class="flex-table chill-task-list"> <div class="flex-table chill-task-list">
{% for document in documents %} {% for document in documents %}
{% include 'ChillDocStoreBundle:List:list_item.html.twig' %} {% include '@ChillDocStore/List/list_item.html.twig' %}
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}

View File

@ -48,6 +48,9 @@
<div> <div>
{% if document.course is defined %} {% if document.course is defined %}
<ul class="record_actions"> <ul class="record_actions">
<li>
{{ chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) }}
</li>
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS', document) %} {% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS', document) %}
<li> <li>
{{ m.download_button(document.object, document.title) }} {{ m.download_button(document.object, document.title) }}

View File

@ -0,0 +1,55 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\DocStoreBundle\Serializer\Normalizer;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
use function array_key_exists;
use function is_array;
class StoredObjectDenormalizer implements DenormalizerInterface
{
use ObjectToPopulateTrait;
private StoredObjectRepository $storedObjectRepository;
public function __construct(StoredObjectRepository $storedObjectRepository)
{
$this->storedObjectRepository = $storedObjectRepository;
}
public function denormalize($data, $type, $format = null, array $context = [])
{
$object = $this->extractObjectToPopulate(StoredObject::class, $context);
if (null !== $object) {
return $object;
}
return $this->storedObjectRepository->find($data['id']);
}
public function supportsDenormalization($data, $type, $format = null)
{
if (false === is_array($data)) {
return false;
}
if (false === array_key_exists('id', $data)) {
return false;
}
return StoredObject::class === $type;
}
}

View File

@ -28,8 +28,10 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
/** /**
* TODO: injecter le repository directement. * TODO: injecter le repository directement.
*/ */
public function __construct(EntityManagerInterface $em, TranslatorInterface $translator) public function __construct(
{ EntityManagerInterface $em,
TranslatorInterface $translator
) {
$this->repository = $em->getRepository(AccompanyingCourseDocument::class); $this->repository = $em->getRepository(AccompanyingCourseDocument::class);
$this->translator = $translator; $this->translator = $translator;
} }
@ -53,7 +55,10 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string
{ {
return $this->translator->trans('workflow.Document (n°%doc%)', ['%doc%' => $entityWorkflow->getRelatedEntityId()]); $doc = $this->getRelatedEntity($entityWorkflow);
return $this->translator->trans('workflow.Document (n°%doc%)', ['%doc%' => $entityWorkflow->getRelatedEntityId()])
. ' - ' . $doc->getTitle();
} }
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingCourseDocument public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingCourseDocument
@ -79,16 +84,6 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
]; ];
} }
public function getTemplateTitle(EntityWorkflow $entityWorkflow, array $options = []): string
{
return '@ChillDocStore/AccompanyingCourseDocument/_workflow.title.html.twig';
}
public function getTemplateTitleData(EntityWorkflow $entityWorkflow, array $options = []): array
{
return $this->getTemplateData($entityWorkflow, $options);
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{ {
return $entityWorkflow->getRelatedEntityClass() === AccompanyingCourseDocument::class; return $entityWorkflow->getRelatedEntityClass() === AccompanyingCourseDocument::class;

View File

@ -0,0 +1,46 @@
---
openapi: "3.0.0"
info:
version: "1.0.0"
title: "Chill api"
description: "Api documentation for chill. Currently, work in progress"
servers:
- url: "/api"
description: "Your current dev server"
components:
schemas:
StoredObject:
type: object
properties:
id:
type: integer
filename:
type: string
type:
type: string
paths:
/1.0/docstore/stored-object.json:
post:
tags:
- storedobject
summary: Create a stored object
requestBody:
description: "A stored object"
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/StoredObject"
responses:
200:
description: "OK"
content:
application/json:
schema:
$ref: "#/components/schemas/StoredObject"
403:
description: "Unauthorized"
422:
description: "Invalid data"

View File

@ -1,4 +1,7 @@
module.exports = function(encore) module.exports = function(encore)
{ {
encore.addAliases({
ChillDocStoreAssets: __dirname + '/Resources/public'
});
encore.addEntry('mod_async_upload', __dirname + '/Resources/public/module/async_upload/index.js'); encore.addEntry('mod_async_upload', __dirname + '/Resources/public/module/async_upload/index.js');
}; };

View File

@ -33,3 +33,10 @@ services:
resource: './../Workflow/' resource: './../Workflow/'
autoconfigure: true autoconfigure: true
autowire: true autowire: true
Chill\DocStoreBundle\Serializer\Normalizer\:
autowire: true
resource: '../Serializer/Normalizer/'
tags:
- { name: 'serializer.normalizer', priority: 16 }

View File

@ -29,7 +29,6 @@ use Chill\MainBundle\Security\ProvideRoleInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverInterface; use Chill\MainBundle\Security\Resolver\CenterResolverInterface;
use Chill\MainBundle\Security\Resolver\ScopeResolverInterface; use Chill\MainBundle\Security\Resolver\ScopeResolverInterface;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface; use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
use Chill\MainBundle\Templating\Entity\CompilerPass as RenderEntityCompilerPass;
use Chill\MainBundle\Templating\UI\NotificationCounterInterface; use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface; use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -70,7 +69,6 @@ class ChillMainBundle extends Bundle
$container->addCompilerPass(new MenuCompilerPass()); $container->addCompilerPass(new MenuCompilerPass());
$container->addCompilerPass(new ACLFlagsCompilerPass()); $container->addCompilerPass(new ACLFlagsCompilerPass());
$container->addCompilerPass(new GroupingCenterCompilerPass()); $container->addCompilerPass(new GroupingCenterCompilerPass());
$container->addCompilerPass(new RenderEntityCompilerPass());
$container->addCompilerPass(new CRUDControllerCompilerPass()); $container->addCompilerPass(new CRUDControllerCompilerPass());
} }
} }

View File

@ -0,0 +1,24 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
class CivilityApiController extends ApiController
{
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
{
return $query->addOrderBy('e.order', 'ASC');
}
}

View File

@ -11,21 +11,26 @@ declare(strict_types=1);
namespace Chill\MainBundle\Controller; namespace Chill\MainBundle\Controller;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowComment; use Chill\MainBundle\Entity\Workflow\EntityWorkflowComment;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
use Chill\MainBundle\Form\EntityWorkflowCommentType; use Chill\MainBundle\Form\EntityWorkflowCommentType;
use Chill\MainBundle\Form\WorkflowStepType; use Chill\MainBundle\Form\WorkflowStepType;
use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository; use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter; use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter;
use Chill\MainBundle\Workflow\EntityWorkflowManager; use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\Validator\StepDestValid;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\TransitionBlocker; use Symfony\Component\Workflow\TransitionBlocker;
@ -80,7 +85,9 @@ class WorkflowController extends AbstractController
$entityWorkflow $entityWorkflow
->setRelatedEntityClass($request->query->get('entityClass')) ->setRelatedEntityClass($request->query->get('entityClass'))
->setRelatedEntityId($request->query->getInt('entityId')) ->setRelatedEntityId($request->query->getInt('entityId'))
->setWorkflowName($request->query->get('workflow')); ->setWorkflowName($request->query->get('workflow'))
->addSubscriberToStep($this->getUser())
->addSubscriberToFinal($this->getUser());
$errors = $this->validator->validate($entityWorkflow, null, ['creation']); $errors = $this->validator->validate($entityWorkflow, null, ['creation']);
@ -104,6 +111,121 @@ class WorkflowController extends AbstractController
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
} }
/**
* @Route("/{_locale}/main/workflow/{id}/delete", name="chill_main_workflow_delete")
*/
public function delete(EntityWorkflow $entityWorkflow, Request $request): Response
{
$this->denyAccessUnlessGranted(EntityWorkflowVoter::DELETE, $entityWorkflow);
$form = $this->createForm(FormType::class);
$form->add('submit', SubmitType::class, ['label' => 'workflow.Delete workflow']);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->remove($entityWorkflow);
$this->entityManager->flush();
$this->addFlash('success', $this->translator->trans('workflow.Workflow deleted with success'));
return $this->redirectToRoute('chill_main_homepage');
}
return $this->render('@ChillMain/Workflow/delete.html.twig', [
'entityWorkflow' => $entityWorkflow,
'delete_form' => $form->createView(),
'handler' => $this->entityWorkflowManager->getHandler($entityWorkflow),
]);
}
/**
* @Route("/{_locale}/main/workflow-step/{id}/access_key", name="chill_main_workflow_grant_access_by_key")
*/
public function getAccessByAccessKey(EntityWorkflowStep $entityWorkflowStep, Request $request): Response
{
if (null === $accessKey = $request->query->get('accessKey', null)) {
throw new BadRequestException('accessKey is missing');
}
if (!$this->getUser() instanceof User) {
throw new AccessDeniedException('Not a valid user');
}
if ($entityWorkflowStep->getAccessKey() !== $accessKey) {
throw new AccessDeniedException('Access key is invalid');
}
if (!$entityWorkflowStep->isWaitingForTransition()) {
$this->addFlash('error', $this->translator->trans('workflow.Steps is not waiting for transition. Maybe someone apply the transition before you ?'));
} else {
$entityWorkflowStep->addDestUserByAccessKey($this->getUser());
$this->entityManager->flush();
$this->addFlash('success', $this->translator->trans('workflow.You get access to this step'));
}
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflowStep->getEntityWorkflow()->getId()]);
}
/**
* Previous workflows where the user has applyed a transition.
*
* @Route("/{_locale}/main/workflow/list/previous_transitionned", name="chill_main_workflow_list_previous_transitionned")
*/
public function myPreviousWorkflowsTransitionned(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
$total = $this->entityWorkflowRepository->countByPreviousTransitionned($this->getUser());
$paginator = $this->paginatorFactory->create($total);
$workflows = $this->entityWorkflowRepository->findByPreviousTransitionned(
$this->getUser(),
['createdAt' => 'DESC'],
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
return $this->render(
'@ChillMain/Workflow/list.html.twig',
[
'help' => 'workflow.Previous workflow transitionned help',
'workflows' => $this->buildHandler($workflows),
'paginator' => $paginator,
'step' => 'previous_transitionned',
]
);
}
/**
* Previous workflows where the user was mentioned, but did not give any reaction.
*
* @Route("/{_locale}/main/workflow/list/previous_without_reaction", name="chill_main_workflow_list_previous_without_reaction")
*/
public function myPreviousWorkflowsWithoutReaction(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
$total = $this->entityWorkflowRepository->countByPreviousDestWithoutReaction($this->getUser());
$paginator = $this->paginatorFactory->create($total);
$workflows = $this->entityWorkflowRepository->findByPreviousDestWithoutReaction(
$this->getUser(),
['createdAt' => 'DESC'],
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
return $this->render(
'@ChillMain/Workflow/list.html.twig',
[
'help' => 'workflow.Previous workflow without reaction help',
'workflows' => $this->buildHandler($workflows),
'paginator' => $paginator,
'step' => 'previous_without_reaction',
]
);
}
/** /**
* @Route("/{_locale}/main/workflow/list/dest", name="chill_main_workflow_list_dest") * @Route("/{_locale}/main/workflow/list/dest", name="chill_main_workflow_list_dest")
*/ */
@ -198,6 +320,7 @@ class WorkflowController extends AbstractController
} }
$entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData(); $entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData();
$entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData();
$workflow->apply($entityWorkflow, $transition); $workflow->apply($entityWorkflow, $transition);
@ -205,18 +328,13 @@ class WorkflowController extends AbstractController
$entityWorkflow->getCurrentStep()->addDestUser($user); $entityWorkflow->getCurrentStep()->addDestUser($user);
} }
$errors = $this->validator->validate( foreach ($transitionForm['future_dest_emails']->getData() as $email) {
$entityWorkflow->getCurrentStep(), $entityWorkflow->getCurrentStep()->addDestEmail($email);
new StepDestValid()
);
if (count($errors) === 0) {
$this->entityManager->flush();
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
} }
return new Response((string) $errors, Response::HTTP_UNPROCESSABLE_ENTITY); $this->entityManager->flush();
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
} }
if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) { if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) {
@ -243,8 +361,8 @@ class WorkflowController extends AbstractController
return $this->render( return $this->render(
'@ChillMain/Workflow/index.html.twig', '@ChillMain/Workflow/index.html.twig',
[ [
'handler' => $handler,
'handler_template' => $handler->getTemplate($entityWorkflow), 'handler_template' => $handler->getTemplate($entityWorkflow),
'handler_template_title' => $handler->getTemplateTitle($entityWorkflow),
'handler_template_data' => $handler->getTemplateData($entityWorkflow), 'handler_template_data' => $handler->getTemplateData($entityWorkflow),
'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null, 'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null,
'entity_workflow' => $entityWorkflow, 'entity_workflow' => $entityWorkflow,

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\MainBundle\DependencyInjection; namespace Chill\MainBundle\DependencyInjection;
use Chill\MainBundle\Controller\AddressApiController; use Chill\MainBundle\Controller\AddressApiController;
use Chill\MainBundle\Controller\CivilityApiController;
use Chill\MainBundle\Controller\LocationController; use Chill\MainBundle\Controller\LocationController;
use Chill\MainBundle\Controller\LocationTypeController; use Chill\MainBundle\Controller\LocationTypeController;
use Chill\MainBundle\Controller\UserController; use Chill\MainBundle\Controller\UserController;
@ -559,6 +560,21 @@ class ChillMainExtension extends Extension implements
], ],
], ],
], ],
[
'class' => \Chill\MainBundle\Entity\Civility::class,
'name' => 'civility',
'base_path' => '/api/1.0/main/civility',
'base_role' => 'ROLE_USER',
'controller' => CivilityApiController::class,
'actions' => [
'_index' => [
'methods' => [
Request::METHOD_GET => true,
Request::METHOD_HEAD => true,
],
],
],
],
], ],
]); ]);
} }

View File

@ -17,6 +17,7 @@ use Symfony\Component\Serializer\Annotation as Serializer;
/** /**
* @ORM\Table(name="chill_main_civility") * @ORM\Table(name="chill_main_civility")
* @ORM\Entity * @ORM\Entity
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={"chill_main_civility": Civility::class})
*/ */
class Civility class Civility
{ {
@ -29,6 +30,7 @@ class Civility
/** /**
* @ORM\Column(type="boolean") * @ORM\Column(type="boolean")
* @Serializer\Groups({"read"})
*/ */
private bool $active = true; private bool $active = true;

View File

@ -41,6 +41,17 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
use TrackUpdateTrait; use TrackUpdateTrait;
/**
* a list of future dest emails for the next steps.
*
* This is in used in order to let controller inform who will be the future emails which will validate
* the next step. This is necessary to perform some computation about the next emails, before they are
* associated to the entity EntityWorkflowStep.
*
* @var array|string[]
*/
public array $futureDestEmails = [];
/** /**
* a list of future dest users for the next steps. * a list of future dest users for the next steps.
* *

View File

@ -27,6 +27,11 @@ use function in_array;
*/ */
class EntityWorkflowStep class EntityWorkflowStep
{ {
/**
* @ORM\Column(type="text", nullable=false)
*/
private string $accessKey;
/** /**
* @ORM\Column(type="text", options={"default": ""}) * @ORM\Column(type="text", options={"default": ""})
*/ */
@ -48,6 +53,12 @@ class EntityWorkflowStep
*/ */
private Collection $destUser; private Collection $destUser;
/**
* @ORM\ManyToMany(targetEntity=User::class)
* @ORM\JoinTable(name="chill_main_workflow_entity_step_user_by_accesskey")
*/
private Collection $destUserByAccessKey;
/** /**
* @ORM\ManyToOne(targetEntity=EntityWorkflow::class, inversedBy="steps") * @ORM\ManyToOne(targetEntity=EntityWorkflow::class, inversedBy="steps")
*/ */
@ -86,7 +97,7 @@ class EntityWorkflowStep
private ?string $transitionAfter = null; private ?string $transitionAfter = null;
/** /**
* @ORM\Column(type="datetime_immutable") * @ORM\Column(type="datetime_immutable", nullable=true, options={"default": null})
*/ */
private ?DateTimeImmutable $transitionAt = null; private ?DateTimeImmutable $transitionAt = null;
@ -104,6 +115,8 @@ class EntityWorkflowStep
public function __construct() public function __construct()
{ {
$this->destUser = new ArrayCollection(); $this->destUser = new ArrayCollection();
$this->destUserByAccessKey = new ArrayCollection();
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
} }
public function addDestEmail(string $email): self public function addDestEmail(string $email): self
@ -119,11 +132,45 @@ class EntityWorkflowStep
{ {
if (!$this->destUser->contains($user)) { if (!$this->destUser->contains($user)) {
$this->destUser[] = $user; $this->destUser[] = $user;
$this->getEntityWorkflow()
->addSubscriberToFinal($user)
->addSubscriberToStep($user);
} }
return $this; return $this;
} }
public function addDestUserByAccessKey(User $user): self
{
if (!$this->destUserByAccessKey->contains($user) && !$this->destUser->contains($user)) {
$this->destUserByAccessKey[] = $user;
$this->getEntityWorkflow()
->addSubscriberToFinal($user)
->addSubscriberToStep($user);
}
return $this;
}
public function getAccessKey(): string
{
return $this->accessKey;
}
/**
* get all the users which are allowed to apply a transition: those added manually, and
* those added automatically bu using an access key.
*/
public function getAllDestUser(): Collection
{
return new ArrayCollection(
[
...$this->getDestUser()->toArray(),
...$this->getDestUserByAccessKey()->toArray(),
]
);
}
public function getComment(): string public function getComment(): string
{ {
return $this->comment; return $this->comment;
@ -140,13 +187,21 @@ class EntityWorkflowStep
} }
/** /**
* @return ArrayCollection|Collection * get dest users added by the creator.
*
* You should **not** rely on this method to get all users which are able to
* apply a transition on this step. Use @see{EntityWorkflowStep::getAllDestUser} instead.
*/ */
public function getDestUser() public function getDestUser(): collection
{ {
return $this->destUser; return $this->destUser;
} }
public function getDestUserByAccessKey(): Collection
{
return $this->destUserByAccessKey;
}
public function getEntityWorkflow(): ?EntityWorkflow public function getEntityWorkflow(): ?EntityWorkflow
{ {
return $this->entityWorkflow; return $this->entityWorkflow;
@ -197,6 +252,19 @@ class EntityWorkflowStep
return $this->freezeAfter; return $this->freezeAfter;
} }
public function isWaitingForTransition(): bool
{
if (null !== $this->transitionAfter) {
return false;
}
if ($this->isFinal()) {
return false;
}
return true;
}
public function removeDestEmail(string $email): self public function removeDestEmail(string $email): self
{ {
$this->destEmail = array_filter($this->destEmail, static function (string $existing) use ($email) { $this->destEmail = array_filter($this->destEmail, static function (string $existing) use ($email) {
@ -213,6 +281,13 @@ class EntityWorkflowStep
return $this; return $this;
} }
public function removeDestUserByAccessKey(User $user): self
{
$this->destUserByAccessKey->removeElement($user);
return $this;
}
public function setComment(?string $comment): EntityWorkflowStep public function setComment(?string $comment): EntityWorkflowStep
{ {
$this->comment = (string) $comment; $this->comment = (string) $comment;

View File

@ -0,0 +1,57 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Form\Type;
use Chill\MainBundle\Entity\Civility;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PickCivilityType extends AbstractType
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('label', 'Civility')
->setDefault(
'choice_label',
function (Civility $civility): string {
return $this->translatableStringHelper->localize($civility->getName());
}
)
->setDefault(
'query_builder',
static function (EntityRepository $er): QueryBuilder {
return $er->createQueryBuilder('c')
->where('c.active = true')
->orderBy('c.order');
},
)
->setDefault('placeholder', 'choose civility')
->setDefault('class', Civility::class);
}
public function getParent()
{
return EntityType::class;
}
}

View File

@ -13,6 +13,7 @@ namespace Chill\MainBundle\Form;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
use Chill\MainBundle\Form\Type\ChillCollectionType;
use Chill\MainBundle\Form\Type\ChillTextareaType; use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\PickUserDynamicType; use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
@ -21,8 +22,14 @@ use LogicException;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\Transition; use Symfony\Component\Workflow\Transition;
use function array_key_exists; use function array_key_exists;
@ -146,6 +153,22 @@ class WorkflowStepType extends AbstractType
'label' => 'workflow.dest for next steps', 'label' => 'workflow.dest for next steps',
'multiple' => true, 'multiple' => true,
'mapped' => false, 'mapped' => false,
])
->add('future_dest_emails', ChillCollectionType::class, [
'label' => 'workflow.dest by email',
'help' => 'workflow.dest by email help',
'mapped' => false,
'allow_add' => true,
'entry_type' => EmailType::class,
'button_add_label' => 'workflow.Add an email',
'button_remove_label' => 'workflow.Remove an email',
'empty_collection_explain' => 'workflow.Any email',
'entry_options' => [
'constraints' => [
new NotNull(), new NotBlank(), new Email(['checkMX' => true]),
],
'label' => 'Email',
],
]); ]);
} }
@ -175,6 +198,37 @@ class WorkflowStepType extends AbstractType
->setRequired('transition') ->setRequired('transition')
->setAllowedTypes('transition', 'bool') ->setAllowedTypes('transition', 'bool')
->setRequired('entity_workflow') ->setRequired('entity_workflow')
->setAllowedTypes('entity_workflow', EntityWorkflow::class); ->setAllowedTypes('entity_workflow', EntityWorkflow::class)
->setDefault('constraints', [
new Callback(
function ($step, ExecutionContextInterface $context, $payload) {
/** @var EntityWorkflowStep $step */
$form = $context->getObject();
$workflow = $this->registry->get($step->getEntityWorkflow(), $step->getEntityWorkflow()->getWorkflowName());
$transition = $form['transition']->getData();
$toFinal = true;
foreach ($transition->getTos() as $to) {
$meta = $workflow->getMetadataStore()->getPlaceMetadata($to);
if (
!array_key_exists('isFinal', $meta) || false === $meta['isFinal']
) {
$toFinal = false;
}
}
$destUsers = $form['future_dest_users']->getData();
$destEmails = $form['future_dest_emails']->getData();
if (!$toFinal && [] === $destUsers && [] === $destEmails) {
$context
->buildViolation('workflow.You must add at least one dest user or email')
->atPath('future_dest_users')
->addViolation();
}
}
),
]);
} }
} }

View File

@ -34,6 +34,20 @@ class EntityWorkflowRepository implements ObjectRepository
return (int) $qb->getQuery()->getSingleScalarResult(); return (int) $qb->getQuery()->getSingleScalarResult();
} }
public function countByPreviousDestWithoutReaction(User $user): int
{
$qb = $this->buildQueryByPreviousDestWithoutReaction($user)->select('count(ew)');
return (int) $qb->getQuery()->getSingleScalarResult();
}
public function countByPreviousTransitionned(User $user): int
{
$qb = $this->buildQueryByPreviousTransitionned($user)->select('count(ew)');
return (int) $qb->getQuery()->getSingleScalarResult();
}
public function countBySubscriber(User $user): int public function countBySubscriber(User $user): int
{ {
$qb = $this->buildQueryBySubscriber($user)->select('count(ew)'); $qb = $this->buildQueryBySubscriber($user)->select('count(ew)');
@ -78,6 +92,32 @@ class EntityWorkflowRepository implements ObjectRepository
return $qb->getQuery()->getResult(); return $qb->getQuery()->getResult();
} }
public function findByPreviousDestWithoutReaction(User $user, ?array $orderBy = null, $limit = null, $offset = null): array
{
$qb = $this->buildQueryByPreviousDestWithoutReaction($user)->select('ew');
foreach ($orderBy as $key => $sort) {
$qb->addOrderBy('ew.' . $key, $sort);
}
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()->getResult();
}
public function findByPreviousTransitionned(User $user, ?array $orderBy = null, $limit = null, $offset = null): array
{
$qb = $this->buildQueryByPreviousTransitionned($user)->select('ew')->distinct(true);
foreach ($orderBy as $key => $sort) {
$qb->addOrderBy('ew.' . $key, $sort);
}
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()->getResult();
}
public function findBySubscriber(User $user, ?array $orderBy = null, $limit = null, $offset = null): array public function findBySubscriber(User $user, ?array $orderBy = null, $limit = null, $offset = null): array
{ {
$qb = $this->buildQueryBySubscriber($user)->select('ew'); $qb = $this->buildQueryBySubscriber($user)->select('ew');
@ -120,6 +160,41 @@ class EntityWorkflowRepository implements ObjectRepository
return $qb; return $qb;
} }
private function buildQueryByPreviousDestWithoutReaction(User $user): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('ew');
$qb->join('ew.steps', 'step');
$qb->where(
$qb->expr()->andX(
$qb->expr()->isMemberOf(':user', 'step.destUser'),
$qb->expr()->neq('step.transitionBy', ':user'),
)
);
$qb->setParameter('user', $user);
return $qb;
}
private function buildQueryByPreviousTransitionned(User $user): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('ew');
$qb->join('ew.steps', 'step');
$qb->where(
$qb->expr()->andX(
$qb->expr()->eq('step.transitionBy', ':user'),
)
);
$qb->setParameter('user', $user);
return $qb;
}
private function buildQueryBySubscriber(User $user): QueryBuilder private function buildQueryBySubscriber(User $user): QueryBuilder
{ {
$qb = $this->repository->createQueryBuilder('ew'); $qb = $this->repository->createQueryBuilder('ew');

View File

@ -72,7 +72,7 @@ section.chill-entity {
} }
} }
p { p {
display: inline-block; // display: inline-block;
margin: 0 0 0 1.5em; margin: 0 0 0 1.5em;
text-indent: -1.5em; text-indent: -1.5em;

View File

@ -3,7 +3,7 @@ import {ShowHide} from 'ChillMainAssets/lib/show_hide/show_hide.js';
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
let let
divTransitions = document.querySelector('#transitions'), divTransitions = document.querySelector('#transitions'),
futureDestUsersContainer = document.querySelector('#futureDestUsers') futureDestUsersContainer = document.querySelector('#futureDests')
; ;
if (null !== divTransitions) { if (null !== divTransitions) {
@ -67,24 +67,4 @@ window.addEventListener('DOMContentLoaded', function() {
}); });
} }
// validate form
let form = document.querySelector('form[name="workflow_step"]');
if (form === null) {
console.error('form to validate not found');
}
form.addEventListener('submit', function (event) {
const datas = new FormData(event.target);
if (datas.has('workflow_step[future_dest_users]')) {
const dests = JSON.parse(datas.get('workflow_step[future_dest_users]'));
if (dests === null || (dests instanceof Array && dests.length === 0)) {
event.preventDefault();
console.log('no users!');
window.alert('Indiquez un utilisateur pour traiter la prochaine étape.');
}
}
});
}); });

View File

@ -77,8 +77,7 @@ export default {
return this.$refs.castPerson.$data.person; return this.$refs.castPerson.$data.person;
case 'thirdparty': case 'thirdparty':
let data = this.$refs.castThirdparty.$data.thirdparty; let data = this.$refs.castThirdparty.$data.thirdparty;
data.name = data.text; if (data.address !== undefined && data.address !== null) {
if (data.address !== null) {
data.address = { id: data.address.address_id } data.address = { id: data.address.address_id }
} else { } else {
data.address = null; data.address = null;

View File

@ -18,7 +18,8 @@
@close="modal.showModal = false"> @close="modal.showModal = false">
<template v-slot:header> <template v-slot:header>
<h3 class="modal-title">{{ $t(titleModal) }}</h3> <h3 v-if="parent" class="modal-title">{{ $t(titleModal, {q: parent.text}) }}</h3>
<h3 v-else class="modal-title">{{ $t(titleModal) }}</h3>
</template> </template>
<template v-slot:body v-if="type === 'person'"> <template v-slot:body v-if="type === 'person'">
@ -51,6 +52,15 @@
</div> </div>
</template> </template>
<template v-slot:body v-else-if="parent">
<on-the-fly-thirdparty
:parent="parent"
:action="action"
type="thirdparty"
ref="castThirdparty">
</on-the-fly-thirdparty>
</template>
<template v-slot:body v-else> <template v-slot:body v-else>
<on-the-fly-create <on-the-fly-create
:action="action" :action="action"
@ -83,6 +93,7 @@ import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
import OnTheFlyCreate from './Create.vue'; import OnTheFlyCreate from './Create.vue';
import OnTheFlyPerson from 'ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue'; import OnTheFlyPerson from 'ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue';
import OnTheFlyThirdparty from 'ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue'; import OnTheFlyThirdparty from 'ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue';
import { postThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly";
export default { export default {
name: 'OnTheFly', name: 'OnTheFly',
@ -118,6 +129,8 @@ export default {
return 'btn-update'; return 'btn-update';
case 'create': case 'create':
return 'btn-create'; return 'btn-create';
case 'addContact':
return 'btn-tpchild';
} }
}, },
titleAction() { titleAction() {
@ -128,6 +141,8 @@ export default {
return 'action.edit'; return 'action.edit';
case 'create': case 'create':
return 'action.create'; return 'action.create';
case 'addContact':
return 'action.addContact';
} }
}, },
titleCreate() { titleCreate() {
@ -145,6 +160,8 @@ export default {
return 'onthefly.edit.' + this.type; return 'onthefly.edit.' + this.type;
case 'create': case 'create':
return this.titleCreate; return this.titleCreate;
case 'addContact':
return 'onthefly.addContact.title';
} }
}, },
titleMessage() { titleMessage() {
@ -178,8 +195,8 @@ export default {
this.modal.showModal = false; this.modal.showModal = false;
}, },
openModal() { openModal() {
//console.log('## OPEN ON THE FLY MODAL'); // console.log('## OPEN ON THE FLY MODAL');
//console.log('## type:', this.type, ', action:', this.action); // console.log('## type:', this.type, ', action:', this.action);
this.modal.showModal = true; this.modal.showModal = true;
this.$nextTick(function() { this.$nextTick(function() {
//this.$refs.search.focus(); //this.$refs.search.focus();
@ -189,7 +206,7 @@ export default {
this.$data.action = action; this.$data.action = action;
}, },
saveAction() { saveAction() {
console.log('saveAction button: create/edit action with', this.type); // console.log('saveAction button: create/edit action with', this.type);
let let
type = this.type, type = this.type,
data = {} ; data = {} ;
@ -201,17 +218,28 @@ export default {
case 'thirdparty': case 'thirdparty':
data = this.$refs.castThirdparty.$data.thirdparty; data = this.$refs.castThirdparty.$data.thirdparty;
/* never executed ? */
break; break;
default: default:
if (typeof this.type === 'undefined') { // action=create if (typeof this.type === 'undefined') { // action=create or addContact
type = this.$refs.castNew.radioType; console.log('will rewrite data');
data = this.$refs.castNew.castDataByType(); if (this.action === 'addContact') {
type = 'thirdparty'
data = this.$refs.castThirdparty.$data.thirdparty;
console.log('data original', data);
data.parent = {type: "thirdparty", id: this.parent.id};
} else {
type = this.$refs.castNew.radioType;
data = this.$refs.castNew.castDataByType();
console.log(data)
}
} else { } else {
throw 'error with object type'; throw 'error with object type';
} }
} }
console.log('type', type);
console.log('data', data);
// pass datas to parent // pass datas to parent
this.$emit('saveFormOnTheFly', { type: type, data: data }); this.$emit('saveFormOnTheFly', { type: type, data: data });
}, },
@ -231,4 +259,8 @@ export default {
a { a {
cursor: pointer; cursor: pointer;
} }
/* .btn-add-contact {
background-color: pink;
} */
</style> </style>

View File

@ -21,7 +21,13 @@ const ontheflyMessages = {
person: "un nouvel usager", person: "un nouvel usager",
thirdparty: "un nouveau tiers professionnel" thirdparty: "un nouveau tiers professionnel"
}, },
resource_comment_title: "Un commentaire est associé à cet interlocuteur" addContact: {
title: "Créer un contact pour {q}"
},
resource_comment_title: "Un commentaire est associé à cet interlocuteur",
addContact: {
title: "Créer un contact pour {q}"
}
} }
} }
} }

View File

@ -5,7 +5,7 @@
<div> <div>
<div class="item-row col"> <div class="item-row col">
<h2>Workflow</h2> <h2>{{ w.title }}</h2>
<div class="flex-grow-1 ms-3 h3"> <div class="flex-grow-1 ms-3 h3">
<div class="visually-hidden"> <div class="visually-hidden">
{{ w.relatedEntityClass }} {{ w.relatedEntityClass }}

View File

@ -12,6 +12,8 @@
:relatedEntityClass="this.relatedEntityClass" :relatedEntityClass="this.relatedEntityClass"
:relatedEntityId="this.relatedEntityId" :relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables" :workflowsAvailables="workflowsAvailables"
:preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate"
:goToGenerateWorkflowPayload="this.goToGenerateWorkflowPayload"
@go-to-generate-workflow="goToGenerateWorkflow" @go-to-generate-workflow="goToGenerateWorkflow"
></pick-workflow> ></pick-workflow>
@ -36,6 +38,7 @@
:relatedEntityId="this.relatedEntityId" :relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables" :workflowsAvailables="workflowsAvailables"
:preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate" :preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate"
:goToGenerateWorkflowPayload="this.goToGenerateWorkflowPayload"
@go-to-generate-workflow="this.goToGenerateWorkflow" @go-to-generate-workflow="this.goToGenerateWorkflow"
></pick-workflow> ></pick-workflow>
</template> </template>
@ -83,6 +86,10 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
goToGenerateWorkflowPayload: {
required: false,
default: {}
},
}, },
data() { data() {
return { return {
@ -105,7 +112,7 @@ export default {
this.modal.showModal = true; this.modal.showModal = true;
}, },
goToGenerateWorkflow(data) { goToGenerateWorkflow(data) {
console.log('go to generate workflow intercepted'); console.log('go to generate workflow intercepted', data);
this.$emit('goToGenerateWorkflow', data); this.$emit('goToGenerateWorkflow', data);
} }
}, },

View File

@ -37,6 +37,10 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
goToGenerateWorkflowPayload: {
required: false,
default: {}
},
}, },
emits: ['goToGenerateWorkflow'], emits: ['goToGenerateWorkflow'],
methods: { methods: {
@ -51,7 +55,7 @@ export default {
window.location.assign(this.makeLink(workflowName)); window.location.assign(this.makeLink(workflowName));
} }
this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName)}); this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName), payload: this.goToGenerateWorkflowPayload});
} }
} }
} }

View File

@ -46,7 +46,8 @@ const messages = {
person: "Quitter la page et ouvrir la fiche de l'usager", person: "Quitter la page et ouvrir la fiche de l'usager",
thirdparty: "Quitter la page et voir le tiers", thirdparty: "Quitter la page et voir le tiers",
}, },
refresh: 'Rafraîchir' refresh: 'Rafraîchir',
addContact: 'Ajouter un contact'
}, },
nav: { nav: {
next: "Suivant", next: "Suivant",

View File

@ -3,6 +3,8 @@
{% if transition_form is not null %} {% if transition_form is not null %}
{{ form_start(transition_form) }} {{ form_start(transition_form) }}
{{ form_errors(transition_form) }}
{% set step = entity_workflow.currentStepChained %} {% set step = entity_workflow.currentStepChained %}
{% set labels = workflow_metadata(entity_workflow, 'label', step.currentStep) %} {% set labels = workflow_metadata(entity_workflow, 'label', step.currentStep) %}
{% set label = labels is null ? step.currentStep : labels|localize_translatable_string %} {% set label = labels is null ? step.currentStep : labels|localize_translatable_string %}
@ -60,8 +62,10 @@
{{ form_row(transition_form.freezeAfter) }} {{ form_row(transition_form.freezeAfter) }}
{% endif %} {% endif %}
<div id="futureDestUsers"> <div id="futureDests">
{{ form_row(transition_form.future_dest_users) }} {{ form_row(transition_form.future_dest_users) }}
{{ form_row(transition_form.future_dest_emails) }}
</div> </div>
<p>{{ form_label(transition_form.comment) }}</p> <p>{{ form_label(transition_form.comment) }}</p>
@ -82,13 +86,23 @@
<p>{{ 'workflow.This workflow is finalized'|trans }}</p> <p>{{ 'workflow.This workflow is finalized'|trans }}</p>
{% else %} {% else %}
<p>{{ 'workflow.You are not allowed to apply a transition on this workflow'|trans }}</p> <p>{{ 'workflow.You are not allowed to apply a transition on this workflow'|trans }}</p>
<p>{{ 'workflow.Only those users are allowed'|trans }}:</p> {% if entity_workflow.currentStep.destUser|length > 0 %}
<p><b>{{ 'workflow.Only those users are allowed'|trans }}&nbsp;:</b></p>
<ul>
{% for u in entity_workflow.currentStep.destUser -%}
<li>{{ u|chill_entity_render_box }}</li>
{%- endfor %}
</ul>
{% endif %}
<ul> {% if entity_workflow.currentStep.destUserByAccessKey|length > 0 %}
{% for u in entity_workflow.currentStep.destUser -%} <p><b>{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }}&nbsp;:</b></p>
<li>{{ u|chill_entity_render_box }}</li> <ul>
{%- endfor %} {% for u in entity_workflow.currentStep.destUserByAccessKey %}
</ul> <li>{{ u|chill_entity_render_box }}</li>
{% endfor %}
</ul>
{% endif %}
{% endif %} {% endif %}
</div> </div>

View File

@ -7,7 +7,7 @@
<div data-list-workflows="1" <div data-list-workflows="1"
data-workflows="{{ entity_workflows_json|json_encode|e('html_attr') }}" data-workflows="{{ entity_workflows_json|json_encode|e('html_attr') }}"
data-allow-create="{{ acl }}" data-allow-create="{{ acl }}"
data-related-entity-class="{{ blank_workflow.relatedEntityClass }}" data-related-entity-class="{{ relatedEntityClass }}"
data-related-entity-id="{{ blank_workflow.relatedEntityId }}" data-related-entity-id="{{ relatedEntityId }}"
data-workflows-availables="{{ workflows_availables|json_encode()|e('html_attr') }}" data-workflows-availables="{{ workflows_available|json_encode()|e('html_attr') }}"
></div> ></div>

View File

@ -69,15 +69,26 @@
</blockquote> </blockquote>
</div> </div>
{% endif %} {% endif %}
{% if loop.last and step.destUser|length > 0 %} {% if loop.last and step.allDestUser|length > 0 %}
<div class="item-row separator"> <div class="item-row separator">
<div> <div>
<p><b>{{ 'workflow.Users allowed to apply transition'|trans }}&nbsp;: </b></p> {% if step.destUser|length > 0 %}
<ul> <p><b>{{ 'workflow.Users allowed to apply transition'|trans }}&nbsp;: </b></p>
{% for u in step.destUser %} <ul>
<li>{{ u|chill_entity_render_box }}</li> {% for u in step.destUser %}
{% endfor %} <li>{{ u|chill_entity_render_box }}</li>
</ul> {% endfor %}
</ul>
{% endif %}
{% if step.destUserByAccessKey|length > 0 %}
<p><b>{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }}&nbsp;:</b></p>
<ul>
{% for u in step.destUserByAccessKey %}
<li>{{ u|chill_entity_render_box }}</li>
{% endfor %}
</ul>
{% endif %}
</div> </div>
</div> </div>
{% endif %} {% endif %}

View File

@ -0,0 +1,29 @@
{% extends '@ChillMain/layout.html.twig' %}
{% block title 'workflow.Delete workflow ?'|trans %}
{% block display_content %}
<h2>
{{ handler.entityTitle(entityWorkflow) }}
</h2>
{% include handler.template(entityWorkflow) with handler.templateData(entityWorkflow)|merge({
'display_action': false
}) %}
{% endblock %}
{% block content %}
<div class="container chill-md-10">
{{ include('@ChillMain/Util/confirmation_template.html.twig',
{
'title' : 'workflow.Delete workflow ?'|trans,
'confirm_question' : 'workflow.Are you sure you want to delete this workflow ?'|trans,
'display_content' : block('display_content'),
'cancel_route' : 'chill_main_workflow_show',
'cancel_parameters' : {'id' : entityWorkflow.id},
'form' : delete_form
} ) }}
</div>
{% endblock %}

View File

@ -21,10 +21,12 @@
{{ encore_entry_link_tags('mod_wopi_link') }} {{ encore_entry_link_tags('mod_wopi_link') }}
{% endblock %} {% endblock %}
{% import '@ChillMain/Workflow/macro_breadcrumb.html.twig' as macro %}
{% block content %} {% block content %}
<div class="col-10 workflow"> <div class="col-10 workflow">
<h1 class="mb-5">{{ block('title') }}</h1> <h1 class="mb-5">{{ block('title') }}</h1>
{# handler_template: {# handler_template:
- src/Bundle/ChillPersonBundle/Resources/views/Workflow/_evaluation.html.twig - src/Bundle/ChillPersonBundle/Resources/views/Workflow/_evaluation.html.twig
- src/Bundle/ChillPersonBundle/Resources/views/Workflow/_accompanying_period_work.html.twig - src/Bundle/ChillPersonBundle/Resources/views/Workflow/_accompanying_period_work.html.twig
@ -32,11 +34,26 @@
#} #}
<section class="step my-4"> <section class="step my-4">
<div class="mb-5"> <div class="mb-5">
{% include handler_template_title with handler_template_data|merge({'breadcrumb': true }) %} <h2>{{ handler.entityTitle(entity_workflow) }}</h2>
{{ macro.breadcrumb({'entity_workflow': entity_workflow}) }}
</div> </div>
{% include handler_template with handler_template_data|merge({'display_action': true }) %} {% include handler_template with handler_template_data|merge({'display_action': true }) %}
{% if is_granted('CHILL_MAIN_WORKFLOW_DELETE', entity_workflow) %}
<ul class="record_actions">
<li>
<a class="btn btn-delete"
href="{{ chill_path_add_return_path('chill_main_workflow_delete', {'id': entity_workflow.id}) }}"
>
{{ 'workflow.Delete workflow'|trans }}
</a>
</li>
</ul>
{% endif %}
</section> </section>
<section class="step my-4">{% include '@ChillMain/Workflow/_follow.html.twig' %}</section> <section class="step my-4">{% include '@ChillMain/Workflow/_follow.html.twig' %}</section>
<section class="step my-4">{% include '@ChillMain/Workflow/_decision.html.twig' %}</section>{# <section class="step my-4">{% include '@ChillMain/Workflow/_decision.html.twig' %}</section>{#
<section class="step my-4">{% include '@ChillMain/Workflow/_comment.html.twig' %}</section> #} <section class="step my-4">{% include '@ChillMain/Workflow/_comment.html.twig' %}</section> #}

View File

@ -8,9 +8,10 @@
{% block content %} {% block content %}
<div class="col-10 workflow"> <div class="col-10 workflow">
<h1 class="mb-5">{{ block('title') }}</h1> <h1 class="mb-5">{{ block('title') }}</h1>
<ul class="nav nav-pills justify-content-center"> <ul class="nav nav-pills justify-content-center">
<li class="nav-item"> <li class="nav-item">
<a href="{{ path('chill_main_workflow_list_subscribed') }}" <a href="{{ path('chill_main_workflow_list_subscribed') }}"
@ -24,8 +25,24 @@
{{ 'workflow.dest'|trans }} {{ 'workflow.dest'|trans }}
</a> </a>
</li> </li>
<li class="nav-item">
<a href="{{ path('chill_main_workflow_list_previous_without_reaction') }}"
class="nav-link {% if step == 'previous_without_reaction' %}active{% endif %}">
{{ 'workflow.Previous dest without reaction'|trans }}
</a>
</li>
<li class="nav-item">
<a href="{{ path('chill_main_workflow_list_previous_transitionned') }}"
class="nav-link {% if step == 'previous_transitionned' %}active{% endif %}">
{{ 'workflow.Previous transitionned'|trans }}
</a>
</li>
</ul> </ul>
{% if help is defined %}
<p style="margin-top: 2rem;">{{ help|trans }}</p>
{% endif %}
{% if workflows|length == 0 %} {% if workflows|length == 0 %}
<p class="chill-no-data-statement">{{ 'workflow.No workflow'|trans }}</p> <p class="chill-no-data-statement">{{ 'workflow.No workflow'|trans }}</p>
{% else %} {% else %}
@ -36,26 +53,24 @@
<button type="button" class="accordion-button collapsed" <button type="button" class="accordion-button collapsed"
data-bs-toggle="collapse" data-bs-target="#flush-collapse-{{ l.entity_workflow.id }}" data-bs-toggle="collapse" data-bs-target="#flush-collapse-{{ l.entity_workflow.id }}"
aria-expanded="false" aria-controls="flush-collapse-{{ l.entity_workflow.id }}"> aria-expanded="false" aria-controls="flush-collapse-{{ l.entity_workflow.id }}">
<div class="item-row col"> <div class="item-row col">
<h2> <h2>
{{ 'workflow_'|trans }} {{ l.handler.entityTitle(l.entity_workflow) }}
</h2> </h2>
{% include l.handler.templateTitle(l.entity_workflow) with l.handler.templateTitleData(l.entity_workflow)|merge({
'description': true,
'add_classes': 'ms-3 h3'
}) %}
</div> </div>
</button> </button>
{{ macro.breadcrumb(l) }} <div>
{{ macro.breadcrumb(l) }}
</div>
</div> </div>
<div id="flush-collapse-{{ l.entity_workflow.id }}" <div id="flush-collapse-{{ l.entity_workflow.id }}"
class="accordion-collapse collapse" class="accordion-collapse collapse"
aria-labelledby="flush-heading-{{ l.entity_workflow.id }}" aria-labelledby="flush-heading-{{ l.entity_workflow.id }}"
data-bs-parent="#workflow-fold"> data-bs-parent="#workflow-fold">
<div class="item-row flex-column"> <div class="item-row flex-column">
{% include l.handler.template(l.entity_workflow) with l.handler.templateData(l.entity_workflow)|merge({ {% include l.handler.template(l.entity_workflow) with l.handler.templateData(l.entity_workflow)|merge({
'display_action': false 'display_action': false
@ -78,6 +93,13 @@
</div> </div>
<div class="item-col"> <div class="item-col">
<ul class="record_actions"> <ul class="record_actions">
{% if is_granted('CHILL_MAIN_WORKFLOW_DELETE', l.entity_workflow) %}
<li>
<a class="btn btn-delete"
href="{{ chill_path_add_return_path('chill_main_workflow_delete', {'id': l.entity_workflow.id}) }}"
></a>
</li>
{% endif %}
<li> <li>
<a href="{{ path('chill_main_workflow_show', {'id': l.entity_workflow.id}) }}" <a href="{{ path('chill_main_workflow_show', {'id': l.entity_workflow.id}) }}"
class="btn btn-show"> class="btn btn-show">
@ -87,7 +109,7 @@
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}

View File

@ -0,0 +1,15 @@
Madame, Monsieur,
Un suivi "{{ workflow.text }}" a atteint une nouvelle étape: {{ workflow.text }}.
Titre du workflow: "{{ entityTitle }}".
Vous êtes invité·e à valider cette étape. Pour obtenir un accès, vous pouvez cliquer sur le lien suivant:
{{ absolute_url(path('chill_main_workflow_grant_access_by_key', {'id': entity_workflow.currentStep.id, 'accessKey': entity_workflow.currentStep.accessKey})) }}
Dès que vous aurez cliqué une fois sur le lien, vous serez autorisé à valider cette étape.
Notez que vous devez disposer d'un compte utilisateur valide dans Chill.
Cordialement,

View File

@ -0,0 +1 @@
Un suivi {{ workflow.text }} demande votre attention: {{ entityTitle }}

View File

@ -23,6 +23,8 @@ class EntityWorkflowVoter extends Voter
{ {
public const CREATE = 'CHILL_MAIN_WORKFLOW_CREATE'; public const CREATE = 'CHILL_MAIN_WORKFLOW_CREATE';
public const DELETE = 'CHILL_MAIN_WORKFLOW_DELETE';
public const SEE = 'CHILL_MAIN_WORKFLOW_SEE'; public const SEE = 'CHILL_MAIN_WORKFLOW_SEE';
private EntityWorkflowManager $manager; private EntityWorkflowManager $manager;
@ -40,7 +42,11 @@ class EntityWorkflowVoter extends Voter
return $subject instanceof EntityWorkflow && in_array($attribute, self::getRoles(), true); return $subject instanceof EntityWorkflow && in_array($attribute, self::getRoles(), true);
} }
protected function voteOnAttribute($attribute, $subject, TokenInterface $token) /**
* @param EntityWorkflow $subject
* @param mixed $attribute
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{ {
switch ($attribute) { switch ($attribute) {
case self::CREATE: case self::CREATE:
@ -55,6 +61,9 @@ class EntityWorkflowVoter extends Voter
return $this->security->isGranted($entityAttribute, $handler->getRelatedEntity($subject)); return $this->security->isGranted($entityAttribute, $handler->getRelatedEntity($subject));
case self::DELETE:
return $subject->getStep() === 'initial';
default: default:
throw new UnexpectedValueException("attribute {$attribute} not supported"); throw new UnexpectedValueException("attribute {$attribute} not supported");
} }
@ -65,6 +74,7 @@ class EntityWorkflowVoter extends Voter
return [ return [
self::SEE, self::SEE,
self::CREATE, self::CREATE,
self::DELETE,
]; ];
} }
} }

View File

@ -25,21 +25,17 @@ class ChillEntityRenderExtension extends AbstractExtension
protected $defaultRender; protected $defaultRender;
/** /**
* @var ChillEntityRenderInterface * @var iterable|ChillEntityRenderInterface[]
*/ */
protected $renders = []; protected $renders = [];
/** /**
* ChillEntityRenderExtension constructor. * ChillEntityRenderExtension constructor.
*/ */
public function __construct() public function __construct(iterable $renders)
{ {
$this->defaultRender = new ChillEntityRender(); $this->defaultRender = new ChillEntityRender();
} $this->renders = $renders;
public function addRender(ChillEntityRenderInterface $render)
{
$this->renders[] = $render;
} }
/** /**

View File

@ -1,31 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Templating\Entity;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Add service tagged with `chill.render_entity` to appropriate service.
*/
class CompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$extension = $container->getDefinition(ChillEntityRenderExtension::class);
foreach ($container->findTaggedServiceIds('chill.render_entity') as $id => $tags) {
$extension->addMethodCall('addRender', [new Reference($id)]);
}
}
}

View File

@ -33,10 +33,6 @@ interface EntityWorkflowHandlerInterface
public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array; public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array;
public function getTemplateTitle(EntityWorkflow $entityWorkflow, array $options = []): string;
public function getTemplateTitleData(EntityWorkflow $entityWorkflow, array $options = []): array;
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool; public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool;
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool; public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool;

View File

@ -72,7 +72,7 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
return; return;
} }
if (!$entityWorkflow->getCurrentStep()->getDestUser()->contains($this->security->getUser())) { if (!$entityWorkflow->getCurrentStep()->getAllDestUser()->contains($this->security->getUser())) {
if (!$event->getMarking()->has('initial')) { if (!$event->getMarking()->has('initial')) {
$event->addTransitionBlocker(new TransitionBlocker( $event->addTransitionBlocker(new TransitionBlocker(
'workflow.You are not allowed to apply a transition on this workflow. Only those users are allowed: %users%', 'workflow.You are not allowed to apply a transition on this workflow. Only those users are allowed: %users%',
@ -80,7 +80,7 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
[ [
'%users%' => implode( '%users%' => implode(
', ', ', ',
$entityWorkflow->getCurrentStep()->getDestUser()->map(function (User $u) { $entityWorkflow->getCurrentStep()->getAllDestUser()->map(function (User $u) {
return $this->userRender->renderString($u, []); return $this->userRender->renderString($u, []);
})->toArray() })->toArray()
), ),

View File

@ -52,11 +52,11 @@ class NotificationOnTransition implements EventSubscriberInterface
public static function getSubscribedEvents(): array public static function getSubscribedEvents(): array
{ {
return [ return [
'workflow.completed' => 'onCompleted', 'workflow.completed' => 'onCompletedSendNotification',
]; ];
} }
public function onCompleted(Event $event): void public function onCompletedSendNotification(Event $event): void
{ {
if (!$event->getSubject() instanceof EntityWorkflow) { if (!$event->getSubject() instanceof EntityWorkflow) {
return; return;

View File

@ -0,0 +1,71 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Workflow\EventSubscriber;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Workflow\Registry;
class SendAccessKeyEventSubscriber
{
private EngineInterface $engine;
private EntityWorkflowManager $entityWorkflowManager;
private MailerInterface $mailer;
private MetadataExtractor $metadataExtractor;
private Registry $registry;
public function __construct(EngineInterface $engine, MetadataExtractor $metadataExtractor, Registry $registry, EntityWorkflowManager $entityWorkflowManager, MailerInterface $mailer)
{
$this->engine = $engine;
$this->metadataExtractor = $metadataExtractor;
$this->registry = $registry;
$this->entityWorkflowManager = $entityWorkflowManager;
$this->mailer = $mailer;
}
public function postPersist(EntityWorkflowStep $step): void
{
$entityWorkflow = $step->getEntityWorkflow();
$place = $this->metadataExtractor->buildArrayPresentationForPlace($entityWorkflow);
$workflow = $this->metadataExtractor->buildArrayPresentationForWorkflow(
$this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName())
);
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
foreach ($entityWorkflow->futureDestEmails as $emailAddress) {
$context = [
'entity_workflow' => $entityWorkflow,
'dest' => $emailAddress,
'place' => $place,
'workflow' => $workflow,
'entityTitle' => $handler->getEntityTitle($entityWorkflow),
];
$email = new Email();
$email
->addTo($emailAddress)
->subject($this->engine->render('@ChillMain/Workflow/workflow_send_access_key_title.fr.txt.twig', $context))
->text($this->engine->render('@ChillMain/Workflow/workflow_send_access_key.fr.txt.twig', $context));
$this->mailer->send($email);
}
}
}

View File

@ -61,7 +61,38 @@ class WorkflowTwigExtensionRuntime implements RuntimeExtensionInterface
return null; return null;
} }
public function listWorkflows(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string /**
* @param array{relatedEntityClass: string, relatedEntityId: int} $supplementaryRelated
*
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*/
public function listWorkflows(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = [], array $supplementaryRelated = []): string
{
[$blankEntityWorkflow, $workflowsAvailable, $entityWorkflows] = $this->getWorkflowsForRelated($relatedEntityClass, $relatedEntityId);
foreach ($supplementaryRelated as $supplementary) {
[$supplementaryBlankEntityWorkflow, $supplementaryWorkflowsAvailable, $supplementaryEntityWorkflows]
= $this->getWorkflowsForRelated($supplementary['relatedEntityClass'], $supplementary['relatedEntityId']);
$entityWorkflows = array_merge($entityWorkflows, $supplementaryEntityWorkflows);
}
return $environment->render('@ChillMain/Workflow/_extension_list_workflow_for.html.twig', [
'entity_workflows_json' => $this->normalizer->normalize($entityWorkflows, 'json', ['groups' => 'read']),
'blank_workflow' => $blankEntityWorkflow,
'workflows_available' => $workflowsAvailable,
'relatedEntityClass' => $relatedEntityClass,
'relatedEntityId' => $relatedEntityId,
]);
}
/**
* @return array where keys are: {0: blankWorkflow, 1: entityWorkflows, 2: workflowList}
*/
private function getWorkflowsForRelated(string $relatedEntityClass, int $relatedEntityId): array
{ {
$blankEntityWorkflow = new EntityWorkflow(); $blankEntityWorkflow = new EntityWorkflow();
$blankEntityWorkflow $blankEntityWorkflow
@ -70,25 +101,13 @@ class WorkflowTwigExtensionRuntime implements RuntimeExtensionInterface
$workflowsList = $this->metadataExtractor->availableWorkflowFor($relatedEntityClass, $relatedEntityId); $workflowsList = $this->metadataExtractor->availableWorkflowFor($relatedEntityClass, $relatedEntityId);
// get the related entity already created return
$entityWorkflows = []; [
$blankEntityWorkflow,
foreach ($entityWorkflowsNaked = $this->repository->findBy( $workflowsList,
['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId] $this->repository->findBy(
) as $entityWorkflow) { ['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId]
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()); ),
$entityWorkflows[] = [
'entity_workflow' => $entityWorkflow,
'workflow' => $this->metadataExtractor->buildArrayPresentationForWorkflow($workflow),
'handler' => $this->entityWorkflowManager->getHandler($entityWorkflow),
]; ];
}
return $environment->render('@ChillMain/Workflow/_extension_list_workflow_for.html.twig', [
'entity_workflows_json' => $this->normalizer->normalize($entityWorkflowsNaked, 'json', ['groups' => 'read']),
'entity_workflows' => $entityWorkflows,
'blank_workflow' => $blankEntityWorkflow,
'workflows_availables' => $workflowsList,
]);
} }
} }

View File

@ -788,6 +788,16 @@ paths:
description: "accepted" description: "accepted"
403: 403:
description: "unauthorized" description: "unauthorized"
/1.0/main/civility.json:
get:
tags:
- civility
summary: Return all civility types
responses:
200:
description: "ok"
401:
description: "Unauthorized"
/1.0/main/user-job.json: /1.0/main/user-job.json:
get: get:
tags: tags:

View File

@ -38,6 +38,17 @@ services:
arguments: arguments:
$handlers: !tagged_iterator chill_main.workflow_handler $handlers: !tagged_iterator chill_main.workflow_handler
Chill\MainBundle\Workflow\EventSubscriber\SendAccessKeyEventSubscriber:
autoconfigure: true
autowire: true
tags:
-
name: 'doctrine.orm.entity_listener'
event: 'postPersist'
entity: 'Chill\MainBundle\Entity\Workflow\EntityWorkflowStep'
# set the 'lazy' option to TRUE to only instantiate listeners when they are used
lazy: true
# other stuffes # other stuffes
chill.main.helper.translatable_string: chill.main.helper.translatable_string:

View File

@ -32,6 +32,8 @@ services:
- { name: twig.extension } - { name: twig.extension }
Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension: Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension:
arguments:
$renders: !tagged_iterator chill.render_entity
tags: tags:
- { name: twig.extension } - { name: twig.extension }

View File

@ -0,0 +1,51 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20220223171457 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_main_workflow_entity_step DROP accessKey');
$this->addSql('DROP TABLE chill_main_workflow_entity_step_user_by_accesskey');
}
public function getDescription(): string
{
return 'Add access key to EntityWorkflowStep';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_main_workflow_entity_step ADD accessKey TEXT DEFAULT NULL');
$this->addSql('WITH randoms AS (select
cmwes.id,
string_agg(substr(characters, (random() * length(characters) + 0.5)::integer, 1), \'\') as random_word
from (values(\'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\')) as symbols(characters)
-- length of word
join generate_series(1, 32) on 1 = 1
JOIN chill_main_workflow_entity_step cmwes ON true
GROUP BY cmwes.id)
UPDATE chill_main_workflow_entity_step SET accessKey = randoms.random_word FROM randoms WHERE chill_main_workflow_entity_step.id = randoms.id');
$this->addSql('ALTER TABLE chill_main_workflow_entity_step ALTER accessKey DROP DEFAULT ');
$this->addSql('ALTER TABLE chill_main_workflow_entity_step ALTER accessKey SET NOT NULL');
$this->addSql('CREATE TABLE chill_main_workflow_entity_step_user_by_accesskey (entityworkflowstep_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(entityworkflowstep_id, user_id))');
$this->addSql('CREATE INDEX IDX_8296D8397E6AF9D4 ON chill_main_workflow_entity_step_user_by_accesskey (entityworkflowstep_id)');
$this->addSql('CREATE INDEX IDX_8296D839A76ED395 ON chill_main_workflow_entity_step_user_by_accesskey (user_id)');
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_user_by_accesskey ADD CONSTRAINT FK_8296D8397E6AF9D4 FOREIGN KEY (entityworkflowstep_id) REFERENCES chill_main_workflow_entity_step (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_user_by_accesskey ADD CONSTRAINT FK_8296D839A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
}
}

View File

@ -9,7 +9,7 @@ notification:
My notifications with counter: >- My notifications with counter: >-
{nb, plural, {nb, plural,
=0 {Mes notifications} =0 {Mes notifications}
one {Une notification} one {# notification}
few {# notifications} few {# notifications}
other {# notifications} other {# notifications}
} }
@ -41,7 +41,7 @@ workflow:
My workflows with counter: >- My workflows with counter: >-
{wc, plural, {wc, plural,
=0 {Mes workflows} =0 {Mes workflows}
one {Une workflow} one {# workflow}
few {# workflows} few {# workflows}
other {# workflows} other {# workflows}
} }

View File

@ -356,6 +356,7 @@ By: Par
For: Pour For: Pour
Created for: Créé pour Created for: Créé pour
Created by: Créé par Created by: Créé par
Created on: Créé le
# Workflows 💊 # Workflows 💊
@ -396,6 +397,23 @@ workflow:
Current step: Étape actuelle Current step: Étape actuelle
Comment on last change: Commentaire à la transition précédente Comment on last change: Commentaire à la transition précédente
Users allowed to apply transition: Utilisateurs pouvant valider cette étape Users allowed to apply transition: Utilisateurs pouvant valider cette étape
Workflow deleted with success: Le workflow a été supprimé
Delete workflow ?: Supprimer le workflow ?
Are you sure you want to delete this workflow ?: Êtes-vous sûr·e de vouloir supprimer ce workflow ?
Delete workflow: Supprimer le workflow
Steps is not waiting for transition. Maybe someone apply the transition before you ?: L'étape que vous cherchez a déjà été modifiée par un autre utilisateur. Peut-être quelqu'un a-t-il modifié cette étape avant vous ?
You get access to this step: Vous avez acquis les droits pour appliquer une transition sur ce workflow.
Those users are also granted to apply a transition by using an access key: Ces utilisateurs peuvent également valider cette étape, grâce à un lien d'accès
dest by email: Liens d'autorisation par email
dest by email help: Les adresses email mentionnées ici recevront un lien d'accès. Ce lien d'accès permettra à l'utilisateur de valider cette étape.
Add an email: Ajouter une adresse email
Remove an email: Enlever cette adresse email
Any email: Aucune adresse email
Previous dest without reaction: Workflows clotûrés après action d'un autre utilisateur
Previous workflow without reaction help: Liste des workflows où vous avez été cité comme pouvant réagir à une étape, mais où un autre utilisateur a exécuté une action avant vous.
Previous transitionned: Anciens workflows
Previous workflow transitionned help: Workflows où vous avez exécuté une action.
Subscribe final: Recevoir une notification à l'étape finale Subscribe final: Recevoir une notification à l'étape finale
Subscribe all steps: Recevoir une notification à chaque étape Subscribe all steps: Recevoir une notification à chaque étape

View File

@ -28,3 +28,6 @@ notification:
At least one addressee: Indiquez au moins un destinataire At least one addressee: Indiquez au moins un destinataire
Title must be defined: Un titre doit être indiqué Title must be defined: Un titre doit être indiqué
Comment content might not be blank: Le commentaire ne peut pas être vide Comment content might not be blank: Le commentaire ne peut pas être vide
workflow:
You must add at least one dest user or email: Indiquez au moins un destinataire ou une adresse email

View File

@ -12,12 +12,9 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Controller; namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Doctrine\ORM\Hydration\FlatHierarchyEntityHydrator;
use Chill\MainBundle\Pagination\PaginatorInterface; use Chill\MainBundle\Pagination\PaginatorInterface;
use DateTimeImmutable; use DateTimeImmutable;
use Doctrine\ORM\Query;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class SocialIssueApiController extends ApiController class SocialIssueApiController extends ApiController
{ {
@ -32,22 +29,10 @@ class SocialIssueApiController extends ApiController
$query->setParameter('now', new DateTimeImmutable()); $query->setParameter('now', new DateTimeImmutable());
} }
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query) protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
{ {
// In order to work, this hydrator only works with $query->addOrderBy('e.ordering', 'ASC');
// entities having the field "children" set up.
return $query
->getQuery()
->setHint(Query::HINT_INCLUDE_META_COLUMNS, true)
->getResult(FlatHierarchyEntityHydrator::LIST);
}
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response return $query;
{
$query
->orderBy('GET_JSON_FIELD_BY_KEY(e.title, :locale)', 'ASC')
->setParameter(':locale', $request->getLocale());
return null;
} }
} }

View File

@ -250,10 +250,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
return $this->accompanyingPeriod; return $this->accompanyingPeriod;
} }
/** public function getAccompanyingPeriodWorkEvaluations(): Collection
* @return Collection
*/
public function getAccompanyingPeriodWorkEvaluations()
{ {
return $this->accompanyingPeriodWorkEvaluations; return $this->accompanyingPeriodWorkEvaluations;
} }

View File

@ -67,10 +67,15 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
private ?User $createdBy = null; private ?User $createdBy = null;
/** /**
* **Note on deserialization/denormalization**: denormalization of documents is handled by.
*
* @see{Chill\PersonBundle\Serializer\Normalizer\AccompanyingPeriodWorkEvaluationDenormalizer}
*
* @ORM\OneToMany( * @ORM\OneToMany(
* targetEntity=AccompanyingPeriodWorkEvaluationDocument::class, * targetEntity=AccompanyingPeriodWorkEvaluationDocument::class,
* mappedBy="accompanyingPeriodWorkEvaluation", * mappedBy="accompanyingPeriodWorkEvaluation",
* cascade={"remove"} * cascade={"remove", "persist"},
* orphanRemoval=true
* ) * )
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
*/ */
@ -261,6 +266,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
public function removeDocument(AccompanyingPeriodWorkEvaluationDocument $document): self public function removeDocument(AccompanyingPeriodWorkEvaluationDocument $document): self
{ {
$this->documents->removeElement($document); $this->documents->removeElement($document);
$document->setAccompanyingPeriodWorkEvaluation(null);
return $this; return $this;
} }

View File

@ -41,6 +41,7 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
/** /**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) * @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/ */
private ?\DateTimeImmutable $createdAt = null; private ?\DateTimeImmutable $createdAt = null;
@ -49,6 +50,7 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* targetEntity=User::class * targetEntity=User::class
* ) * )
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/ */
private ?User $createdBy = null; private ?User $createdBy = null;
@ -60,15 +62,32 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* @internal the default name exceeds 64 characters, we must set manually: * @internal the default name exceeds 64 characters, we must set manually:
* @ORM\SequenceGenerator(sequenceName="chill_person_social_work_eval_doc_id_seq", allocationSize=1, initialValue=1000) * @ORM\SequenceGenerator(sequenceName="chill_person_social_work_eval_doc_id_seq", allocationSize=1, initialValue=1000)
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/ */
private ?int $id; private ?int $id = null;
/**
* This is a workaround for client, to allow them to assign arbitrary data
* dedicated to their job.
*
* This data is not persisted into database, but will appears on the data
* normalized during the same request (like PUT/PATCH request)
*
* @Serializer\Groups({"read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*
* @var mixed
*/
private $key;
/** /**
* @ORM\ManyToOne( * @ORM\ManyToOne(
* targetEntity=StoredObject::class, * targetEntity=StoredObject::class,
* cascade={"remove"},
* ) * )
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/ */
private ?StoredObject $storedObject = null; private ?StoredObject $storedObject = null;
@ -77,12 +96,22 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* targetEntity=DocGeneratorTemplate::class * targetEntity=DocGeneratorTemplate::class
* ) * )
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/ */
private ?DocGeneratorTemplate $template = null; private ?DocGeneratorTemplate $template = null;
/**
* @ORM\Column(type="text", nullable=false, options={"default": ""})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?string $title = '';
/** /**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) * @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/ */
private ?\DateTimeImmutable $updatedAt = null; private ?\DateTimeImmutable $updatedAt = null;
@ -91,6 +120,7 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
* targetEntity=User::class * targetEntity=User::class
* ) * )
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/ */
private ?User $updatedBy = null; private ?User $updatedBy = null;
@ -117,6 +147,14 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this->id; return $this->id;
} }
/**
* @return mixed
*/
public function getKey()
{
return $this->key;
}
public function getStoredObject(): ?StoredObject public function getStoredObject(): ?StoredObject
{ {
return $this->storedObject; return $this->storedObject;
@ -127,6 +165,11 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this->template; return $this->template;
} }
public function getTitle(): ?string
{
return $this->title;
}
/** /**
* @return DateTimeImmutable|null * @return DateTimeImmutable|null
*/ */
@ -171,6 +214,18 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this; return $this;
} }
/**
* @param mixed $key
*
* @return AccompanyingPeriodWorkEvaluationDocument
*/
public function setKey($key)
{
$this->key = $key;
return $this;
}
public function setStoredObject(?StoredObject $storedObject): AccompanyingPeriodWorkEvaluationDocument public function setStoredObject(?StoredObject $storedObject): AccompanyingPeriodWorkEvaluationDocument
{ {
$this->storedObject = $storedObject; $this->storedObject = $storedObject;
@ -185,6 +240,13 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
return $this; return $this;
} }
public function setTitle(?string $title): AccompanyingPeriodWorkEvaluationDocument
{
$this->title = $title;
return $this;
}
public function setUpdatedAt(DateTimeInterface $datetime): TrackUpdateInterface public function setUpdatedAt(DateTimeInterface $datetime): TrackUpdateInterface
{ {
$this->updatedAt = $datetime; $this->updatedAt = $datetime;

View File

@ -358,7 +358,7 @@ class Household
$membership->getEndDate() $membership->getEndDate()
)->filter( )->filter(
static function (HouseholdMember $m) use ($membership) { static function (HouseholdMember $m) use ($membership) {
return $m !== $membership; return $m->getPerson() !== $membership->getPerson();
} }
); );
} }

View File

@ -16,7 +16,7 @@ use Chill\MainBundle\Form\Type\CommentType;
use Chill\PersonBundle\Entity\Person\PersonResource; use Chill\PersonBundle\Entity\Person\PersonResource;
use Chill\PersonBundle\Entity\Person\PersonResourceKind; use Chill\PersonBundle\Entity\Person\PersonResourceKind;
use Chill\PersonBundle\Form\Type\PickPersonDynamicType; use Chill\PersonBundle\Form\Type\PickPersonDynamicType;
use Chill\PersonBundle\Templating\Entity\PersonRender; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Chill\PersonBundle\Templating\Entity\ResourceKindRender; use Chill\PersonBundle\Templating\Entity\ResourceKindRender;
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType; use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender; use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
@ -29,7 +29,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
final class PersonResourceType extends AbstractType final class PersonResourceType extends AbstractType
{ {
private PersonRender $personRender; private PersonRenderInterface $personRender;
private ResourceKindRender $resourceKindRender; private ResourceKindRender $resourceKindRender;
@ -37,7 +37,7 @@ final class PersonResourceType extends AbstractType
private TranslatorInterface $translator; private TranslatorInterface $translator;
public function __construct(ResourceKindRender $resourceKindRender, PersonRender $personRender, ThirdPartyRender $thirdPartyRender, TranslatorInterface $translator) public function __construct(ResourceKindRender $resourceKindRender, PersonRenderInterface $personRender, ThirdPartyRender $thirdPartyRender, TranslatorInterface $translator)
{ {
$this->resourceKindRender = $resourceKindRender; $this->resourceKindRender = $resourceKindRender;
$this->personRender = $personRender; $this->personRender = $personRender;

View File

@ -12,11 +12,11 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Form; namespace Chill\PersonBundle\Form;
use Chill\CustomFieldsBundle\Form\Type\CustomFieldType; use Chill\CustomFieldsBundle\Form\Type\CustomFieldType;
use Chill\MainBundle\Entity\Civility;
use Chill\MainBundle\Form\Type\ChillCollectionType; use Chill\MainBundle\Form\Type\ChillCollectionType;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\ChillTextareaType; use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\CommentType; use Chill\MainBundle\Form\Type\CommentType;
use Chill\MainBundle\Form\Type\PickCivilityType;
use Chill\MainBundle\Form\Type\Select2CountryType; use Chill\MainBundle\Form\Type\Select2CountryType;
use Chill\MainBundle\Form\Type\Select2LanguageType; use Chill\MainBundle\Form\Type\Select2LanguageType;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
@ -27,9 +27,6 @@ use Chill\PersonBundle\Form\Type\GenderType;
use Chill\PersonBundle\Form\Type\PersonAltNameType; use Chill\PersonBundle\Form\Type\PersonAltNameType;
use Chill\PersonBundle\Form\Type\PersonPhoneType; use Chill\PersonBundle\Form\Type\PersonPhoneType;
use Chill\PersonBundle\Form\Type\Select2MaritalStatusType; use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
@ -190,19 +187,10 @@ class PersonType extends AbstractType
if ('visible' === $this->config['civility']) { if ('visible' === $this->config['civility']) {
$builder $builder
->add('civility', EntityType::class, [ ->add('civility', PickCivilityType::class, [
'label' => 'Civility',
'class' => Civility::class,
'choice_label' => function (Civility $civility): string {
return $this->translatableStringHelper->localize($civility->getName());
},
'query_builder' => static function (EntityRepository $er): QueryBuilder {
return $er->createQueryBuilder('c')
->where('c.active = true')
->orderBy('c.order');
},
'placeholder' => 'choose civility',
'required' => false, 'required' => false,
'label' => 'Civility',
'placeholder' => 'choose civility',
]); ]);
} }

View File

@ -72,6 +72,16 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
'order' => 60, 'order' => 60,
]); ]);
$menu->addChild($this->translator->trans('person_resources_menu'), [
'route' => 'chill_person_resource_list',
'routeParameters' => [
'person_id' => $parameters['person']->getId(),
],
])
->setExtras([
'order' => 70,
]);
$menu->addChild($this->translator->trans('household.person history'), [ $menu->addChild($this->translator->trans('household.person history'), [
'route' => 'chill_person_household_person_history', 'route' => 'chill_person_household_person_history',
'routeParameters' => [ 'routeParameters' => [
@ -106,16 +116,6 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
'order' => 100, 'order' => 100,
]); ]);
} }
$menu->addChild($this->translator->trans('person_resources_menu'), [
'route' => 'chill_person_resource_list',
'routeParameters' => [
'person_id' => $parameters['person']->getId(),
],
])
->setExtras([
'order' => 99999,
]);
} }
public static function getMenuIds(): array public static function getMenuIds(): array

View File

@ -0,0 +1,61 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectRepository
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{
$this->repository = $em->getRepository(AccompanyingPeriodWorkEvaluationDocument::class);
}
public function find($id): ?AccompanyingPeriodWorkEvaluationDocument
{
return $this->repository->find($id);
}
/**
* @return array|object[]|AccompanyingPeriodWorkEvaluationDocument[]
*/
public function findAll(): array
{
return $this->repository->findAll();
}
/**
* @param null|mixed $limit
* @param null|mixed $offset
*
* @return array|object[]|AccompanyingPeriodWorkEvaluationDocument[]
*/
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): ?AccompanyingPeriodWorkEvaluationDocument
{
return $this->repository->findOneBy($criteria);
}
public function getClassName(): string
{
return AccompanyingPeriodWorkEvaluationDocument::class;
}
}

View File

@ -159,10 +159,10 @@ export default {
.catch((error) => { .catch((error) => {
if (error.name === 'ValidationException') { if (error.name === 'ValidationException') {
for (let v of error.violations) { for (let v of error.violations) {
this.$toast.open({message: v }); this.$toast.open({message: v });
} }
} else { } else {
this.$toast.open({message: 'An error occurred'}); this.$toast.open({message: 'An error occurred'});
} }
}) })
} }
@ -171,9 +171,7 @@ export default {
body.name = payload.data.text; body.name = payload.data.text;
body.email = payload.data.email; body.email = payload.data.email;
body.telephone = payload.data.phonenumber; body.telephone = payload.data.phonenumber;
body.address = payload.data.address ? { id: payload.data.address.address_id } : null; body.address = payload.data.address ? { id: payload.data.address.address_id } : null;
console.log('body', body)
makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body) makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body)
.then(response => { .then(response => {
@ -183,10 +181,10 @@ export default {
.catch((error) => { .catch((error) => {
if (error.name === 'ValidationException') { if (error.name === 'ValidationException') {
for (let v of error.violations) { for (let v of error.violations) {
this.$toast.open({message: v }); this.$toast.open({message: v });
} }
} else { } else {
this.$toast.open({message: 'An error occurred'}); this.$toast.open({message: 'An error occurred'});
} }
}) })
} }

View File

@ -251,6 +251,7 @@
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork" relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork"
:relatedEntityId="this.work.id" :relatedEntityId="this.work.id"
:workflowsAvailables="this.work.workflows_availables" :workflowsAvailables="this.work.workflows_availables"
:preventDefaultMoveToGenerate="true"
@go-to-generate-workflow="goToGenerateWorkflow" @go-to-generate-workflow="goToGenerateWorkflow"
></list-workflow-modal> ></list-workflow-modal>
</li> </li>

View File

@ -106,8 +106,6 @@ export default {
this.toggleEditEvaluation(); this.toggleEditEvaluation();
}, },
goToGenerateWorkflow({event, link, workflowName}) { goToGenerateWorkflow({event, link, workflowName}) {
console.log('goToGenerate in evaluation', event, link, workflowName);
const callback = (data) => { const callback = (data) => {
let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id; let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id;
window.location.assign(buildLinkCreate(workflowName, window.location.assign(buildLinkCreate(workflowName,

View File

@ -65,29 +65,55 @@
<h5>{{ $t('Documents') }} :</h5> <h5>{{ $t('Documents') }} :</h5>
<div class="flex-table"> <div class="flex-table">
<div class="item-bloc" v-for="d in evaluation.documents"> <div class="item-bloc" v-for="(d, i) in evaluation.documents" :key="d.key">
<div class="item-row"> <div class="item-row">
<div class="item-col"><h6>{{ d.template.name.fr }}</h6></div> <div class="input-group input-group-lg mb-3">
<div class="item-col"> <div>
<p>Créé par {{ d.createdBy.text }}<br/> <input
Le {{ $d(ISOToDatetime(d.createdAt.datetime), 'long') }}</p> class="form-control form-control-lg"
style="font-weight: bold;"
</div> type="text"
:value="d.title"
:id="d.id"
@input="onInputDocumentTitle"/>
</div>
</div>
</div> </div>
<div class="item-row"> <div class="item-row">
<ul class="record_actions" > <div class="item-col item-meta">
<li> <p v-if="d.createdBy" class="createdBy">Créé par {{ d.createdBy.text }}<br/>
<a :href="buildEditLink(d.storedObject)" class="btn btn-action btn-sm"> Le {{ $d(ISOToDatetime(d.createdAt.datetime), 'long') }}</p>
<i class="fa fa-edit"></i> </div>
</a> <div class="item-col">
</li> <ul class="record_actions" >
</ul> <li v-if="d.workflows_availables.length > 0">
<list-workflow-modal
:workflows="d.workflows"
:allowCreate="true"
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument"
:relatedEntityId="d.id"
:workflowsAvailables="d.workflows_availables"
:preventDefaultMoveToGenerate="true"
:goToGenerateWorkflowPayload="{doc: d}"
@go-to-generate-workflow="goToGenerateWorkflowEvaluationDocument"
></list-workflow-modal>
</li>
<li>
<a :href="buildEditLink(d.storedObject)" class="btn btn-wopilink"></a>
</li>
<li>
<a class="btn btn-delete" @click="removeDocument(d)">
</a>
</li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<h6>{{ $t('document_add') }} :</h6>
<pick-template <pick-template
entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation" entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation"
:id="evaluation.id" :id="evaluation.id"
@ -99,6 +125,20 @@
<label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label> <label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label>
</template> </template>
</pick-template> </pick-template>
<div>
<label class="col-sm-4 col-form-label">{{ $t('document_upload') }}</label>
<ul class="record_actions">
<li>
<add-async-upload
:buttonTitle="$t('browse')"
:options="asyncUploadOptions"
@addDocument="addDocument"
>
</add-async-upload>
</li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -111,6 +151,9 @@ import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js';
import { mapGetters, mapState } from 'vuex'; import { mapGetters, mapState } from 'vuex';
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue'; import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator'; import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator';
import AddAsyncUpload from 'ChillDocStoreAssets/vuejs/_components/AddAsyncUpload.vue';
import ListWorkflowModal from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
const i18n = { const i18n = {
messages: { messages: {
@ -129,6 +172,11 @@ const i18n = {
evaluation_add_a_document: "Ajouter un document", evaluation_add_a_document: "Ajouter un document",
evaluation_add: "Ajouter une évaluation", evaluation_add: "Ajouter une évaluation",
Documents: "Documents", Documents: "Documents",
document_add: "Générer ou téléverser un document",
document_upload: "Téléverser un document",
document_title: "Titre du document",
template_title: "Nom du template",
browse: "Ajouter un document"
} }
} }
}; };
@ -139,12 +187,19 @@ export default {
components: { components: {
ckeditor: CKEditor.component, ckeditor: CKEditor.component,
PickTemplate, PickTemplate,
AddAsyncUpload,
ListWorkflowModal,
}, },
i18n, i18n,
data() { data() {
return { return {
editor: ClassicEditor, editor: ClassicEditor,
template: null, template: null,
asyncUploadOptions: {
maxFiles: 1,
maxPostSize: 15000000,
required: false,
}
} }
}, },
computed: { computed: {
@ -162,7 +217,6 @@ export default {
return dateToISO(this.evaluation.startDate); return dateToISO(this.evaluation.startDate);
}, },
set(v) { set(v) {
console.log(v);
this.$store.commit('setEvaluationStartDate', { key: this.evaluation.key, date: ISOToDate(v) }); this.$store.commit('setEvaluationStartDate', { key: this.evaluation.key, date: ISOToDate(v) });
} }
}, },
@ -205,11 +259,11 @@ export default {
}) })
; ;
}, },
buildEditLink(storedObject) { buildEditLink(storedObject) {
return `/wopi/edit/${storedObject.uuid}?returnPath=` + encodeURIComponent( return `/wopi/edit/${storedObject.uuid}?returnPath=` + encodeURIComponent(
window.location.pathname + window.location.search + window.location.hash); window.location.pathname + window.location.search + window.location.hash);
}, },
submitBeforeGenerate({template}) { submitBeforeGenerate({template}) {
const callback = (data) => { const callback = (data) => {
let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id; let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id;
@ -217,7 +271,36 @@ export default {
}; };
return this.$store.dispatch('submit', callback).catch(e => { console.log(e); throw e; }); return this.$store.dispatch('submit', callback).catch(e => { console.log(e); throw e; });
} },
onInputDocumentTitle(event) {
const id = Number(event.target.id);
const title = event.target.value;
this.$store.commit('updateDocumentTitle', {id: id, evaluationKey: this.evaluation.key, title: title});
},
addDocument(storedObject) {
let document = {
type: 'accompanying_period_work_evaluation_document',
storedObject: storedObject,
title: 'Nouveau document',
};
this.$store.commit('addDocument', {key: this.evaluation.key, document: document});
},
removeDocument(document) {
if (window.confirm("Êtes-vous sûr·e de vouloir supprimer le document qui a pour titre \"" + document.title +"\" ?")) {
this.$store.commit('removeDocument', {key: this.evaluation.key, document: document});
}
},
goToGenerateWorkflowEvaluationDocument({event, link, workflowName, payload}) {
const callback = (data) => {
let evaluation = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key);
let updatedDocument = evaluation.documents.find(d => d.key === payload.doc.key);
window.location.assign(buildLinkCreate(workflowName,
'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument', updatedDocument.id));
};
return this.$store.dispatch('submit', callback)
.catch(e => { console.log(e); throw e; });
},
}, },
} }
</script> </script>

View File

@ -110,6 +110,7 @@ const store = createStore({
maxDate: e.maxDate !== null ? { datetime: datetimeToISO(e.maxDate) } : null, maxDate: e.maxDate !== null ? { datetime: datetimeToISO(e.maxDate) } : null,
warningInterval: intervalDaysToISO(e.warningInterval), warningInterval: intervalDaysToISO(e.warningInterval),
comment: e.comment, comment: e.comment,
documents: e.documents
}; };
if (e.id !== undefined) { if (e.id !== undefined) {
o.id = e.id; o.id = e.id;
@ -130,6 +131,11 @@ const store = createStore({
endDate: e.endDate !== null ? ISOToDatetime(e.endDate.datetime) : null, endDate: e.endDate !== null ? ISOToDatetime(e.endDate.datetime) : null,
maxDate: e.maxDate !== null ? ISOToDatetime(e.maxDate.datetime) : null, maxDate: e.maxDate !== null ? ISOToDatetime(e.maxDate.datetime) : null,
warningInterval: e.warningInterval !== null ? intervalISOToDays(e.warningInterval) : null, warningInterval: e.warningInterval !== null ? intervalISOToDays(e.warningInterval) : null,
documents: e.documents.map((d, dindex) => {
return Object.assign(d, {
key: index
});
}),
}); });
return k; return k;
@ -197,6 +203,24 @@ const store = createStore({
found.results = found.results.filter(r => r.id !== result.id); found.results = found.results.filter(r => r.id !== result.id);
}, },
addDocument(state, payload) {
let evaluation = state.evaluationsPicked.find(e => e.key === payload.key);
evaluation.documents.push(Object.assign(
payload.document, {
key: evaluation.documents.length + 1,
workflows_availables: state.work.workflows_availables_evaluation_documents,
workflows: [],
}));
},
removeDocument(state, {key, document}) {
let evaluations = state.evaluationsPicked.find(e => e.key === key);
if (evaluations === undefined) {
return;
}
evaluations.documents = evaluations.documents.filter(d => d.key !== document.key);
},
addEvaluation(state, evaluation) { addEvaluation(state, evaluation) {
let e = { let e = {
type: "accompanying_period_work_evaluation", type: "accompanying_period_work_evaluation",
@ -284,6 +308,10 @@ const store = createStore({
setIsPosting(state, st) { setIsPosting(state, st) {
state.isPosting = st; state.isPosting = st;
}, },
updateDocumentTitle(state, payload) {
state.evaluationsPicked.find(e => e.key === payload.evaluationKey)
.documents.find(d => d.id === payload.id).title = payload.title;
}
}, },
actions: { actions: {
updateThirdParty({ commit }, payload) { updateThirdParty({ commit }, payload) {
@ -374,13 +402,18 @@ const store = createStore({
}); });
} }
}, },
addDocument({commit}, payload) {
commit('addDocument', payload);
},
removeDocument({commit}, payload) {
commit('removeDocument', payload);
},
submit({ getters, state, commit }, callback) { submit({ getters, state, commit }, callback) {
let let
payload = getters.buildPayload, payload = getters.buildPayload,
url = `/api/1.0/person/accompanying-course/work/${state.work.id}.json`, url = `/api/1.0/person/accompanying-course/work/${state.work.id}.json`,
errors = [] errors = []
; ;
commit('setIsPosting', true); commit('setIsPosting', true);
return makeFetch('PUT', url, payload) return makeFetch('PUT', url, payload)
@ -397,6 +430,9 @@ const store = createStore({
commit('setErrors', error.violations); commit('setErrors', error.violations);
}); });
}, },
updateDocumentTitle({commit}, payload) {
commit('updateDocumentTitle', payload)
}
} }
}); });

View File

@ -57,6 +57,8 @@
v-bind:item="item" v-bind:item="item"
v-bind:search="search" v-bind:search="search"
v-bind:type="checkUniq" v-bind:type="checkUniq"
@saveFormOnTheFly="saveFormOnTheFly"
@newPriorSuggestion="newPriorSuggestion"
@updateSelected="updateSelected"> @updateSelected="updateSelected">
</person-suggestion> </person-suggestion>
@ -249,7 +251,7 @@ export default {
} }
}, },
newPriorSuggestion(entity) { newPriorSuggestion(entity) {
console.log('newPriorSuggestion', entity); // console.log('newPriorSuggestion', entity);
if (entity !== null) { if (entity !== null) {
let suggestion = { let suggestion = {
key: entity.type + entity.id, key: entity.type + entity.id,

View File

@ -18,6 +18,7 @@
<suggestion-third-party <suggestion-third-party
v-if="item.result.type === 'thirdparty'" v-if="item.result.type === 'thirdparty'"
@newPriorSuggestion="newPriorSuggestion"
v-bind:item="item"> v-bind:item="item">
</suggestion-third-party> </suggestion-third-party>
@ -26,10 +27,10 @@
v-bind:item="item"> v-bind:item="item">
</suggestion-user> </suggestion-user>
<suggestion-household <suggestion-household
v-if="item.result.type === 'household'" v-if="item.result.type === 'household'"
v-bind:item="item"> v-bind:item="item">
</suggestion-household> </suggestion-household>
</label> </label>
</div> </div>
@ -54,7 +55,7 @@ export default {
'search', 'search',
'type' 'type'
], ],
emits: ['updateSelected'], emits: ['updateSelected', 'newPriorSuggestion'],
computed: { computed: {
selected: { selected: {
set(value) { set(value) {
@ -72,6 +73,9 @@ export default {
methods: { methods: {
setValueByType(value, type) { setValueByType(value, type) {
return (type === 'radio')? [value] : value; return (type === 'radio')? [value] : value;
},
newPriorSuggestion(response) {
this.$emit('newPriorSuggestion', response)
} }
} }
}; };

View File

@ -3,7 +3,7 @@
<div class="tparty-identification"> <div class="tparty-identification">
<span v-if="item.result.profession" class="profession">{{ item.result.profession.name.fr }}</span> <span v-if="item.result.profession" class="profession">{{ item.result.profession.name.fr }}</span>
<span class="name"> <span class="name">
{{ item.result.text }}&nbsp; {{ item.result.text }}&nbsp;
</span> </span>
<span class="location"> <span class="location">
<template v-if="hasAddress"> <template v-if="hasAddress">
@ -24,6 +24,12 @@
:entity="item.result" :entity="item.result"
:options="{ displayLong: true }"> :options="{ displayLong: true }">
</badge-entity> </badge-entity>
<on-the-fly v-if="item.result.kind === 'company'"
v-bind:parent="item.result"
@saveFormOnTheFly="saveFormOnTheFly"
action="addContact"
ref="onTheFly"
></on-the-fly>
<on-the-fly <on-the-fly
type="thirdparty" type="thirdparty"
v-bind:id="item.result.id" v-bind:id="item.result.id"
@ -35,6 +41,7 @@
<script> <script>
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue'; import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue'; import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
const i18n = { const i18n = {
messages: { messages: {
@ -55,6 +62,7 @@ export default {
BadgeEntity BadgeEntity
}, },
props: ['item'], props: ['item'],
emits: ['newPriorSuggestion'],
i18n, i18n,
computed: { computed: {
hasAddress() { hasAddress() {
@ -77,7 +85,26 @@ export default {
} }
return null; return null;
}, }
},
methods: {
saveFormOnTheFly({data, type}) {
makeFetch('POST', '/api/1.0/thirdparty/thirdparty.json', data)
.then(response => {
this.$emit('newPriorSuggestion', response);
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === 'ValidationException') {
for (let v of error.violations) {
this.$toast.open({message: v });
}
} else {
this.$toast.open({message: 'An error occurred'});
}
})
}
} }
} }
</script> </script>

View File

@ -12,6 +12,7 @@
<!-- use person-text here to avoid code duplication ? TODO --> <!-- use person-text here to avoid code duplication ? TODO -->
<span class="firstname">{{ person.firstName }}</span> <span class="firstname">{{ person.firstName }}</span>
<span class="lastname">{{ person.lastName }}</span> <span class="lastname">{{ person.lastName }}</span>
<span v-if="person.suffixText" class="suffixtext">&nbsp;{{ person.suffixText }}</span>
<span v-if="person.altNames && options.addAltNames == true" class="altnames"> <span v-if="person.altNames && options.addAltNames == true" class="altnames">
<span :class="'altname altname-' + altNameKey">{{ altNameLabel }}</span> <span :class="'altname altname-' + altNameKey">{{ altNameLabel }}</span>
</span> </span>
@ -20,6 +21,7 @@
<!-- use person-text here to avoid code duplication ? TODO --> <!-- use person-text here to avoid code duplication ? TODO -->
<span class="firstname">{{ person.firstName }}</span> <span class="firstname">{{ person.firstName }}</span>
<span class="lastname">{{ person.lastName }}</span> <span class="lastname">{{ person.lastName }}</span>
<span v-if="person.suffixText" class="suffixtext">&nbsp;{{ person.suffixText }}</span>
<span v-if="person.deathdate" class="deathdate"> ()</span> <span v-if="person.deathdate" class="deathdate"> ()</span>
<span v-if="person.altNames && options.addAltNames == true" class="altnames"> <span v-if="person.altNames && options.addAltNames == true" class="altnames">
<span :class="'altname altname-' + altNameKey">{{ altNameLabel }}</span> <span :class="'altname altname-' + altNameKey">{{ altNameLabel }}</span>
@ -85,6 +87,15 @@
</p> </p>
</li> </li>
<li v-if="person.email">
<i class="fa fa-li fa-envelope-o"></i>
<a :href="'mailto: ' + person.email">{{ person.email }}</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-envelope-o"></i>
<p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
</li>
<li v-if="person.mobilenumber"> <li v-if="person.mobilenumber">
<i class="fa fa-li fa-mobile"></i> <i class="fa fa-li fa-mobile"></i>
<a :href="'tel: ' + person.mobilenumber">{{ person.mobilenumber }}</a> <a :href="'tel: ' + person.mobilenumber">{{ person.mobilenumber }}</a>

View File

@ -6,6 +6,7 @@
<span v-if="person.altNames && person.altNames.length > 0" class="altnames"> <span v-if="person.altNames && person.altNames.length > 0" class="altnames">
<span :class="'altname altname-' + altNameKey"> ({{ altNameLabel }})</span> <span :class="'altname altname-' + altNameKey"> ({{ altNameLabel }})</span>
</span> </span>
<span v-if="person.suffixText" class="suffixtext">&nbsp;{{ person.suffixText }}</span>
<span class="age" v-if="this.addAge && person.birthdate !== null && person.deathdate === null">{{ $tc('renderbox.years_old', person.age) }}</span> <span class="age" v-if="this.addAge && person.birthdate !== null && person.deathdate === null">{{ $tc('renderbox.years_old', person.age) }}</span>
<span v-else-if="this.addAge && person.deathdate !== null">&nbsp;()</span> <span v-else-if="this.addAge && person.deathdate !== null">&nbsp;()</span>
</span> </span>

View File

@ -101,14 +101,31 @@
{% if notif_counter.total > 0 %} {% if notif_counter.total > 0 %}
{{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', w.id) }} {{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', w.id) }}
{% endif %} {% endif %}
{% import '@ChillPerson/Macro/updatedBy.html.twig' as macro %} {% import '@ChillPerson/Macro/updatedBy.html.twig' as macro %}
{{ macro.updatedBy(w) }} {{ macro.updatedBy(w) }}
</div> </div>
{% if displayAction is defined and displayAction == true %} {% if displayAction is defined and displayAction == true %}
<div class="item-col"> <div class="item-col">
<ul class="record_actions"> <ul class="record_actions">
{% set suppEvaluations = [] %}
{% for e in w.accompanyingPeriodWorkEvaluations %}
{% set suppEvaluations = suppEvaluations|merge([
{'relatedEntityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation', 'relatedEntityId': e.id }
]) %}
{% for d in e.documents %}
{% set suppEvaluations = suppEvaluations|merge([
{'relatedEntityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument', 'relatedEntityId': d.id }
]) %}
{% endfor %}
{% endfor %}
<li>
{{ chill_entity_workflow_list(
'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork',
w.id, [], suppEvaluations) }}
</li>
<li> <li>
<a class="btn btn-edit" title="{{ 'Edit'|trans }}" <a class="btn btn-edit" title="{{ 'Edit'|trans }}"
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}" href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"

View File

@ -2,6 +2,16 @@
{% block title 'accompanying_course_work.List accompanying course work'|trans %} {% block title 'accompanying_course_work.List accompanying course work'|trans %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
{% endblock %}
{% block content %} {% block content %}
<div class="accompanying_course_work"> <div class="accompanying_course_work">

View File

@ -12,6 +12,7 @@
* hLevel integer * hLevel integer
* addDeath bool * addDeath bool
* addAgeBadge bool * addAgeBadge bool
* suffixText bool
* address_multiline bool * address_multiline bool
* customButtons [ * customButtons [
'before' Twig\Markup, (injected with macro) 'before' Twig\Markup, (injected with macro)
@ -46,6 +47,11 @@
<span>({{- 'years_old'|trans({ 'age': person.age }) -}})</span> <span>({{- 'years_old'|trans({ 'age': person.age }) -}})</span>
{% endif %} {% endif %}
{% endif %} {% endif %}
{%- if options['suffixText'] is defined -%}
{% for o in options['suffixText'] %}
<span>{{ o }}</span>
{% endfor %}
{% endif %}
{% endmacro raw %} {% endmacro raw %}
{% macro label(person, options) %} {% macro label(person, options) %}

View File

@ -3,10 +3,27 @@
{{ 'Last updated on'|trans }} {{ 'Last updated on'|trans }}
<span class="date"> <span class="date">
{{ entity.updatedAt|format_datetime('medium', 'short') }} {{ entity.updatedAt|format_datetime('medium', 'short') }}
</span>,
{{ 'by_user'|trans }}
<span class="user">
{{ entity.updatedBy|chill_entity_render_box }}
</span> </span>
{% if entity.updatedBy %}
{{ ', ' ~ 'by_user'|trans }}
<span class="user">
{{ entity.updatedBy|chill_entity_render_box }}
</span>
{% endif %}
</div>
{% endmacro %}
{% macro createdBy(entity) %}
<div class="updatedBy">
{{ 'Created on'|trans }}
<span class="date">
{{ entity.createdAt|format_datetime('medium', 'short') }}
</span>
{% if entity.createdBy %}
{{ ', ' ~ 'by_user'|trans }}
<span class="user">
{{ entity.createdBy|chill_entity_render_string }}
</span>
{% endif %}
</div> </div>
{% endmacro %} {% endmacro %}

View File

@ -1,19 +0,0 @@
{% import '@ChillMain/Workflow/macro_breadcrumb.html.twig' as m %}
<div class="flex-grow-1 {% if add_classes is defined %}{{ add_classes }}{% else %}h2{% endif %}">
<div>
{% if concerne is defined and concerne == true %}
<span class="item-key">{{ 'Concerne'|trans }}: </span>
{% endif %}
{{ 'workflow.Work (n°%w%)'|trans({'%w%': work.id }) }}
{% if description is defined and description == true %}
{{ ' — ' ~ work.socialAction|chill_entity_render_string }}
{% endif %}
</div>
{% if breadcrumb is defined and breadcrumb == true %}
{{ m.breadcrumb(_context) }}
{% endif %}
</div>

View File

@ -62,10 +62,14 @@
</li> </li>
{% endif %} {% endif %}
<li> <li>
{% if evaluation.createdBy is not null %}
<span class="item-key">créé par</span> <span class="item-key">créé par</span>
<b>{{ evaluation.createdBy.username }}</b> <b>{{ evaluation.createdBy.username }}</b>
{% endif %}
{% if evaluation.createdAt is not null %}
<span class="item-key">{{ 'le'|trans }}</span> <span class="item-key">{{ 'le'|trans }}</span>
<b>{{ evaluation.createdAt|format_date('short') }}</b> <b>{{ evaluation.createdAt|format_date('short') }}</b>
{% endif %}
</li> </li>
</ul> </ul>
{% if evaluation.comment %} {% if evaluation.comment %}

View File

@ -1,19 +0,0 @@
{% import '@ChillMain/Workflow/macro_breadcrumb.html.twig' as m %}
<div class="flex-grow-1 {% if add_classes is defined %}{{ add_classes }}{% else %}h2{% endif %}">
<div>
{% if concerne is defined and concerne == true %}
<span class="item-key">{{ 'Concerne'|trans }}: </span>
{% endif %}
{{ 'workflow.Evaluation (n°%eval%)'|trans({'%eval%': evaluation.id}) }}
{% if description is defined and description == true %}
{{ ' — ' ~ evaluation.evaluation.title|localize_translatable_string }}
{% endif %}
</div>
{% if breadcrumb is defined and breadcrumb == true %}
{{ m.breadcrumb(_context) }}
{% endif %}
</div>

View File

@ -0,0 +1,103 @@
<div class="flex-table accompanying_course_work-list">
<div class="item-bloc evaluation-item bg-chill-llight-gray">
<div class="item-row mb-2">
<h2 class="badge-title">
<span class="title_label"></span>
<span class="title_action">
{{ evaluation.accompanyingPeriodWork.socialAction|chill_entity_render_string }}
<ul class="small_in_title columns mt-1">
<li>
<span class="item-key">{{ 'accompanying_course_work.start_date'|trans ~ ' : ' }}</span>
<b>{{ evaluation.accompanyingPeriodWork.startDate|format_date('short') }}</b>
</li>
{% if evaluation.accompanyingPeriodWork.endDate %}
<li>
<span class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
<b>{{ evaluation.accompanyingPeriodWork.endDate|format_date('short') }}</b>
</li>
{% endif %}
</ul>
</span>
</h2>
</div>
<div class="item-row column">
<table class="obj-res-eval my-3" style="font-size: 110% !important;">
<thead>
<tr>
<th class="eval">
<h4 class="title_label">
{{ 'Évaluation'|trans }}
</h4>
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="eval">
<ul class="eval_title">
<li class="my-2">
{{ evaluation.evaluation.title|localize_translatable_string }}
<ul class="columns pt-2">
<li>
<span class="item-key">{{ 'accompanying_course_work.start_date'|trans ~ ' : ' }}</span>
<b>{{ evaluation.startDate|format_date('short') }}</b>
</li>
{% if evaluation.endDate %}
<li>
<span class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
<b>{{ evaluation.endDate|format_date('short') }}</b>
</li>
{% endif %}
{% if evaluation.maxDate %}
<li>
<span class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
<b>{{ evaluation.maxDate|format_date('short') }}</b>
</li>
{% endif %}
{% if evaluation.warningInterval and evaluation.warningInterval.d > 0 %}
<li>
{% set days = (evaluation.warningInterval.d + evaluation.warningInterval.m * 30) %}
<span class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
{{ 'accompanying_course_work.%days% days before max_date'|trans({'%days%': days }) }}
</li>
{% endif %}
<li>
{% if evaluation.createdBy is not null %}
<span class="item-key">créé par</span>
<b>{{ evaluation.createdBy.username }}</b>
{% endif %}
{% if evaluation.createdAt is not null %}
<span class="item-key">{{ 'le'|trans }}</span>
<b>{{ evaluation.createdAt|format_date('short') }}</b>
{% endif %}
</li>
</ul>
{% if evaluation.comment %}
<blockquote class="chill-user-quote" style="margin-left: 0;">
{{ evaluation.comment }}
</blockquote>
{% endif %}
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<div class="item-row">
{% import '@ChillPerson/Macro/updatedBy.html.twig' as macro %}
{{ macro.updatedBy(evaluation) }}
</div>
</div>
</div>
{% if display_action is defined and display_action == true %}
{# TODO add acl #}
<ul class="record_actions">
<li>
<a class="btn btn-show" href="{{ path('chill_person_accompanying_period_work_edit', {'id': evaluation.accompanyingPeriodWork.id}) }}">
{{ 'Show'|trans }}
</a>
</li>
</ul>
{% endif %}

View File

@ -15,7 +15,7 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonNotDuplicateRepository; use Chill\PersonBundle\Repository\PersonNotDuplicateRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Templating\Entity\PersonRender; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
@ -39,7 +39,7 @@ class SimilarPersonMatcher
protected PersonNotDuplicateRepository $personNotDuplicateRepository; protected PersonNotDuplicateRepository $personNotDuplicateRepository;
protected PersonRender $personRender; protected PersonRenderInterface $personRender;
/** /**
* @var TokenStorageInterface * @var TokenStorageInterface
@ -51,7 +51,7 @@ class SimilarPersonMatcher
AuthorizationHelper $authorizationHelper, AuthorizationHelper $authorizationHelper,
TokenStorageInterface $tokenStorage, TokenStorageInterface $tokenStorage,
PersonNotDuplicateRepository $personNotDuplicateRepository, PersonNotDuplicateRepository $personNotDuplicateRepository,
PersonRender $personRender PersonRenderInterface $personRender
) { ) {
$this->em = $em; $this->em = $em;
$this->authorizationHelper = $authorizationHelper; $this->authorizationHelper = $authorizationHelper;

View File

@ -0,0 +1,62 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Security\Authorization;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use UnexpectedValueException;
/**
* Voter for AccompanyingPeriodWorkEvaluationDocument.
*
* Delegates to the sames authorization than for Evalution
*/
class AccompanyingPeriodWorkEvaluationDocumentVoter extends Voter
{
public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_DOCUMENT_SHOW';
private AccessDecisionManagerInterface $accessDecisionManager;
public function __construct(AccessDecisionManagerInterface $accessDecisionManager)
{
$this->accessDecisionManager = $accessDecisionManager;
}
protected function supports($attribute, $subject)
{
return $subject instanceof AccompanyingPeriodWorkEvaluationDocument
&& self::SEE === $attribute;
}
/**
* @param string $attribute
* @param AccompanyingPeriodWorkEvaluationDocument $subject
*
* @return bool|void
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
switch ($attribute) {
case self::SEE:
return $this->accessDecisionManager->decide(
$token,
[AccompanyingPeriodWorkEvaluationVoter::SEE],
$subject->getAccompanyingPeriodWorkEvaluation()
);
default:
throw new UnexpectedValueException("The attribute {$attribute} is not supported");
}
}
}

View File

@ -0,0 +1,124 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
use function array_key_exists;
use function array_merge;
use function is_array;
/**
* This denormalizer rely on AbstractNormalizer for most of the job, and
* add some logic for synchronizing collection.
*/
class AccompanyingPeriodWorkEvaluationDenormalizer implements ContextAwareDenormalizerInterface, DenormalizerAwareInterface
{
use DenormalizerAwareTrait;
use ObjectToPopulateTrait;
private EntityManagerInterface $em;
private AccompanyingPeriodWorkRepository $workRepository;
public function __construct(
AccompanyingPeriodWorkRepository $workRepository,
EntityManagerInterface $em
) {
$this->workRepository = $workRepository;
$this->em = $em;
}
public function denormalize($data, $type, $format = null, array $context = [])
{
$evaluation = $this->denormalizer->denormalize($data, $type, $format, array_merge(
$context,
['skip' => self::class]
));
$this->handleDocumentCollection($data, $evaluation, $format, $context);
return $evaluation;
}
public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
{
return AccompanyingPeriodWorkEvaluation::class === $type
&& self::class !== ($context['skip'] ?? null)
&& is_array($data)
&& array_key_exists('type', $data)
&& 'accompanying_period_work_evaluation' === $data['type'];
}
private function handleDocumentCollection(array $data, AccompanyingPeriodWorkEvaluation $evaluation, string $format, array $context)
{
$dataById = [];
$dataWithoutId = [];
foreach ($data['documents'] as $e) {
if (array_key_exists('id', $e)) {
$dataById[$e['id']] = $e;
} else {
$dataWithoutId[] = $e;
}
}
//partition the separate kept documents and removed one
[$kept, $removed] = $evaluation->getDocuments()
->partition(
static fn (int $key, AccompanyingPeriodWorkEvaluationDocument $a) => array_key_exists($a->getId(), $dataById)
);
// remove the document from evaluation
foreach ($removed as $r) {
$evaluation->removeDocument($r);
}
// handle the documents kept
foreach ($kept as $k) {
$this->denormalizer->denormalize(
$dataById[$k->getId()],
AccompanyingPeriodWorkEvaluationDocument::class,
$format,
array_merge(
$context,
[
'groups' => ['write'],
AbstractNormalizer::OBJECT_TO_POPULATE => $k,
]
)
);
}
// create new document
foreach ($dataWithoutId as $newData) {
$document = $this->denormalizer->denormalize(
$newData,
AccompanyingPeriodWorkEvaluationDocument::class,
$format,
array_merge(
$context,
['groups' => ['accompanying_period_work_evaluation:create']]
)
);
$evaluation->addDocument($document);
}
}
}

View File

@ -0,0 +1,71 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Workflow\Registry;
use function array_key_exists;
class AccompanyingPeriodWorkEvaluationDocumentNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
private const SKIP = 'accompanying_period_work_evaluation_document_skip';
private EntityWorkflowRepository $entityWorkflowRepository;
private MetadataExtractor $metadataExtractor;
private Registry $registry;
public function __construct(EntityWorkflowRepository $entityWorkflowRepository, MetadataExtractor $metadataExtractor, Registry $registry)
{
$this->entityWorkflowRepository = $entityWorkflowRepository;
$this->metadataExtractor = $metadataExtractor;
$this->registry = $registry;
}
public function normalize($object, ?string $format = null, array $context = []): array
{
$initial = $this->normalizer->normalize($object, $format, array_merge($context, [
self::SKIP => spl_object_hash($object),
]));
$initial['workflows_availables'] = $this->metadataExtractor->availableWorkflowFor(
AccompanyingPeriodWorkEvaluationDocument::class,
$object->getId()
);
$workflows = $this->entityWorkflowRepository->findBy([
'relatedEntityClass' => AccompanyingPeriodWorkEvaluationDocument::class,
'relatedEntityId' => $object->getId(),
]);
$initial['workflows'] = $this->normalizer->normalize($workflows, 'json', $context);
return $initial;
}
public function supportsNormalization($data, ?string $format = null, array $context = [])
{
return $data instanceof AccompanyingPeriodWorkEvaluationDocument
&& 'json' === $format
&& (
!array_key_exists(self::SKIP, $context)
|| spl_object_hash($data) !== $context[self::SKIP]
);
}
}

View File

@ -49,6 +49,15 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements ContextAwareNormaliz
[self::IGNORE_EVALUATION => spl_object_hash($object)] [self::IGNORE_EVALUATION => spl_object_hash($object)]
)); ));
// due to bug: https://api-platform.com/docs/core/serialization/#collection-relation
// and also: https://github.com/symfony/symfony/issues/36965
// we have to rewrite the documents as a collection
$initial['documents'] = $this->normalizer->normalize(
$object->getDocuments()->getValues(),
$format,
$context
);
// then, we add normalization for things which are not into the entity // then, we add normalization for things which are not into the entity
$initial['workflows_availables'] = $this->metadataExtractor->availableWorkflowFor( $initial['workflows_availables'] = $this->metadataExtractor->availableWorkflowFor(

View File

@ -16,6 +16,7 @@ use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Workflow\Helper\MetadataExtractor; use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Symfony\Component\Serializer\Exception\ExceptionInterface; use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
@ -56,6 +57,15 @@ class AccompanyingPeriodWorkNormalizer implements ContextAwareNormalizerInterfac
[self::IGNORE_WORK => spl_object_hash($object)] [self::IGNORE_WORK => spl_object_hash($object)]
)); ));
// due to bug: https://api-platform.com/docs/core/serialization/#collection-relation
// and also: https://github.com/symfony/symfony/issues/36965
// we have to rewrite the evaluations as a collection
$initial['accompanyingPeriodWorkEvaluations'] = $this->normalizer->normalize(
$object->getAccompanyingPeriodWorkEvaluations()->getValues(),
$format,
$context
);
// then, we add normalization for things which are not into the entity // then, we add normalization for things which are not into the entity
$initial['workflows_availables'] = $this->metadataExtractor->availableWorkflowFor( $initial['workflows_availables'] = $this->metadataExtractor->availableWorkflowFor(
@ -67,10 +77,15 @@ class AccompanyingPeriodWorkNormalizer implements ContextAwareNormalizerInterfac
AccompanyingPeriodWorkEvaluation::class AccompanyingPeriodWorkEvaluation::class
); );
$initial['workflows_availables_evaluation_documents'] = $this->metadataExtractor->availableWorkflowFor(
AccompanyingPeriodWorkEvaluationDocument::class
);
$workflows = $this->entityWorkflowRepository->findBy([ $workflows = $this->entityWorkflowRepository->findBy([
'relatedEntityClass' => AccompanyingPeriodWork::class, 'relatedEntityClass' => AccompanyingPeriodWork::class,
'relatedEntityId' => $object->getId(), 'relatedEntityId' => $object->getId(),
]); ]);
$initial['workflows'] = $this->normalizer->normalize($workflows, 'json', $context); $initial['workflows'] = $this->normalizer->normalize($workflows, 'json', $context);
return $initial; return $initial;

View File

@ -19,7 +19,7 @@ use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonAltName; use Chill\PersonBundle\Entity\PersonAltName;
use Chill\PersonBundle\Repository\Relationships\RelationshipRepository; use Chill\PersonBundle\Repository\Relationships\RelationshipRepository;
use Chill\PersonBundle\Templating\Entity\PersonRender; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use DateTimeInterface; use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@ -37,7 +37,7 @@ class PersonDocGenNormalizer implements
{ {
use NormalizerAwareTrait; use NormalizerAwareTrait;
private PersonRender $personRender; private PersonRenderInterface $personRender;
private RelationshipRepository $relationshipRepository; private RelationshipRepository $relationshipRepository;
@ -46,7 +46,7 @@ class PersonDocGenNormalizer implements
private TranslatorInterface $translator; private TranslatorInterface $translator;
public function __construct( public function __construct(
PersonRender $personRender, PersonRenderInterface $personRender,
RelationshipRepository $relationshipRepository, RelationshipRepository $relationshipRepository,
TranslatorInterface $translator, TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper TranslatableStringHelper $translatableStringHelper

View File

@ -23,21 +23,15 @@ use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait; use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
use function array_key_exists; use function array_key_exists;
/** /**
* Serialize a Person entity. * Serialize a Person entity.
*/ */
class PersonJsonNormalizer implements class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwareInterface, PersonJsonNormalizerInterface
DenormalizerAwareInterface,
DenormalizerInterface,
NormalizerAwareInterface,
NormalizerInterface
{ {
use DenormalizerAwareTrait; use DenormalizerAwareTrait;

View File

@ -0,0 +1,24 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Serializer\Normalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* Serialize a Person entity.
*/
interface PersonJsonNormalizerInterface extends
DenormalizerInterface,
NormalizerInterface
{
}

View File

@ -23,7 +23,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Templating\Entity\PersonRender; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use DateTime; use DateTime;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
@ -48,7 +48,7 @@ class AccompanyingPeriodContext implements
private NormalizerInterface $normalizer; private NormalizerInterface $normalizer;
private PersonRender $personRender; private PersonRenderInterface $personRender;
private TranslatableStringHelperInterface $translatableStringHelper; private TranslatableStringHelperInterface $translatableStringHelper;
@ -59,7 +59,7 @@ class AccompanyingPeriodContext implements
NormalizerInterface $normalizer, NormalizerInterface $normalizer,
TranslatableStringHelperInterface $translatableStringHelper, TranslatableStringHelperInterface $translatableStringHelper,
EntityManagerInterface $em, EntityManagerInterface $em,
PersonRender $personRender, PersonRenderInterface $personRender,
TranslatorInterface $translator, TranslatorInterface $translator,
BaseContextData $baseContextData BaseContextData $baseContextData
) { ) {

View File

@ -22,7 +22,7 @@ use function array_key_exists;
/** /**
* Render a Person. * Render a Person.
*/ */
class PersonRender extends AbstractChillEntityRender class PersonRender extends AbstractChillEntityRender implements PersonRenderInterface
{ {
private ConfigPersonAltNamesHelper $configAltNamesHelper; private ConfigPersonAltNamesHelper $configAltNamesHelper;
@ -59,6 +59,7 @@ class PersonRender extends AbstractChillEntityRender
'customArea' => $options['customArea'] ?? [], 'customArea' => $options['customArea'] ?? [],
'addDeath' => $options['addDeath'] ?? true, 'addDeath' => $options['addDeath'] ?? true,
'addAgeBadge' => $options['addAgeBadge'] ?? false, 'addAgeBadge' => $options['addAgeBadge'] ?? false,
'suffixText' => $options['suffixText'] ?? [],
]; ];
return return

View File

@ -0,0 +1,21 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Templating\Entity;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
/**
* Render a Person.
*/
interface PersonRenderInterface extends ChillEntityRenderInterface
{
}

View File

@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod; namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Templating\Entity\PersonRender; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedTypeException;
@ -20,9 +20,9 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException;
class LocationValidityValidator extends ConstraintValidator class LocationValidityValidator extends ConstraintValidator
{ {
private PersonRender $render; private PersonRenderInterface $render;
public function __construct(PersonRender $render) public function __construct(PersonRenderInterface $render)
{ {
$this->render = $render; $this->render = $render;
} }

View File

@ -13,7 +13,7 @@ namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod;
use Chill\MainBundle\Util\DateRangeCovering; use Chill\MainBundle\Util\DateRangeCovering;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Templating\Entity\PersonRender; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender; use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
@ -26,11 +26,11 @@ class ParticipationOverlapValidator extends ConstraintValidator
{ {
private const MAX_PARTICIPATION = 1; private const MAX_PARTICIPATION = 1;
private PersonRender $personRender; private PersonRenderInterface $personRender;
private ThirdPartyRender $thirdpartyRender; private ThirdPartyRender $thirdpartyRender;
public function __construct(PersonRender $personRender, ThirdPartyRender $thirdPartyRender) public function __construct(PersonRenderInterface $personRender, ThirdPartyRender $thirdPartyRender)
{ {
$this->personRender = $personRender; $this->personRender = $personRender;
$this->thirdpartyRender = $thirdPartyRender; $this->thirdpartyRender = $thirdPartyRender;

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