diff --git a/CHANGELOG.md b/CHANGELOG.md
index 549276554..4cf2fa64c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,11 +11,29 @@ and this project adheres to
## Unreleased
-* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
-* [parcours] bugfix if deathdate is not defined (eg. for a thirdparty) parcours is still displayed. Gave error before.
## Test releases
+### test release 2022-01-24
+
+* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
+* [notification: formulaire création] descend la box avec la description dans le bas du formulaire
+* [notification for activity]: fix link to activity
+* [notification] add "URGENT" before accompanying course with emergency = true
+* [notification] add a "read more" button on system notification
+* [notification] add `[Chill]` in the subject of each notification, automatically
+* [notification] add a counter for notification in activity list and accompanying period list, and search results
+* [parcours] bugfix if deathdate is not defined (eg. for a thirdparty) parcours is still displayed. Gave error before.
+* [workflow] add breadcrumb to show steps
+* [popover] add popover html popup mechanism (used by workflow breadcrumb)
+* [templates] improve updatedBy macro in item metadatas
+* [parcours]: bug fix when comment is pinned all other comments remain in the collection (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/385)
+* [workflow]
+ * add My workflow section with my opened subscriptions
+ * apply workflow on documents, accompanyingCourseWork and Evaluations
+* [wopi-link] a new vue component allow to open wopi link in a fullscreen chill-themed modal
+
+
### test release 2022-01-19
* vuejs: add dead information on all on-the-fly person render boxes, in vis graph and other templates (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/271)
* [thirdparty] fix bug in 3rd party view: types was replaced by thirdPartyTypes
diff --git a/composer.json b/composer.json
index d3766bb65..2223f65c0 100644
--- a/composer.json
+++ b/composer.json
@@ -52,9 +52,6 @@
"twig/string-extra": "^3.3",
"twig/twig": "^3.0"
},
- "conflict": {
- "symfony/symfony": "*"
- },
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"drupol/php-conventions": "^5",
@@ -71,23 +68,17 @@
"symfony/var-dumper": "^4.4",
"symfony/web-profiler-bundle": "^4.4"
},
- "config": {
- "bin-dir": "bin",
- "optimize-autoloader": true,
- "sort-packages": true,
- "vendor-dir": "tests/app/vendor",
- "allow-plugins": {
- "composer/package-versions-deprecated": true,
- "phpstan/extension-installer": true,
- "ergebnis/composer-normalize": true,
- "phpro/grumphp": true
- }
+ "conflict": {
+ "symfony/symfony": "*"
},
"autoload": {
"psr-4": {
"Chill\\ActivityBundle\\": "src/Bundle/ChillActivityBundle",
+ "Chill\\AsideActivityBundle\\": "src/Bundle/ChillAsideActivityBundle/src",
"Chill\\BudgetBundle\\": "src/Bundle/ChillBudgetBundle",
+ "Chill\\CalendarBundle\\": "src/Bundle/ChillCalendarBundle",
"Chill\\CustomFieldsBundle\\": "src/Bundle/ChillCustomFieldsBundle",
+ "Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
"Chill\\FamilyMemberBundle\\": "src/Bundle/ChillFamilyMemberBundle",
@@ -96,9 +87,6 @@
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
- "Chill\\AsideActivityBundle\\": "src/Bundle/ChillAsideActivityBundle/src",
- "Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
- "Chill\\CalendarBundle\\": "src/Bundle/ChillCalendarBundle",
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src"
}
},
@@ -111,10 +99,10 @@
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true,
- "phpstan/extension-installer": true,
"ergebnis/composer-normalize": true,
+ "ocramius/package-versions": true,
"phpro/grumphp": true,
- "ocramius/package-versions": true
+ "phpstan/extension-installer": true
},
"bin-dir": "bin",
"optimize-autoloader": true,
@@ -123,8 +111,8 @@
},
"scripts": {
"auto-scripts": {
- "cache:clear": "symfony-cmd",
- "assets:install %PUBLIC_DIR%": "symfony-cmd"
+ "assets:install %PUBLIC_DIR%": "symfony-cmd",
+ "cache:clear": "symfony-cmd"
}
}
}
diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php
index 6db1f6945..2947fda38 100644
--- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php
+++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php
@@ -31,6 +31,7 @@ use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
+use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@@ -38,8 +39,8 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role;
-use Symfony\Component\Serializer\SerializerInterface;
+use Symfony\Component\Serializer\SerializerInterface;
use function array_key_exists;
final class ActivityController extends AbstractController
@@ -471,20 +472,21 @@ final class ActivityController extends AbstractController
public function showAction(Request $request, int $id): Response
{
- $view = null;
+ $entity = $this->activityRepository->find($id);
- [$person, $accompanyingPeriod] = $this->getEntity($request);
+ if (null === $entity) {
+ throw $this->createNotFoundException('Unable to find Activity entity.');
+ }
+
+ $accompanyingPeriod = $entity->getAccompanyingPeriod();
+ $person = $entity->getPerson();
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:showAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:showPerson.html.twig';
- }
-
- $entity = $this->activityRepository->find($id);
-
- if (null === $entity) {
- throw $this->createNotFoundException('Unable to find Activity entity.');
+ } else {
+ throw new RuntimeException('the activity should be linked with a period or person');
}
if (null !== $accompanyingPeriod) {
@@ -493,8 +495,7 @@ final class ActivityController extends AbstractController
$entity->personsNotAssociated = $entity->getPersonsNotAssociated();
}
- // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
- // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity);
+ $this->denyAccessUnlessGranted(ActivityVoter::SEE, $entity);
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php
index 77f650ce4..75c2aeaf6 100644
--- a/src/Bundle/ChillActivityBundle/Entity/Activity.php
+++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php
@@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Chill\ActivityBundle\Validator\Constraints as ActivityValidator;
-use Chill\DocStoreBundle\Entity\Document;
+use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\HasCenterInterface;
@@ -61,13 +61,13 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod")
- * @Groups({"read"})
+ * @Groups({"read", "docgen:read"})
*/
private ?AccompanyingPeriod $accompanyingPeriod = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityType")
- * @Groups({"read"})
+ * @Groups({"read", "docgen:read"})
* @SerializedName("activityType")
* @ORM\JoinColumn(name="type_id")
*/
@@ -107,13 +107,13 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
- * @Groups({"read"})
+ * @Groups({"read", "docgen:read"})
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location")
- * @groups({"read"})
+ * @groups({"read", "docgen:read"})
*/
private ?Location $location = null;
@@ -124,7 +124,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person")
- * @Groups({"read"})
+ * @Groups({"read", "docgen:read"})
*/
private ?Collection $persons = null;
@@ -146,20 +146,20 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\SocialWork\SocialAction")
* @ORM\JoinTable(name="chill_activity_activity_chill_person_socialaction")
- * @Groups({"read"})
+ * @Groups({"read", "docgen:read"})
*/
private Collection $socialActions;
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\SocialWork\SocialIssue")
* @ORM\JoinTable(name="chill_activity_activity_chill_person_socialissue")
- * @Groups({"read"})
+ * @Groups({"read", "docgen:read"})
*/
private Collection $socialIssues;
/**
* @ORM\ManyToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
- * @Groups({"read"})
+ * @Groups({"read", "docgen:read"})
*/
private ?Collection $thirdParties = null;
@@ -191,7 +191,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
$this->socialActions = new ArrayCollection();
}
- public function addDocument(Document $document): self
+ public function addDocument(StoredObject $document): self
{
$this->documents[] = $document;
@@ -425,7 +425,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->getEmergency();
}
- public function removeDocument(Document $document): void
+ public function removeDocument(StoredObject $document): void
{
$this->documents->removeElement($document);
}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/_list_item.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/_list_item.html.twig
index 09a7ab2a3..6b2e33dfb 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/_list_item.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/_list_item.html.twig
@@ -143,9 +143,17 @@
-
+
+ {% set notif_counter = chill_count_notifications('Chill\\ActivityBundle\\Entity\\Activity', activity.id) %}
+ {% if notif_counter.total > 0 %}
+ {{ chill_counter_notifications('Chill\\ActivityBundle\\Entity\\Activity', activity.id) }}
+ {% endif %}
+
+
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig
index 33575cea6..d9d72845a 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig
@@ -8,7 +8,7 @@
action: 'show', displayBadge: true,
targetEntity: { name: type, id: entity.id },
buttonText: entity|chill_entity_render_string,
- isDead: entity.deathdate is not null,
+ isDead: entity.deathdate is defined and entity.deathdate is not null,
parent: parent
} %}
{% endmacro %}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/edit.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/edit.html.twig
index 235a5b7f2..a59c596c3 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/edit.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/edit.html.twig
@@ -83,15 +83,15 @@
{{ form_row(edit_form.comment) }}
{% endif %}
-{%- if edit_form.documents is defined -%}
- {{ form_row(edit_form.documents) }}
-{% endif %}
-
{%- if edit_form.attendee is defined -%}
{{ form_row(edit_form.attendee) }}
{% endif %}
-{# TODO .. status #}
+{%- if edit_form.documents is defined -%}
+ {{ form_row(edit_form.documents) }}
+{% endif %}
+
+
{% set person_id = null %}
{% if entity.person %}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/editAccompanyingCourse.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/editAccompanyingCourse.html.twig
index b278c0300..09ff16fec 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/editAccompanyingCourse.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/editAccompanyingCourse.html.twig
@@ -24,10 +24,12 @@
window.activity = {{ activity_json|json_encode|raw }};
{{ encore_entry_script_tags('vue_activity') }}
+ {{ encore_entry_script_tags('mod_docgen_picktemplate') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_link_tags('vue_activity') }}
+ {{ encore_entry_link_tags('mod_docgen_picktemplate') }}
{% endblock %}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/editPerson.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/editPerson.html.twig
index 82c7403c6..72c74c68e 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/editPerson.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/editPerson.html.twig
@@ -39,9 +39,11 @@
window.activity = {{ activity_json|json_encode|raw }};
{{ encore_entry_script_tags('vue_activity') }}
+ {{ encore_entry_script_tags('mod_docgen_picktemplate') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_link_tags('vue_activity') }}
+ {{ encore_entry_link_tags('mod_docgen_picktemplate') }}
{% endblock %}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig
index a64142863..7ae24fa4b 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig
@@ -1,58 +1,60 @@
{% macro recordAction(activity, context = null, person_id = null, accompanying_course_id = null) %}
- {% if no_action is not defined or no_action == false %}
-
- {{ 'notification.Notify'|trans }}
-
- {% endif %}
- {% if context == 'person' and activity.accompanyingPeriod is not empty %}
- {#
- Disable person_id in following links, for redirect to accompanyingCourse context
- #}
- {% set person_id = null %}
- {% set accompanying_course_id = activity.accompanyingPeriod.id %}
-
-
-
- {{ 'Period number %number%'|trans({'%number%': accompanying_course_id}) }}
-
-
- {% endif %}
-
-
-
- {% if no_action is not defined or no_action == false %}
- {% if is_granted('CHILL_ACTIVITY_UPDATE', activity) %}
+ {% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) %}
+ {% if no_action is not defined or no_action == false %}
-
+ {{ 'notification.Notify'|trans }}
{% endif %}
- {% if is_granted('CHILL_ACTIVITY_DELETE', activity) %}
+ {% if context == 'person' and activity.accompanyingPeriod is not empty %}
+ {#
+ Disable person_id in following links, for redirect to accompanyingCourse context
+ #}
+ {% set person_id = null %}
+ {% set accompanying_course_id = activity.accompanyingPeriod.id %}
-
+ class="btn btn-primary"
+ title="{{ 'See activity in accompanying course context'|trans }}">
+
+ {{ 'Period number %number%'|trans({'%number%': accompanying_course_id}) }}
+
{% endif %}
+
+
+
+ {% if no_action is not defined or no_action == false %}
+ {% if is_granted('CHILL_ACTIVITY_UPDATE', activity) %}
+
+
+
+ {% endif %}
+ {% if is_granted('CHILL_ACTIVITY_DELETE', activity) %}
+
+
+
+ {% endif %}
+ {% endif %}
{% endif %}
{% endmacro %}
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig
index b67ff08ef..546ba99ce 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig
@@ -86,7 +86,7 @@
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {
'context': context,
'render': 'bloc',
- 'badge_person': 'true'
+ 'badge_person': true
} %}
{{ 'Activity data'|trans }}
diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php
new file mode 100644
index 000000000..9a8e6b3b8
--- /dev/null
+++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php
@@ -0,0 +1,220 @@
+documentCategoryRepository = $documentCategoryRepository;
+ $this->normalizer = $normalizer;
+ $this->translatableStringHelper = $translatableStringHelper;
+ $this->em = $em;
+ $this->personRender = $personRender;
+ $this->translator = $translator;
+ $this->baseContextData = $baseContextData;
+ }
+
+ public function adminFormReverseTransform(array $data): array
+ {
+ return $data;
+ }
+
+ public function adminFormTransform(array $data): array
+ {
+ return [
+ 'mainPerson' => $data['mainPerson'] ?? false,
+ 'mainPersonLabel' => $data['mainPersonLabel'] ?? $this->translator->trans('docgen.Main person'),
+ 'person1' => $data['person1'] ?? false,
+ 'person1Label' => $data['person1Label'] ?? $this->translator->trans('docgen.person 1'),
+ 'person2' => $data['person2'] ?? false,
+ 'person2Label' => $data['person2Label'] ?? $this->translator->trans('docgen.person 2'),
+ ];
+ }
+
+ public function buildAdminForm(FormBuilderInterface $builder): void
+ {
+ $builder
+ ->add('mainPerson', CheckboxType::class, [
+ 'required' => false,
+ 'label' => 'docgen.Ask for main person',
+ ])
+ ->add('mainPersonLabel', TextType::class, [
+ 'label' => 'main person label',
+ 'required' => true,
+ ])
+ ->add('person1', CheckboxType::class, [
+ 'required' => false,
+ 'label' => 'docgen.Ask for person 1',
+ ])
+ ->add('person1Label', TextType::class, [
+ 'label' => 'person 1 label',
+ 'required' => true,
+ ])
+ ->add('person2', CheckboxType::class, [
+ 'required' => false,
+ 'label' => 'docgen.Ask for person 2',
+ ])
+ ->add('person2Label', TextType::class, [
+ 'label' => 'person 2 label',
+ 'required' => true,
+ ]);
+ }
+
+ /**
+ * @param Activity $entity
+ */
+ public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
+ {
+ $options = $template->getOptions();
+ $persons = $entity->getPersons();
+
+ foreach (['mainPerson', 'person1', 'person2'] as $key) {
+ if ($options[$key] ?? false) {
+ $builder->add($key, EntityType::class, [
+ 'class' => Person::class,
+ 'choices' => $persons,
+ 'choice_label' => function (Person $p) {
+ return $this->personRender->renderString($p, []);
+ },
+ 'multiple' => false,
+ 'expanded' => true,
+ 'label' => $options[$key . 'Label'],
+ ]);
+ }
+ }
+ }
+
+ public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
+ {
+ if (!$entity instanceof Activity) {
+ throw new UnexpectedTypeException($entity, Activity::class);
+ }
+ $options = $template->getOptions();
+
+ $data = [];
+ $data = array_merge($data, $this->baseContextData->getData());
+ $data['activity'] = $this->normalizer->normalize($entity, 'docgen', ['docgen:expects' => Activity::class, 'groups' => 'docgen:read']);
+
+ $data['course'] = $this->normalizer->normalize($entity->getAccompanyingPeriod(), 'docgen', ['docgen:expects' => AccompanyingPeriod::class, 'groups' => 'docgen:read']);
+ $data['person'] = $this->normalizer->normalize($entity->getPerson(), 'docgen', ['docgen:expects' => Person::class, 'groups' => 'docgen:read']);
+
+ foreach (['mainPerson', 'person1', 'person2'] as $k) {
+ if ($options[$k]) {
+ $data[$k] = $this->normalizer->normalize($contextGenerationData[$k], 'docgen', [
+ 'docgen:expects' => Person::class,
+ 'groups' => 'docgen:read',
+ 'docgen:person:with-household' => true,
+ 'docgen:person:with-relations' => true,
+ ]);
+ }
+ }
+
+ return $data;
+ }
+
+ public function getDescription(): string
+ {
+ return 'docgen.A basic context for activity';
+ }
+
+ public function getEntityClass(): string
+ {
+ return Activity::class;
+ }
+
+ public function getFormData(DocGeneratorTemplate $template, $entity): array
+ {
+ return [
+ 'activity' => $entity,
+ ];
+ }
+
+ public static function getKey(): string
+ {
+ return self::class;
+ }
+
+ public function getName(): string
+ {
+ return 'docgen.Activity basic';
+ }
+
+ public function hasAdminForm(): bool
+ {
+ return true;
+ }
+
+ public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
+ {
+ $options = $template->getOptions();
+
+ return $options['mainPerson'] || $options['person1'] || $options['person2'];
+ }
+
+ /**
+ * @param Activity $entity
+ */
+ public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
+ {
+ $doc = new StoredObject();
+ // TODO push document to remote
+
+ $this->em->persist($doc);
+
+ $entity->addDocument($doc);
+ }
+}
diff --git a/src/Bundle/ChillActivityBundle/Tests/Security/Authorization/ActivityVoterTest.php b/src/Bundle/ChillActivityBundle/Tests/Security/Authorization/ActivityVoterTest.php
index b3d8472c6..3ca5aab45 100644
--- a/src/Bundle/ChillActivityBundle/Tests/Security/Authorization/ActivityVoterTest.php
+++ b/src/Bundle/ChillActivityBundle/Tests/Security/Authorization/ActivityVoterTest.php
@@ -29,9 +29,13 @@ use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
final class ActivityVoterTest extends KernelTestCase
{
use PrepareActivityTrait;
+
use PrepareCenterTrait;
+
use PreparePersonTrait;
+
use PrepareScopeTrait;
+
use PrepareUserTrait;
/**
diff --git a/src/Bundle/ChillActivityBundle/config/services.yaml b/src/Bundle/ChillActivityBundle/config/services.yaml
index 1ca413f0e..23f00ed4e 100644
--- a/src/Bundle/ChillActivityBundle/config/services.yaml
+++ b/src/Bundle/ChillActivityBundle/config/services.yaml
@@ -32,3 +32,8 @@ services:
autowire: true
autoconfigure: true
resource: '../Validator/Constraints/'
+
+ Chill\ActivityBundle\Service\DocGenerator\:
+ autowire: true
+ autoconfigure: true
+ resource: '../Service/DocGenerator/'
diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml
index ed7de9c66..81a34d47d 100644
--- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml
+++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml
@@ -228,3 +228,7 @@ See activity in accompanying course context: Voir l'activité dans le contexte d
You get notified of an activity which does not exists any more: Cette notification ne correspond pas à une activité valide.
you are not allowed to see it details: La notification fait référence à une activité à laquelle vous n'avez pas accès.
This is the minimal activity data: Activité n°
+
+docgen:
+ Activity basic: Echange
+ A basic context for activity: Contexte pour les échanges
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.html.twig b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.html.twig
new file mode 100644
index 000000000..4ec11a8e8
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.html.twig
@@ -0,0 +1,52 @@
+{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
+
+
+
+
+
+
+
+
+
{{ document.title }}
+
{{ document.object.type }}
+
+ {% if document.description is not empty %}
+
+ {{ document.description }}
+
+ {% endif %}
+
+
+
+
+
+
+{% if display_action is defined and display_action == true %}
+
+
+ {{ m.download_button(document.object, document.title) }}
+
+
+
+ {#
+ data-button is optional !
+ OPTIONS:
+ 'changeIcon' string
+ 'changeClass' string
+ 'noText' boolean
+
+ #}{% set button = {
+ 'changeIcon': 'fa-unlock',
+ } %}
+
+ {# vue component #}
+
+
+
+{% endif %}
\ No newline at end of file
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.title.html.twig b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.title.html.twig
new file mode 100644
index 000000000..e3c1dc50a
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.title.html.twig
@@ -0,0 +1,19 @@
+{% import '@ChillMain/Workflow/macro_breadcrumb.html.twig' as m %}
+
+
+
+ {% if concerne is defined and concerne == true %}
+ {{ 'Concerne'|trans }}:
+ {% endif %}
+
+ {{ 'workflow.Document (n°%doc%)'|trans({'%doc%': document.id}) }}
+
+ {% if description is defined and description == true %}
+ {{ ' — ' ~ document.title }}
+ {% endif %}
+
+
+ {% if breadcrumb is defined and breadcrumb == true %}
+ {{ m.breadcrumb(_context) }}
+ {% endif %}
+
\ No newline at end of file
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/show.html.twig b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/show.html.twig
index 97299675a..9d6175e12 100644
--- a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/show.html.twig
+++ b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/show.html.twig
@@ -6,61 +6,71 @@
{% block title %}
{# {{ 'Detail of document of %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }} #}
-{% endblock %}
-
-
-{% block js %}
- {{ parent() }}
- {{ encore_entry_script_tags('mod_async_upload') }}
+ {{ 'Document %title%' | trans({ '%title%': document.title }) }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_async_upload') }}
+ {{ encore_entry_link_tags('mod_entity_workflow_pick') }}
{% endblock %}
{% block content %}
-
- {{ 'Document %title%' | trans({ '%title%': document.title }) }}
-
-
- {{ 'Title'|trans }}
- {{ document.title }}
-
+
+
{{ block('title') }}
+
+
+ {{ 'Title'|trans }}
+ {{ document.title }}
+
{% if document.category is not null %}
{{ 'Category'|trans }}
{{ document.category.name|localize_translatable_string }}
{% endif %}
+
+ {{ 'Description' | trans }}
+
+ {% if document.description is empty %}
+ {{ 'Any description'|trans }}
+ {% else %}
+
+ {{ document.description|chill_markdown_to_html }}
+
+ {% endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block block_post_menu %}
+
+{% endblock %}
- {{ 'Description' | trans }}
-
- {% if document.description is empty %}
- {{ 'Any description'|trans }}
- {% else %}
-
- {{ document.description|chill_markdown_to_html }}
-
- {% endif %}
-
-
-
-
-
-
-
- {{ 'Back to the list' | trans }}
-
-
-
-
- {{ m.download_button(document.object, document.title) }}
-
-
- {% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %}
-
-
- {{ 'Edit' | trans }}
-
-
- {% endif %}
- {% endblock %}
+{% block js %}
+ {{ parent() }}
+ {{ encore_entry_script_tags('mod_async_upload') }}
+ {{ encore_entry_script_tags('mod_entity_workflow_pick') }}
+{% endblock %}
\ No newline at end of file
diff --git a/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php b/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php
new file mode 100644
index 000000000..a615ddda2
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php
@@ -0,0 +1,74 @@
+repository = $em->getRepository(AccompanyingCourseDocument::class);
+ }
+
+ public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingCourseDocument
+ {
+ return $this->repository->find($entityWorkflow->getRelatedEntityId());
+ }
+
+ public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
+ {
+ return null;
+ }
+
+ public function getTemplate(EntityWorkflow $entityWorkflow, array $options = []): string
+ {
+ return '@ChillDocStore/AccompanyingCourseDocument/_workflow.html.twig';
+ }
+
+ public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array
+ {
+ return [
+ 'entity_workflow' => $entityWorkflow,
+ 'document' => $this->getRelatedEntity($entityWorkflow),
+ ];
+ }
+
+ 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
+ {
+ return $entityWorkflow->getRelatedEntityClass() === AccompanyingCourseDocument::class;
+ }
+
+ public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
+ {
+ return true;
+ }
+}
diff --git a/src/Bundle/ChillDocStoreBundle/config/services.yaml b/src/Bundle/ChillDocStoreBundle/config/services.yaml
index 2766cb1c7..6685d22eb 100644
--- a/src/Bundle/ChillDocStoreBundle/config/services.yaml
+++ b/src/Bundle/ChillDocStoreBundle/config/services.yaml
@@ -27,3 +27,8 @@ services:
autoconfigure: true
tags:
- { name: chill.role }
+
+ Chill\DocStoreBundle\Workflow\:
+ resource: './../Workflow/'
+ autoconfigure: true
+ autowire: true
diff --git a/src/Bundle/ChillDocStoreBundle/translations/messages.fr.yml b/src/Bundle/ChillDocStoreBundle/translations/messages.fr.yml
index 947471531..3ee10721c 100644
--- a/src/Bundle/ChillDocStoreBundle/translations/messages.fr.yml
+++ b/src/Bundle/ChillDocStoreBundle/translations/messages.fr.yml
@@ -9,6 +9,7 @@ Create new document: Créer un nouveau document
New document for %name%: Nouveau document pour %name%
Editing document for %name%: Modification d'un document pour %name%
Edit Document: Modification d'un document
+Update document: Modifier le document
Existing document: Document existant
No document to download: Aucun document à télécharger
'Choose a document category': Choisissez une catégorie de document
diff --git a/src/Bundle/ChillMainBundle/ChillMainBundle.php b/src/Bundle/ChillMainBundle/ChillMainBundle.php
index 7a4e563bb..07daf65ea 100644
--- a/src/Bundle/ChillMainBundle/ChillMainBundle.php
+++ b/src/Bundle/ChillMainBundle/ChillMainBundle.php
@@ -31,6 +31,7 @@ use Chill\MainBundle\Security\Resolver\ScopeResolverInterface;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
use Chill\MainBundle\Templating\Entity\CompilerPass as RenderEntityCompilerPass;
use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
+use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -56,6 +57,8 @@ class ChillMainBundle extends Bundle
->addTag('chill_main.notification_handler');
$container->registerForAutoconfiguration(NotificationCounterInterface::class)
->addTag('chill.count_notification.user');
+ $container->registerForAutoconfiguration(EntityWorkflowHandlerInterface::class)
+ ->addTag('chill_main.workflow_handler');
$container->addCompilerPass(new SearchableServicesCompilerPass());
$container->addCompilerPass(new ConfigConsistencyCompilerPass());
diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php
new file mode 100644
index 000000000..dda787708
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php
@@ -0,0 +1,118 @@
+entityManager = $entityManager;
+ $this->security = $security;
+ }
+
+ /**
+ * @Route("/api/1.0/main/workflow/{id}/subscribe", methods={"POST"})
+ */
+ public function subscribe(EntityWorkflow $entityWorkflow, Request $request): Response
+ {
+ return $this->handleSubscription($entityWorkflow, $request, 'subscribe');
+ }
+
+ /**
+ * @Route("/api/1.0/main/workflow/{id}/unsubscribe", methods={"POST"})
+ */
+ public function unsubscribe(EntityWorkflow $entityWorkflow, Request $request): Response
+ {
+ return $this->handleSubscription($entityWorkflow, $request, 'unsubscribe');
+ }
+
+ private function handleSubscription(EntityWorkflow $entityWorkflow, Request $request, string $action): JsonResponse
+ {
+ if (!$this->security->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
+ throw new AccessDeniedException();
+ }
+
+ if (!$request->query->has('subscribe')) {
+ throw new BadRequestHttpException('missing subscribe parameter');
+ }
+
+ $user = $this->security->getUser();
+
+ switch ($request->query->get('subscribe')) {
+ case 'final':
+ switch ($action) {
+ case 'subscribe':
+ $entityWorkflow->addSubscriberToFinal($user);
+
+ break;
+
+ case 'unsubscribe':
+ $entityWorkflow->removeSubscriberToFinal($user);
+
+ break;
+
+ default:
+ throw new LogicException();
+ }
+
+ break;
+
+ case 'step':
+ switch ($action) {
+ case 'subscribe':
+ $entityWorkflow->addSubscriberToStep($user);
+
+ break;
+
+ case 'unsubscribe':
+ $entityWorkflow->removeSubscriberToStep($user);
+
+ break;
+
+ default:
+ throw new LogicException();
+ }
+
+ break;
+
+ default:
+ throw new BadRequestHttpException('subscribe parameter must be equal to "step" or "final"');
+ }
+
+ $this->entityManager->flush();
+
+ return new JsonResponse(
+ [
+ 'step' => $entityWorkflow->isUserSubscribedToStep($user),
+ 'final' => $entityWorkflow->isUserSubscribedToFinal($user),
+ ],
+ JsonResponse::HTTP_OK,
+ [],
+ false
+ );
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php
new file mode 100644
index 000000000..33f1bd774
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php
@@ -0,0 +1,257 @@
+entityWorkflowManager = $entityWorkflowManager;
+ $this->entityWorkflowRepository = $entityWorkflowRepository;
+ $this->validator = $validator;
+ $this->paginatorFactory = $paginatorFactory;
+ $this->registry = $registry;
+ $this->entityManager = $entityManager;
+ $this->translator = $translator;
+ }
+
+ /**
+ * @Route("/{_locale}/main/workflow/create", name="chill_main_workflow_create")
+ */
+ public function create(Request $request): Response
+ {
+ if (!$request->query->has('entityClass')) {
+ throw new BadRequestHttpException('Missing entityClass parameter');
+ }
+
+ if (!$request->query->has('entityId')) {
+ throw new BadRequestHttpException('missing entityId parameter');
+ }
+
+ if (!$request->query->has('workflow')) {
+ throw new BadRequestHttpException('missing workflow parameter');
+ }
+
+ $entityWorkflow = new EntityWorkflow();
+ $entityWorkflow
+ ->setRelatedEntityClass($request->query->get('entityClass'))
+ ->setRelatedEntityId($request->query->getInt('entityId'))
+ ->setWorkflowName($request->query->get('workflow'));
+
+ $errors = $this->validator->validate($entityWorkflow, null, ['creation']);
+
+ if (count($errors) > 0) {
+ $msg = [];
+
+ foreach ($errors as $error) {
+ /** @var \Symfony\Component\Validator\ConstraintViolationInterface $error */
+ $msg[] = $error->getMessage();
+ }
+
+ return new Response(implode("\n", $msg), Response::HTTP_UNPROCESSABLE_ENTITY);
+ }
+
+ $this->denyAccessUnlessGranted(EntityWorkflowVoter::CREATE, $entityWorkflow);
+
+ $em = $this->getDoctrine()->getManager();
+ $em->persist($entityWorkflow);
+ $em->flush();
+
+ return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
+ }
+
+ /**
+ * @Route("/{_locale}/main/workflow/list/dest", name="chill_main_workflow_list_dest")
+ */
+ public function myWorkflowsDest(Request $request): Response
+ {
+ $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
+
+ $total = $this->entityWorkflowRepository->countByDest($this->getUser());
+ $paginator = $this->paginatorFactory->create($total);
+
+ $workflows = $this->entityWorkflowRepository->findByDest(
+ $this->getUser(),
+ ['createdAt' => 'DESC'],
+ $paginator->getItemsPerPage(),
+ $paginator->getCurrentPageFirstItemNumber()
+ );
+
+ return $this->render(
+ '@ChillMain/Workflow/list.html.twig',
+ [
+ 'workflows' => $this->buildHandler($workflows),
+ 'paginator' => $paginator,
+ 'step' => 'dest',
+ ]
+ );
+ }
+
+ /**
+ * @Route("/{_locale}/main/workflow/list/subscribed", name="chill_main_workflow_list_subscribed")
+ */
+ public function myWorkflowsSubscribed(Request $request): Response
+ {
+ $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
+
+ $total = $this->entityWorkflowRepository->countBySubscriber($this->getUser());
+ $paginator = $this->paginatorFactory->create($total);
+
+ $workflows = $this->entityWorkflowRepository->findBySubscriber(
+ $this->getUser(),
+ ['createdAt' => 'DESC'],
+ $paginator->getItemsPerPage(),
+ $paginator->getCurrentPageFirstItemNumber()
+ );
+
+ return $this->render(
+ '@ChillMain/Workflow/list.html.twig',
+ [
+ 'workflows' => $this->buildHandler($workflows),
+ 'paginator' => $paginator,
+ 'step' => 'subscribed',
+ ]
+ );
+ }
+
+ /**
+ * @Route("/{_locale}/main/workflow/{id}/show", name="chill_main_workflow_show")
+ */
+ public function show(EntityWorkflow $entityWorkflow, Request $request): Response
+ {
+ $this->denyAccessUnlessGranted(EntityWorkflowVoter::SEE, $entityWorkflow);
+
+ $handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
+ $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
+
+ if (count($workflow->getEnabledTransitions($entityWorkflow)) > 0) {
+ // possible transition
+ $transitionForm = $this->createForm(
+ WorkflowStepType::class,
+ $entityWorkflow->getCurrentStep(),
+ ['transition' => true, 'entity_workflow' => $entityWorkflow]
+ );
+
+ $transitionForm->handleRequest($request);
+
+ if ($transitionForm->isSubmitted() && $transitionForm->isValid()) {
+ if (!$workflow->can($entityWorkflow, $transition = $transitionForm['transition']->getData()->getName())) {
+ $blockers = $workflow->buildTransitionBlockerList($entityWorkflow, $transition);
+ $msgs = array_map(function (TransitionBlocker $tb) {
+ return $this->translator->trans(
+ $tb->getMessage(),
+ $tb->getParameters()
+ );
+ }, iterator_to_array($blockers));
+
+ throw $this->createAccessDeniedException(
+ sprintf(
+ "not allowed to apply transition {$transition}: %s",
+ implode(', ', $msgs)
+ )
+ );
+ }
+
+ $workflow->apply($entityWorkflow, $transition);
+
+ foreach ($transitionForm['future_dest_users']->getData() as $user) {
+ $entityWorkflow->getCurrentStep()->addDestUser($user);
+ }
+
+ $this->entityManager->flush();
+
+ return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
+ }
+
+ if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) {
+ $this->addFlash('error', $this->translator->trans('This form contains errors'));
+ }
+ }
+
+ /*
+ $commentForm = $this->createForm(EntityWorkflowCommentType::class, $newComment = new EntityWorkflowComment());
+ $commentForm->handleRequest($request);
+
+ if ($commentForm->isSubmitted() && $commentForm->isValid()) {
+ $this->entityManager->persist($newComment);
+ $this->entityManager->flush();
+
+ $this->addFlash('success', $this->translator->trans('workflow.Comment added'));
+
+ return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
+ } elseif ($commentForm->isSubmitted() && !$commentForm->isValid()) {
+ $this->addFlash('error', $this->translator->trans('This form contains errors'));
+ }
+ */
+
+ return $this->render(
+ '@ChillMain/Workflow/index.html.twig',
+ [
+ 'handler_template' => $handler->getTemplate($entityWorkflow),
+ 'handler_template_title' => $handler->getTemplateTitle($entityWorkflow),
+ 'handler_template_data' => $handler->getTemplateData($entityWorkflow),
+ 'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null,
+ 'entity_workflow' => $entityWorkflow,
+ //'comment_form' => $commentForm->createView(),
+ ]
+ );
+ }
+
+ private function buildHandler(array $workflows): array
+ {
+ $lines = [];
+
+ foreach ($workflows as $workflow) {
+ $handler = $this->entityWorkflowManager->getHandler($workflow);
+ $lines[] = [
+ 'handler' => $handler,
+ 'entity_workflow' => $workflow,
+ ];
+ }
+
+ return $lines;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
index 772f4d8ee..fa561b7e7 100644
--- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
+++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
@@ -168,6 +168,7 @@ class ChillMainExtension extends Extension implements
$loader->load('services/timeline.yaml');
$loader->load('services/search.yaml');
$loader->load('services/serializer.yaml');
+ $loader->load('services/mailer.yaml');
$this->configureCruds($container, $config['cruds'], $config['apis'], $loader);
}
@@ -391,6 +392,26 @@ class ChillMainExtension extends Extension implements
],
],
],
+ [
+ 'class' => \Chill\MainBundle\Entity\UserJob::class,
+ 'name' => 'user_job',
+ 'base_path' => '/api/1.0/main/user-job',
+ 'base_role' => 'ROLE_USER',
+ 'actions' => [
+ '_index' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true,
+ ],
+ ],
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true,
+ ],
+ ],
+ ],
+ ],
[
'controller' => \Chill\MainBundle\Controller\AddressReferenceAPIController::class,
'class' => \Chill\MainBundle\Entity\AddressReference::class,
diff --git a/src/Bundle/ChillMainBundle/Doctrine/Model/TrackCreationTrait.php b/src/Bundle/ChillMainBundle/Doctrine/Model/TrackCreationTrait.php
new file mode 100644
index 000000000..a991399c8
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Doctrine/Model/TrackCreationTrait.php
@@ -0,0 +1,56 @@
+createdAt;
+ }
+
+ public function getCreatedBy(): ?User
+ {
+ return $this->createdBy;
+ }
+
+ public function setCreatedAt(DateTimeInterface $datetime): self
+ {
+ $this->createdAt = $datetime instanceof DateTime ? DateTimeImmutable::createFromMutable($datetime) : $datetime;
+
+ return $this;
+ }
+
+ public function setCreatedBy(User $user): self
+ {
+ $this->createdBy = $user;
+
+ return $this;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Doctrine/Model/TrackUpdateTrait.php b/src/Bundle/ChillMainBundle/Doctrine/Model/TrackUpdateTrait.php
new file mode 100644
index 000000000..3c706459c
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Doctrine/Model/TrackUpdateTrait.php
@@ -0,0 +1,56 @@
+updatedAt;
+ }
+
+ public function getUpdatedBy(): ?User
+ {
+ return $this->updatedBy;
+ }
+
+ public function setUpdatedAt(DateTimeInterface $datetime): self
+ {
+ $this->updatedAt = $datetime instanceof DateTime ? DateTimeImmutable::createFromMutable($datetime) : $datetime;
+
+ return $this;
+ }
+
+ public function setUpdatedBy(User $user): self
+ {
+ $this->updatedBy = $user;
+
+ return $this;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Entity/Notification.php b/src/Bundle/ChillMainBundle/Entity/Notification.php
index 0624fff39..b2fe40c60 100644
--- a/src/Bundle/ChillMainBundle/Entity/Notification.php
+++ b/src/Bundle/ChillMainBundle/Entity/Notification.php
@@ -23,6 +23,9 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\Entity
* @ORM\Table(
* name="chill_main_notification",
+ * indexes={
+ * @ORM\Index(name="chill_main_notification_related_entity_idx", columns={"relatedentityclass", "relatedentityid"})
+ * }
* )
* @ORM\HasLifecycleCallbacks
*/
diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php
new file mode 100644
index 000000000..0d7142e9f
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php
@@ -0,0 +1,440 @@
+subscriberToFinal = new ArrayCollection();
+ $this->subscriberToStep = new ArrayCollection();
+ $this->comments = new ArrayCollection();
+ $this->steps = new ArrayCollection();
+
+ $initialStep = new EntityWorkflowStep();
+ $initialStep
+ ->setCurrentStep('initial');
+ $this->addStep($initialStep);
+ }
+
+ public function addComment(EntityWorkflowComment $comment): self
+ {
+ if (!$this->comments->contains($comment)) {
+ $this->comments[] = $comment;
+ $comment->setEntityWorkflow($this);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @internal You should prepare a step and run a workflow transition instead of manually adding a step
+ */
+ public function addStep(EntityWorkflowStep $step): self
+ {
+ if (!$this->steps->contains($step)) {
+ $this->steps[] = $step;
+ $step->setEntityWorkflow($this);
+
+ if ($this->isFinalize()) {
+ $step->setFinalizeAfter(true);
+ }
+ }
+
+ return $this;
+ }
+
+ public function addSubscriberToFinal(User $user): self
+ {
+ if (!$this->subscriberToFinal->contains($user)) {
+ $this->subscriberToFinal[] = $user;
+ }
+
+ return $this;
+ }
+
+ public function addSubscriberToStep(User $user): self
+ {
+ if (!$this->subscriberToStep->contains($user)) {
+ $this->subscriberToStep[] = $user;
+ }
+
+ return $this;
+ }
+
+ public function getComments(): Collection
+ {
+ return $this->comments;
+ }
+
+ public function getCurrentStep(): ?EntityWorkflowStep
+ {
+ $step = $this->steps->last();
+
+ if (false !== $step) {
+ return $step;
+ }
+
+ return null;
+ }
+
+ public function getCurrentStepCreatedAt(): ?DateTimeInterface
+ {
+ if (null !== $previous = $this->getPreviousStepIfAny()) {
+ return $previous->getTransitionAt();
+ }
+
+ return null;
+ }
+
+ public function getCurrentStepCreatedBy(): ?User
+ {
+ if (null !== $previous = $this->getPreviousStepIfAny()) {
+ return $previous->getTransitionBy();
+ }
+
+ return null;
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getRelatedEntityClass(): string
+ {
+ return $this->relatedEntityClass;
+ }
+
+ public function getRelatedEntityId(): int
+ {
+ return $this->relatedEntityId;
+ }
+
+ /**
+ * Method used by MarkingStore.
+ *
+ * get a string representation of the step
+ */
+ public function getStep(): string
+ {
+ return $this->getCurrentStep()->getCurrentStep();
+ }
+
+ public function getStepAfter(EntityWorkflowStep $step): ?EntityWorkflowStep
+ {
+ $iterator = $this->steps->getIterator();
+
+ if ($iterator instanceof Iterator) {
+ $iterator->rewind();
+
+ while ($iterator->valid()) {
+ $curStep = $iterator->current();
+
+ if ($curStep === $step) {
+ $iterator->next();
+
+ if ($iterator->valid()) {
+ return $iterator->current();
+ }
+
+ return null;
+ }
+ $iterator->next();
+ }
+
+ return null;
+ }
+
+ throw new RuntimeException();
+ }
+
+ /**
+ * @return ArrayCollection|Collection
+ */
+ public function getSteps()
+ {
+ return $this->steps;
+ }
+
+ public function getStepsChained(): array
+ {
+ $iterator = $this->steps->getIterator();
+ $previous = $next = $current = null;
+ $steps = [];
+
+ $iterator->rewind();
+
+ while ($iterator->valid()) {
+ $previous = $current;
+ $steps[] = $current = $iterator->current();
+ $current->setPrevious($previous);
+
+ $iterator->next();
+
+ if ($iterator->valid()) {
+ $next = $iterator->current();
+ } else {
+ $next = null;
+ }
+
+ $current->setNext($next);
+ }
+
+ return $steps;
+ }
+
+ /**
+ * @return ArrayCollection|Collection
+ */
+ public function getSubscriberToFinal()
+ {
+ return $this->subscriberToFinal;
+ }
+
+ /**
+ * @return ArrayCollection|Collection
+ */
+ public function getSubscriberToStep()
+ {
+ return $this->subscriberToStep;
+ }
+
+ /**
+ * get the step which is transitionning. Should be called only by event which will
+ * concern the transition.
+ */
+ public function getTransitionningStep(): ?EntityWorkflowStep
+ {
+ return $this->transitionningStep;
+ }
+
+ public function getWorkflowName(): string
+ {
+ return $this->workflowName;
+ }
+
+ public function isFinalize(): bool
+ {
+ $steps = $this->getStepsChained();
+
+ if (1 === count($steps)) {
+ // the initial step cannot be finalized
+ return false;
+ }
+
+ /** @var EntityWorkflowStep $last */
+ $last = end($steps);
+
+ return $last->getPrevious()->isFinalizeAfter();
+ }
+
+ public function isFreeze(): bool
+ {
+ $steps = $this->getStepsChained();
+
+ if (1 === count($steps)) {
+ // the initial step cannot be finalized
+ return false;
+ }
+
+ /** @var EntityWorkflowStep $last */
+ $last = end($steps);
+
+ return $last->getPrevious()->isFreezeAfter();
+ }
+
+ public function isUserSubscribedToFinal(User $user): bool
+ {
+ return $this->subscriberToFinal->contains($user);
+ }
+
+ public function isUserSubscribedToStep(User $user): bool
+ {
+ return $this->subscriberToStep->contains($user);
+ }
+
+ public function prepareStepBeforeTransition(EntityWorkflowStep $step): self
+ {
+ $this->transitionningStep = $step;
+
+ return $this;
+ }
+
+ public function removeComment(EntityWorkflowComment $comment): self
+ {
+ if ($this->comments->removeElement($comment)) {
+ $comment->setEntityWorkflow(null);
+ }
+
+ return $this;
+ }
+
+ public function removeStep(EntityWorkflowStep $step): self
+ {
+ if ($this->steps->removeElement($step)) {
+ $step->setEntityWorkflow(null);
+ }
+
+ return $this;
+ }
+
+ public function removeSubscriberToFinal(User $user): self
+ {
+ $this->subscriberToFinal->removeElement($user);
+
+ return $this;
+ }
+
+ public function removeSubscriberToStep(User $user): self
+ {
+ $this->subscriberToStep->removeElement($user);
+
+ return $this;
+ }
+
+ public function setRelatedEntityClass(string $relatedEntityClass): EntityWorkflow
+ {
+ $this->relatedEntityClass = $relatedEntityClass;
+
+ return $this;
+ }
+
+ public function setRelatedEntityId(int $relatedEntityId): EntityWorkflow
+ {
+ $this->relatedEntityId = $relatedEntityId;
+
+ return $this;
+ }
+
+ /**
+ * Method use by marking store.
+ *
+ * @return $this
+ */
+ public function setStep(string $step): self
+ {
+ $newStep = new EntityWorkflowStep();
+ $newStep->setCurrentStep($step);
+
+ // copy the freeze
+ if ($this->getCurrentStep()->isFreezeAfter()) {
+ $newStep->setFreezeAfter(true);
+ }
+
+ $this->addStep($newStep);
+
+ return $this;
+ }
+
+ public function setWorkflowName(string $workflowName): EntityWorkflow
+ {
+ $this->workflowName = $workflowName;
+
+ return $this;
+ }
+
+ private function getPreviousStepIfAny(): ?EntityWorkflowStep
+ {
+ if (1 === count($this->steps)) {
+ return null;
+ }
+
+ return $this->steps->get($this->steps->count() - 2);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowComment.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowComment.php
new file mode 100644
index 000000000..9f4e7f096
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowComment.php
@@ -0,0 +1,78 @@
+comment;
+ }
+
+ public function getEntityWorkflow(): ?EntityWorkflow
+ {
+ return $this->entityWorkflow;
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function setComment(string $comment): self
+ {
+ $this->comment = $comment;
+
+ return $this;
+ }
+
+ /**
+ * @internal use @see{EntityWorkflow::addComment}
+ */
+ public function setEntityWorkflow(?EntityWorkflow $entityWorkflow): self
+ {
+ $this->entityWorkflow = $entityWorkflow;
+
+ return $this;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php
new file mode 100644
index 000000000..6bd39ae0c
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php
@@ -0,0 +1,336 @@
+destUser = new ArrayCollection();
+ }
+
+ public function addDestEmail(string $email): self
+ {
+ if (!in_array($email, $this->destEmail, true)) {
+ $this->destEmail[] = $email;
+ }
+
+ return $this;
+ }
+
+ public function addDestUser(User $user): self
+ {
+ if (!$this->destUser->contains($user)) {
+ $this->destUser[] = $user;
+ }
+
+ return $this;
+ }
+
+ public function getComment(): string
+ {
+ return $this->comment;
+ }
+
+ public function getCurrentStep(): ?string
+ {
+ return $this->currentStep;
+ }
+
+ public function getDestEmail(): array
+ {
+ return $this->destEmail;
+ }
+
+ /**
+ * @return ArrayCollection|Collection
+ */
+ public function getDestUser()
+ {
+ return $this->destUser;
+ }
+
+ public function getEntityWorkflow(): ?EntityWorkflow
+ {
+ return $this->entityWorkflow;
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getNext(): ?EntityWorkflowStep
+ {
+ return $this->next;
+ }
+
+ public function getPrevious(): ?EntityWorkflowStep
+ {
+ return $this->previous;
+ }
+
+ public function getTransitionAfter(): ?string
+ {
+ return $this->transitionAfter;
+ }
+
+ public function getTransitionAt(): ?DateTimeImmutable
+ {
+ return $this->transitionAt;
+ }
+
+ public function getTransitionBy(): ?User
+ {
+ return $this->transitionBy;
+ }
+
+ public function getTransitionByEmail(): ?string
+ {
+ return $this->transitionByEmail;
+ }
+
+ public function isFinalizeAfter(): bool
+ {
+ return $this->finalizeAfter;
+ }
+
+ public function isFreezeAfter(): bool
+ {
+ return $this->freezeAfter;
+ }
+
+ public function removeDestEmail(string $email): self
+ {
+ $this->destEmail = array_filter($this->destEmail, static function (string $existing) use ($email) {
+ return $email !== $existing;
+ });
+
+ return $this;
+ }
+
+ public function removeDestUser(User $user): self
+ {
+ $this->destUser->removeElement($user);
+
+ return $this;
+ }
+
+ public function setComment(?string $comment): EntityWorkflowStep
+ {
+ $this->comment = (string) $comment;
+
+ return $this;
+ }
+
+ public function setCurrentStep(?string $currentStep): EntityWorkflowStep
+ {
+ $this->currentStep = $currentStep;
+
+ return $this;
+ }
+
+ public function setDestEmail(array $destEmail): EntityWorkflowStep
+ {
+ $this->destEmail = $destEmail;
+
+ return $this;
+ }
+
+ /**
+ * @internal use @see(EntityWorkflow::addStep} instead
+ */
+ public function setEntityWorkflow(?EntityWorkflow $entityWorkflow): EntityWorkflowStep
+ {
+ $this->entityWorkflow = $entityWorkflow;
+
+ return $this;
+ }
+
+ public function setFinalizeAfter(bool $finalizeAfter): EntityWorkflowStep
+ {
+ $this->finalizeAfter = $finalizeAfter;
+
+ return $this;
+ }
+
+ public function setFreezeAfter(bool $freezeAfter): EntityWorkflowStep
+ {
+ $this->freezeAfter = $freezeAfter;
+
+ return $this;
+ }
+
+ /**
+ * @return EntityWorkflowStep
+ *
+ * @internal
+ */
+ public function setNext(?EntityWorkflowStep $next): self
+ {
+ $this->next = $next;
+
+ return $this;
+ }
+
+ /**
+ * @return EntityWorkflowStep
+ *
+ * @internal
+ */
+ public function setPrevious(?EntityWorkflowStep $previous): self
+ {
+ $this->previous = $previous;
+
+ return $this;
+ }
+
+ public function setTransitionAfter(?string $transitionAfter): EntityWorkflowStep
+ {
+ $this->transitionAfter = $transitionAfter;
+
+ return $this;
+ }
+
+ public function setTransitionAt(?DateTimeImmutable $transitionAt): EntityWorkflowStep
+ {
+ $this->transitionAt = $transitionAt;
+
+ return $this;
+ }
+
+ public function setTransitionBy(?User $transitionBy): EntityWorkflowStep
+ {
+ $this->transitionBy = $transitionBy;
+
+ return $this;
+ }
+
+ public function setTransitionByEmail(?string $transitionByEmail): EntityWorkflowStep
+ {
+ $this->transitionByEmail = $transitionByEmail;
+
+ return $this;
+ }
+
+ /**
+ * @Assert\Callback
+ *
+ * @param mixed $payload
+ */
+ public function validateOnCreation(ExecutionContextInterface $context, $payload): void
+ {
+ return;
+
+ if ($this->isFinalizeAfter()) {
+ if (0 !== count($this->getDestUser())) {
+ $context->buildViolation('workflow.No dest users when the workflow is finalized')
+ ->atPath('finalizeAfter')
+ ->addViolation();
+ }
+ } else {
+ if (0 === count($this->getDestUser())) {
+ $context->buildViolation('workflow.The next step must count at least one dest')
+ ->atPath('finalizeAfter')
+ ->addViolation();
+ }
+ }
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Form/EntityWorkflowCommentType.php b/src/Bundle/ChillMainBundle/Form/EntityWorkflowCommentType.php
new file mode 100644
index 000000000..c32e09dfd
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Form/EntityWorkflowCommentType.php
@@ -0,0 +1,27 @@
+add('comment', ChillTextareaType::class, [
+ 'required' => false,
+ ]);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/UserToJsonTransformer.php b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/UserToJsonTransformer.php
index df670f891..ca87ea4f5 100644
--- a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/UserToJsonTransformer.php
+++ b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/UserToJsonTransformer.php
@@ -36,14 +36,20 @@ class UserToJsonTransformer implements DataTransformerInterface
public function reverseTransform($value)
{
+ $denormalized = json_decode($value, true);
+
if ($this->multiple) {
+ if (null === $denormalized) {
+ return [];
+ }
+
return array_map(
function ($item) { return $this->denormalizeOne($item); },
- json_decode($value, true)
+ $denormalized
);
}
- return $this->denormalizeOne(json_decode($value, true));
+ return $this->denormalizeOne($denormalized);
}
/**
diff --git a/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php b/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php
new file mode 100644
index 000000000..6600e330a
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php
@@ -0,0 +1,111 @@
+entityWorkflowManager = $entityWorkflowManager;
+ $this->registry = $registry;
+ }
+
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ /** @var \Chill\MainBundle\Entity\Workflow\EntityWorkflow $entityWorkflow */
+ $entityWorkflow = $options['entity_workflow'];
+ $handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
+
+ if (true === $options['transition']) {
+ if (null === $options['entity_workflow']) {
+ throw new LogicException('if transition is true, entity_workflow should be defined');
+ }
+
+ $transitions = $this->registry
+ ->get($options['entity_workflow'], $entityWorkflow->getWorkflowName())
+ ->getEnabledTransitions($entityWorkflow);
+
+ $choices = array_combine(
+ array_map(static function (Transition $transition) { return $transition->getName(); }, $transitions),
+ $transitions
+ );
+
+ $builder
+ ->add('transition', ChoiceType::class, [
+ 'label' => 'workflow.Transition',
+ 'mapped' => false,
+ 'multiple' => false,
+ 'expanded' => true,
+ 'choices' => $choices,
+ 'choice_label' => static function (Transition $transition) {
+ return implode(', ', $transition->getTos());
+ },
+ ])
+ ->add('future_dest_users', PickUserDynamicType::class, [
+ 'label' => 'workflow.dest for next steps',
+ 'multiple' => true,
+ 'mapped' => false,
+ ]);
+ }
+
+ if (
+ $handler->supportsFreeze($entityWorkflow)
+ && !$entityWorkflow->isFreeze()
+ ) {
+ $builder
+ ->add('freezeAfter', CheckboxType::class, [
+ 'required' => false,
+ 'label' => 'workflow.Freeze',
+ 'help' => 'workflow.The associated element will be freezed',
+ ]);
+ }
+
+ $builder
+ ->add('finalizeAfter', CheckboxType::class, [
+ 'required' => false,
+ 'label' => 'workflow.Finalize',
+ 'help' => 'workflow.The workflow will be finalized',
+ ])
+ ->add('comment', ChillTextareaType::class, [
+ 'required' => false,
+ 'label' => 'Comment',
+ ]);
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver
+ ->setDefined('class', EntityWorkflowStep::class)
+ ->setRequired('transition')
+ ->setAllowedTypes('transition', 'bool')
+ ->setRequired('entity_workflow')
+ ->setAllowedTypes('entity_workflow', EntityWorkflow::class);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Form/WorkflowTransitionType.php b/src/Bundle/ChillMainBundle/Form/WorkflowTransitionType.php
new file mode 100644
index 000000000..1692928dd
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Form/WorkflowTransitionType.php
@@ -0,0 +1,36 @@
+add('current_step', WorkflowStepType::class, [
+ 'transition' => true,
+ 'entity_workflow' => $options['entity_workflow'],
+ ]);
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver
+ ->setRequired('entity_workflow')
+ ->setAllowedTypes('entity_workflow', EntityWorkflow::class);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Notification/Email/NotificationMailer.php b/src/Bundle/ChillMainBundle/Notification/Email/NotificationMailer.php
index 658764c26..e04a96af0 100644
--- a/src/Bundle/ChillMainBundle/Notification/Email/NotificationMailer.php
+++ b/src/Bundle/ChillMainBundle/Notification/Email/NotificationMailer.php
@@ -50,7 +50,7 @@ class NotificationMailer
$email = new TemplatedEmail();
$email
->to($dest->getEmail())
- ->subject('Re: [Chill] ' . $comment->getNotification()->getTitle())
+ ->subject('Re: ' . $comment->getNotification()->getTitle())
->textTemplate('@ChillMain/Notification/email_notification_comment_persist.fr.md.twig')
->context([
'comment' => $comment,
@@ -79,11 +79,13 @@ class NotificationMailer
continue;
}
+ $email = new Email();
+ $email
+ ->subject($notification->getTitle());
+
if ($notification->isSystem()) {
- $email = new Email();
$email
- ->text($notification->getMessage())
- ->subject('[Chill] ' . $notification->getTitle());
+ ->text($notification->getMessage());
} else {
$email = new TemplatedEmail();
$email
diff --git a/src/Bundle/ChillMainBundle/Notification/NotificationPresence.php b/src/Bundle/ChillMainBundle/Notification/NotificationPresence.php
index 91bea3197..ed3a8cf66 100644
--- a/src/Bundle/ChillMainBundle/Notification/NotificationPresence.php
+++ b/src/Bundle/ChillMainBundle/Notification/NotificationPresence.php
@@ -15,12 +15,15 @@ use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\NotificationRepository;
use Symfony\Component\Security\Core\Security;
+use function array_key_exists;
/**
* Helps to find if a notification exist for a given entity.
*/
class NotificationPresence
{
+ private array $cache = [];
+
private NotificationRepository $notificationRepository;
private Security $security;
@@ -31,6 +34,29 @@ class NotificationPresence
$this->notificationRepository = $notificationRepository;
}
+ public function countNotificationsForClassAndEntity(string $relatedEntityClass, int $relatedEntityId): array
+ {
+ if (array_key_exists($relatedEntityClass, $this->cache) && array_key_exists($relatedEntityId, $this->cache[$relatedEntityClass])) {
+ return $this->cache[$relatedEntityClass][$relatedEntityId];
+ }
+
+ $user = $this->security->getUser();
+
+ if ($user instanceof User) {
+ $counter = $this->notificationRepository->countNotificationByRelatedEntityAndUserAssociated(
+ $relatedEntityClass,
+ $relatedEntityId,
+ $user
+ );
+
+ $this->cache[$relatedEntityClass][$relatedEntityId] = $counter;
+
+ return $counter;
+ }
+
+ return ['unread' => 0, 'read' => 0];
+ }
+
/**
* @return array|Notification[]
*/
diff --git a/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtension.php b/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtension.php
index 115adf06b..eb017d912 100644
--- a/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtension.php
+++ b/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtension.php
@@ -23,6 +23,13 @@ class NotificationTwigExtension extends AbstractExtension
'needs_environment' => true,
'is_safe' => ['html'],
]),
+ new TwigFunction('chill_count_notifications', [NotificationTwigExtensionRuntime::class, 'countNotificationsFor'], [
+ 'is_safe' => [],
+ ]),
+ new TwigFunction('chill_counter_notifications', [NotificationTwigExtensionRuntime::class, 'counterNotificationFor'], [
+ 'needs_environment' => true,
+ 'is_safe' => ['html'],
+ ]),
];
}
}
diff --git a/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php b/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php
index e35a39ac0..d5ec75699 100644
--- a/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php
+++ b/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php
@@ -24,6 +24,21 @@ class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface
$this->notificationPresence = $notificationPresence;
}
+ public function counterNotificationFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string
+ {
+ return $environment->render(
+ '@ChillMain/Notification/extension_counter_notifications_for.html.twig',
+ [
+ 'counter' => $this->notificationPresence->countNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId),
+ ]
+ );
+ }
+
+ public function countNotificationsFor(string $relatedEntityClass, int $relatedEntityId, array $options = []): array
+ {
+ return $this->notificationPresence->countNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId);
+ }
+
public function listNotificationsFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string
{
$notifications = $this->notificationPresence->getNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId);
diff --git a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
index 1580cfa6a..1479474fc 100644
--- a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
+++ b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
@@ -13,6 +13,7 @@ namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\User;
+use Doctrine\DBAL\Statement;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
@@ -24,6 +25,8 @@ final class NotificationRepository implements ObjectRepository
{
private EntityManagerInterface $em;
+ private ?Statement $notificationByRelatedEntityAndUserAssociatedStatement = null;
+
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
@@ -48,6 +51,30 @@ final class NotificationRepository implements ObjectRepository
->getSingleScalarResult();
}
+ public function countNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user): array
+ {
+ if (null === $this->notificationByRelatedEntityAndUserAssociatedStatement) {
+ $sql =
+ 'SELECT
+ SUM((EXISTS (SELECT 1 AS c FROM chill_main_notification_addresses_unread cmnau WHERE user_id = 1812 and cmnau.notification_id = cmn.id))::int) AS unread,
+ SUM((cmn.sender_id = 1812)::int) AS sent,
+ COUNT(cmn.*) AS total
+ FROM chill_main_notification cmn
+ WHERE relatedentityclass = :relatedEntityClass AND relatedentityid = :relatedEntityId AND sender_id IS NOT NULL';
+ $this->notificationByRelatedEntityAndUserAssociatedStatement =
+ $this->em->getConnection()->prepare($sql);
+ }
+
+ $results = $this->notificationByRelatedEntityAndUserAssociatedStatement
+ ->executeQuery(['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId]);
+
+ $result = $results->fetchAssociative();
+
+ $results->free();
+
+ return $result;
+ }
+
public function countUnreadByUser(User $user): int
{
$sql = 'SELECT count(*) AS c FROM chill_main_notification_addresses_unread WHERE user_id = :userId';
@@ -153,11 +180,29 @@ final class NotificationRepository implements ObjectRepository
* @return array|Notification[]
*/
public function findNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user): array
+ {
+ return
+ $this->buildQueryNotificationByRelatedEntityAndUserAssociated($relatedEntityClass, $relatedEntityId, $user)
+ ->select('n')
+ ->getQuery()
+ ->getResult();
+ }
+
+ public function findOneBy(array $criteria, ?array $orderBy = null): ?Notification
+ {
+ return $this->repository->findOneBy($criteria, $orderBy);
+ }
+
+ public function getClassName()
+ {
+ return Notification::class;
+ }
+
+ private function buildQueryNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('n');
$qb
- ->select('n')
->where($qb->expr()->eq('n.relatedEntityClass', ':relatedEntityClass'))
->andWhere($qb->expr()->eq('n.relatedEntityId', ':relatedEntityId'))
->andWhere($qb->expr()->isNotNull('n.sender'))
@@ -174,11 +219,6 @@ final class NotificationRepository implements ObjectRepository
return $qb->getQuery()->getResult();
}
- public function findOneBy(array $criteria, ?array $orderBy = null): ?Notification
- {
- return $this->repository->findOneBy($criteria, $orderBy);
- }
-
/**
* @return array|Notification[]
*/
@@ -202,11 +242,6 @@ final class NotificationRepository implements ObjectRepository
return $nq->getResult();
}
- public function getClassName()
- {
- return Notification::class;
- }
-
private function queryByAddressee(User $addressee, bool $countQuery = false): QueryBuilder
{
$qb = $this->repository->createQueryBuilder('n');
diff --git a/src/Bundle/ChillMainBundle/Repository/Workflow/EntityWorkflowRepository.php b/src/Bundle/ChillMainBundle/Repository/Workflow/EntityWorkflowRepository.php
new file mode 100644
index 000000000..cac5c9996
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Repository/Workflow/EntityWorkflowRepository.php
@@ -0,0 +1,137 @@
+repository = $entityManager->getRepository(EntityWorkflow::class);
+ }
+
+ public function countByDest(User $user): int
+ {
+ $qb = $this->buildQueryByDest($user)->select('count(ew)');
+
+ return (int) $qb->getQuery()->getSingleScalarResult();
+ }
+
+ public function countBySubscriber(User $user): int
+ {
+ $qb = $this->buildQueryBySubscriber($user)->select('count(ew)');
+
+ return (int) $qb->getQuery()->getSingleScalarResult();
+ }
+
+ public function find($id): ?EntityWorkflow
+ {
+ return $this->repository->find($id);
+ }
+
+ /**
+ * @return array|EntityWorkflow[]
+ */
+ public function findAll(): array
+ {
+ return $this->repository->findAll();
+ }
+
+ /**
+ * @param null|mixed $limit
+ * @param null|mixed $offset
+ *
+ * @return array|EntityWorkflow[]
+ */
+ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
+ {
+ return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
+ }
+
+ public function findByDest(User $user, ?array $orderBy = null, $limit = null, $offset = null): array
+ {
+ $qb = $this->buildQueryByDest($user)->select('ew');
+
+ 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
+ {
+ $qb = $this->buildQueryBySubscriber($user)->select('ew');
+
+ foreach ($orderBy as $key => $sort) {
+ $qb->addOrderBy('ew.' . $key, $sort);
+ }
+
+ $qb->setMaxResults($limit)->setFirstResult($offset);
+
+ return $qb->getQuery()->getResult();
+ }
+
+ public function findOneBy(array $criteria): ?EntityWorkflow
+ {
+ return $this->repository->findOneBy($criteria);
+ }
+
+ public function getClassName(): string
+ {
+ return EntityWorkflow::class;
+ }
+
+ private function buildQueryByDest(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()->isNull('step.transitionAfter')
+ )
+ );
+
+ $qb->setParameter('user', $user);
+
+ return $qb;
+ }
+
+ private function buildQueryBySubscriber(User $user): QueryBuilder
+ {
+ $qb = $this->repository->createQueryBuilder('ew');
+
+ $qb->where(
+ $qb->expr()->orX(
+ $qb->expr()->isMemberOf(':user', 'ew.subscriberToStep'),
+ $qb->expr()->isMemberOf(':user', 'ew.subscriberToFinal'),
+ )
+ );
+
+ $qb->setParameter('user', $user);
+
+ return $qb;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss
index 4829f9634..39f8b3f8a 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss
+++ b/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss
@@ -1,5 +1,5 @@
// Access to Bootstrap variables and mixins
-@import '~ChillMainAssets/module/bootstrap/shared';
+@import 'ChillMainAssets/module/bootstrap/shared';
// Chill variables
@import './scss/chill_variables';
@@ -277,11 +277,17 @@ table.table-bordered {
}
}
+/// meta-data
+div.updatedBy,
+div.metadata {
+ span.user, span.date {
+ text-decoration: underline dotted;
+ }
+}
div.metadata {
font-size: smaller;
color: $gray-600;
span.user, span.date {
- text-decoration: underline dotted;
&:hover {
color: $gray-700;
}
@@ -424,7 +430,63 @@ span.item-key {
//text-decoration: dotted underline;
}
+/// Workflows
+div.workflow {
+ section.step {
+ border: 1px solid $chill-l-gray;
+ padding: 1em 2em;
+ div.flex-table {
+ margin: 1.5em -2em;
+ }
+ }
+ div.to-decision,
+ div.decided {
+ font-variant: all-small-caps;
+ margin-left: 1em;
+ }
+ div.to-decision {
+ font-weight: 300;
+ }
+ div.decided {
+ font-weight: 600;
+ }
+ div.breadcrumb {
+ display: initial;
+ margin-bottom: 0;
+ padding-right: 0.5em;
+ background-color: tint-color($chill-yellow, 90%);
+ border: 1px solid $chill-yellow;
+ color: $primary;
+ border-radius: 1.5em;
+ font-size: 12pt;
+ font-weight: 500;
+ font-variant: small-caps;
+ span, a {
+ cursor: pointer;
+ text-decoration: none;
+ &:hover {
+ font-weight: 700;
+ }
+ }
+ }
+}
+
+// Override bootstrap popover styles
+div.popover {
+ box-shadow: 0 0 10px -5px $dark;
+ .popover-arrow {}
+ .popover-header {}
+ .popover-body {}
+
+ // Specific worflow breadcrumb popover
+ &.workflow-transition {
+ .popover-header {
+ font-variant: small-caps;
+ }
+ }
+}
+
// increase toast message z-index (above all modals)
div.v-toast {
z-index: 10000!important;
-}
\ No newline at end of file
+}
diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss
index ad0584d74..1eb437a2a 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss
+++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss
@@ -18,6 +18,7 @@ $chill-theme-buttons: (
"show": $chill-blue,
"view": $chill-blue,
"misc": $gray-300,
+ "download": $gray-300,
"cancel": $gray-300,
"choose": $gray-300,
"notify": $gray-300,
@@ -78,6 +79,7 @@ $chill-theme-buttons: (
&.btn-choose::before,
&.btn-notify::before,
&.btn-tpchild::before,
+ &.btn-download::before,
&.btn-cancel::before {
font: normal normal normal 14px/1 ForkAwesome;
margin-right: 0.5em;
@@ -105,6 +107,7 @@ $chill-theme-buttons: (
&.btn-unlink::before { content: "\f127"; } // fa-chain-broken
&.btn-notify::before { content: "\f1d8"; } // fa-paper-plane
&.btn-tpchild::before { content: "\f007"; } // fa-user
+ &.btn-download::before { content: "\f019"; } // fa-download
}
diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss
index 20fe927a6..67160863d 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss
+++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss
@@ -41,6 +41,15 @@ div.flex-table {
margin-right: 5px;
}
}
+
+ div.item-meta {
+ flex-grow: 1 !important;
+ flex-shrink: 1 !important;
+ width: unset !important;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
}
/*
diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/notification.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/notification.scss
index 93e9bd152..15f6deb3a 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/notification.scss
+++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/notification.scss
@@ -68,6 +68,7 @@ div.notification-show {
}
// Override bootstrap accordion
+div#workflow-fold,
div#notification-fold {
.accordion-button {
padding: 0;
@@ -78,3 +79,14 @@ div#notification-fold {
}
}
}
+
+// Counter
+div.notification-counter {
+ span {
+ &:not(:first-child) {
+ &::before {
+ content: '/ ';
+ }
+ }
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss
index 6100bc842..37d4f97c4 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss
+++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss
@@ -106,6 +106,8 @@ section.chill-entity {
// used for comment-embeddable
&.entity-comment-embeddable {
width: 100%;
+
+ /* already defined !!
div.metadata {
font-size: smaller;
color: $gray-600;
@@ -116,5 +118,6 @@ section.chill-entity {
}
}
}
+ */
}
}
diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/entity-workflow/api.js b/src/Bundle/ChillMainBundle/Resources/public/lib/entity-workflow/api.js
new file mode 100644
index 000000000..a89dd66f5
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/lib/entity-workflow/api.js
@@ -0,0 +1,12 @@
+const buildLinkCreate = function(workflowName, relatedEntityClass, relatedEntityId) {
+ let params = new URLSearchParams();
+ params.set('entityClass', relatedEntityClass);
+ params.set('entityId', relatedEntityId);
+ params.set('workflow', workflowName);
+
+ return `/fr/main/workflow/create?`+params.toString();
+};
+
+export {
+ buildLinkCreate,
+};
diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js b/src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js
index 936587dec..a9d34e01d 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js
+++ b/src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js
@@ -9,9 +9,10 @@ import Dropdown from 'bootstrap/js/src/dropdown';
import Modal from 'bootstrap/js/dist/modal';
import Collapse from 'bootstrap/js/src/collapse';
import Carousel from 'bootstrap/js/src/carousel';
+import Popover from 'bootstrap/js/src/popover';
//
-// ACHeaderSlider is a small slider used in banner of AccompanyingCourse Section
+// Carousel: ACHeaderSlider is a small slider used in banner of AccompanyingCourse Section
// Initialize options, and show/hide controls in first/last slides
//
let ACHeaderSlider = document.querySelector('#ACHeaderSlider');
@@ -48,3 +49,14 @@ if (ACHeaderSlider) {
}
})
}
+
+//
+// Popover: used in workflow breadcrumb,
+// (expected in: contextual help, notification-box, workflow-box )
+//
+const triggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
+const popoverList = triggerList.map(function (el) {
+ return new Popover(el, {
+ html: true,
+ });
+});
\ No newline at end of file
diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-pick/index.js b/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-pick/index.js
new file mode 100644
index 000000000..d6d657719
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-pick/index.js
@@ -0,0 +1,49 @@
+import { createApp } from "vue";
+import PickWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
+import ListWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue';
+
+// pick workflow
+document.querySelectorAll('[data-pick-workflow]')
+ .forEach(function(el) {
+ const app = {
+ components: {
+ PickWorkflowVue
+ },
+ template:
+ ' ',
+ data() {
+ return {
+ relatedEntityClass: el.dataset.relatedEntityClass,
+ relatedEntityId: Number.parseInt(el.dataset.relatedEntityId),
+ workflowsAvailables: JSON.parse(el.dataset.workflowsAvailables),
+ }
+ }
+ };
+ createApp(app).mount(el);
+ })
+;
+
+// list workflow
+document.querySelectorAll('[data-list-workflows]')
+ .forEach(function (el) {
+ const app = {
+ components: {
+ ListWorkflowVue,
+ },
+ template:
+ ' ',
+ data() {
+ return {
+ workflows: JSON.parse(el.dataset.workflows),
+ }
+ }
+ };
+ createApp(app).mount(el);
+ })
+;
\ No newline at end of file
diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-subscribe/index.js b/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-subscribe/index.js
new file mode 100644
index 000000000..c0e482aab
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-subscribe/index.js
@@ -0,0 +1,32 @@
+import {createApp} from "vue";
+import EntityWorkflowVueSubscriber from 'ChillMainAssets/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue';
+import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
+import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
+
+const i18n = _createI18n(appMessages);
+
+let containers = document.querySelectorAll('[data-entity-workflow-subscribe]');
+
+containers.forEach(container => {
+ let app = {
+ components: {
+ EntityWorkflowVueSubscriber,
+ },
+ template: ' ',
+ data() {
+ return {
+ entityWorkflowId: Number.parseInt(container.dataset.entityWorkflowId),
+ subscriberStep: container.dataset.subscribeStep === "1",
+ subscriberFinal: container.dataset.subscribeFinal === "1",
+ }
+ },
+ methods: {
+ onUpdate(status) {
+ this.subscriberStep = status.step;
+ this.subscriberFinal = status.final;
+ }
+ }
+ }
+
+ createApp(app).use(i18n).mount(container);
+})
diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js b/src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js
index 83a890cd9..329ac4e6c 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js
+++ b/src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js
@@ -31,7 +31,7 @@ window.addEventListener('DOMContentLoaded', function(e) {
return {
multiple: isMultiple,
types: JSON.parse(el.dataset.types),
- picked,
+ picked: picked === null ? [] : picked,
uniqid: el.dataset.uniqid,
}
},
diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/wopi-link/index.js b/src/Bundle/ChillMainBundle/Resources/public/module/wopi-link/index.js
new file mode 100644
index 000000000..f39b6c83b
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/module/wopi-link/index.js
@@ -0,0 +1,29 @@
+import { createApp } from 'vue';
+import OpenWopiLink from 'ChillMainAssets/vuejs/_components/OpenWopiLink';
+import {_createI18n} from "ChillMainAssets/vuejs/_js/i18n";
+
+const i18n = _createI18n({});
+
+window.addEventListener('DOMContentLoaded', function (e) {
+ document.querySelectorAll('span[data-module="wopi-link"]')
+ .forEach(function (el) {
+ createApp({
+ template: ' ',
+ components: {
+ OpenWopiLink
+ },
+ data() {
+ return {
+ wopiUrl: el.dataset.wopiUrl,
+ title: el.dataset.docTitle,
+ type: el.dataset.docType,
+ button: el.dataset.button ? JSON.parse(el.dataset.button) : {}
+ }
+ }
+ })
+ .use(i18n)
+ .mount(el)
+ ;
+ })
+ ;
+});
\ No newline at end of file
diff --git a/src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js b/src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js
new file mode 100644
index 000000000..2e2d4e89c
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js
@@ -0,0 +1,30 @@
+import {ShowHide} from 'ChillMainAssets/lib/show_hide/show_hide.js';
+
+window.addEventListener('DOMContentLoaded', function() {
+ let
+ finalizeAfterContainer = document.querySelector('#finalizeAfter'),
+ futureDestUsersContainer = document.querySelector('#futureDestUsers')
+ ;
+
+ if (null === finalizeAfterContainer) {
+ return;
+ }
+
+ new ShowHide({
+ load_event: null,
+ froms: [finalizeAfterContainer],
+ container: [futureDestUsersContainer],
+ test: function(containers, arg2, arg3) {
+ for (let container of containers) {
+ for (let input of container.querySelectorAll('input')) {
+ if (!input.checked) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ },
+ })
+});
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue
new file mode 100644
index 000000000..3e3d5405e
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue
@@ -0,0 +1,101 @@
+
+
+
+
+ {{ $t('subscribe_final') }}
+
+
+
+ {{ $t('unsubscribe_final') }}
+
+
+
+ {{ $t('subscribe_all_steps') }}
+
+
+
+ {{ $t('unsubscribe_all_steps') }}
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue
new file mode 100644
index 000000000..4a3346972
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue
@@ -0,0 +1,36 @@
+
+
+
+
Workflow associés
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue
new file mode 100644
index 000000000..3a39c0eff
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue
@@ -0,0 +1,50 @@
+
+
+
+
+ Créer un workflow
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue
index b806c4b3e..1a577274b 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue
@@ -15,7 +15,7 @@
-
- {% if c.full_content is defined and c.full_content == 'true' %}
+ {% if c.full_content is defined and c.full_content == true %}
{{ c.notification.message|chill_markdown_to_html }}
{% else %}
{{ c.notification.message|u.truncate(250, '…', false)|chill_markdown_to_html }}
+
{{ 'Read more'|trans }}
{% endif %}
- {% if c.action_button is not defined or c.action_button != 'false' %}
+ {% if c.action_button is not defined or c.action_button != false %}
@@ -85,7 +86,13 @@
{% if is_granted('CHILL_MAIN_NOTIFICATION_SEE', c.notification) %}
+ class="btn {% if not c.notification.isSystem %}btn-show change-icon{% else %}btn-misc{% endif %}" title="{{ 'notification.see_comments_thread'|trans }}">
+ {% if not c.notification.isSystem() %}
+
+ {% else %}
+ {{ 'Read more'|trans }}
+ {% endif %}
+
{% endif %}
@@ -95,7 +102,7 @@
- {% if fold_item is defined and fold_item != 'false' %}
+ {% if fold_item is defined and fold_item != false %}
+ {% include handler.template(notification) with handler.templateData(notification) %}
+
{{ form_end(form) }}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig
index 690f2187d..ed4a05c34 100644
--- a/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig
+++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig
@@ -40,8 +40,8 @@
'template': handler.getTemplate(notification),
'template_data': handler.getTemplateData(notification)
},
- 'action_button': 'false',
- 'full_content': 'true'
+ 'action_button': false,
+ 'full_content': true
} %}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_attachment.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_attachment.html.twig
new file mode 100644
index 000000000..b825e4d43
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_attachment.html.twig
@@ -0,0 +1,120 @@
+{# TODO Adapt condition #}
+{% if random(1) == 0 %}
+
+ {# For a document #}
+ {{ 'Document'|trans ~ 'target'|trans }}
+
+
+
+
+
+
+
Imprimé unique, parcours n°14635
+
Document PDF (6.2 Mo)
+
+ Description du document. Sed euismod nisi porta lorem mollis aliquam. Non curabitur gravida arcu ac tortor.
+
+
+
+
+{% else %}
+
+ {# For an action #}
+ {{ 'Accompanying Course Action'|trans ~ 'target'|trans }}
+
+
+ {# dynamic insertion
+ ::: TODO delete all static insertion, remove condition and pass work object in inclusion
+ #}{% if dynamic is defined %}
+
+ {% set work = '
' %}
+ {% include '@ChillPerson/AccompanyingCourseWork/_item.html.twig' with { 'w': work } %}
+
+ {% else %}
+
+ {# BEGIN static insertion #}
+
+
+
+
+ Exercer un AEB > Conclure l'AEB
+
+ Date de début : 25/11/2021
+ Date de fin : 10/03/2022
+
+
+
+
+
+
+
+
+
Usagers du parcours
+
+
+
+
Problématique sociale
+
+
+ AD - PREVENTION, ACCES AUX DROITS, BUDGET > SOUTIEN EQUILIBRE BUDGET
+
+
+
+
+
+
+
+
+ Objectif - motif - dispositif
+ Résultats - orientations
+
+
+
+
+ Aucun objectif - motif - dispositif
+
+
+
+ Résultat : Arrêt à l'initiative du ménage pour déménagement
+ Orientation vers une MASP
+
+
+
+
+
+
+
+
+ Dernière mise à jour par
+ Fred(Responsable tous les territoires) (ASE) ,
+ le 3 décembre 2021 à 15:19
+
+
+
+ {# END static insertion #}
+
+ {% endif %}
+
+
+{% endif %}
+
+
+
+
+ {{ 'Download'|trans }}
+
+
+
+ {% set x = random(1) %}
+
+
+ {{ 'Edit'|trans }}
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_comment.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_comment.html.twig
new file mode 100644
index 000000000..ec7cf8875
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_comment.html.twig
@@ -0,0 +1,13 @@
+{{ 'Join a comment'|trans }}
+
+{{ form_start(comment_form) }}
+
+{{ form_widget(comment_form.comment) }}
+
+
+
+ {{ 'Save'|trans }}
+
+
+
+{{ form_end(comment_form) }}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig
new file mode 100644
index 000000000..5751449c3
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig
@@ -0,0 +1,48 @@
+{{ 'Decision'|trans }}
+
+{% if transition_form is not null %}
+ {{ form_start(transition_form) }}
+
+ {{ form_row(transition_form.transition) }}
+
+
+ {{ form_row(transition_form.finalizeAfter) }}
+
+
+ {% if transition_form.freezeAfter is defined %}
+ {{ form_row(transition_form.freezeAfter) }}
+ {% endif %}
+
+
+ {{ form_row(transition_form.future_dest_users) }}
+
+
+ {{ form_label(transition_form.comment) }}
+
+ {{ form_widget(transition_form.comment) }}
+
+
+
+ {{ 'Save'|trans }}
+
+
+
+ {{ form_end(transition_form) }}
+{% else %}
+
+
+ {% if entity_workflow.currentStep.isFinalizeAfter %}
+
{{ 'workflow.This workflow is finalized'|trans }}
+ {% else %}
+
{{ 'workflow.You are not allowed to apply a transition on this workflow'|trans }}
+
{{ 'workflow.Only those users are allowed'|trans }}:
+
+
+ {% for u in entity_workflow.currentStep.destUser -%}
+ {{ u|chill_entity_render_box }}
+ {%- endfor %}
+
+ {% endif %}
+
+
+{% endif %}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_extension_list_workflow_for.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_extension_list_workflow_for.html.twig
new file mode 100644
index 000000000..67037465b
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_extension_list_workflow_for.html.twig
@@ -0,0 +1,13 @@
+{% if is_granted('CHILL_MAIN_WORKFLOW_CREATE', blank_workflow) %}
+ {# vue component #}
+
+{% endif %}
+
+{% if entity_workflows|length > 0 %}
+ {# vue component #}
+
+{% endif %}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_follow.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_follow.html.twig
new file mode 100644
index 000000000..7a14e83fe
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_follow.html.twig
@@ -0,0 +1,8 @@
+{{ 'Follow workflow'|trans }}
+
+{# vue component #}
+
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_history.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_history.html.twig
new file mode 100644
index 000000000..faa94e3a7
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_history.html.twig
@@ -0,0 +1,59 @@
+{{ 'Workflow history'|trans }}
+
+
+ {% for step in entity_workflow.stepsChained %}
+
+
+ {% if loop.first and step.next is null %}
+
+ {{ 'workflow.No transitions'|trans }}
+
+ {% endif %}
+
+
+
+ {% if not loop.first %}
+
+ {% endif %}
+ {{ step.currentStep }}
+
+ {#
+
+
+ Refusé
+
+ #}
+
+
+ {% if step.next is not null %}
+
+
+ {% if step.transitionBy is not null %}
+
+ {{ step.transitionBy|chill_entity_render_box }}
+
+ {% endif %}
+
+ {{ step.transitionAt|format_datetime('long', 'medium') }}
+
+
+
+
+
+ {{ step.next.currentStep }}
+
+
+
+ {% endif %}
+ {% if step.comment is not empty %}
+
+
+ {{ step.comment|chill_markdown_to_html }}
+
+
+ {% endif %}
+
+
+ {% endfor %}
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_notification_include.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_notification_include.html.twig
new file mode 100644
index 000000000..c20f51193
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_notification_include.html.twig
@@ -0,0 +1,12 @@
+
+
+ {{ 'workflow_'|trans }}
+
+ {% include handler.templateTitle(l.entity_workflow) with handler.templateTitleData(entity_workflow)|merge({
+ 'description': true,
+ 'breadcrumb': true,
+ 'add_classes': 'ms-3 h3'
+ }) %}
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/index.html.twig
new file mode 100644
index 000000000..588e2de0b
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/index.html.twig
@@ -0,0 +1,56 @@
+{% extends '@ChillMain/layout.html.twig' %}
+
+{% block title %}
+ {{ 'Workflow'|trans }}
+{% endblock %}
+
+{% block js %}
+ {{ parent() }}
+
+ {{ encore_entry_script_tags('mod_async_upload') }}
+ {{ encore_entry_script_tags('mod_pickentity_type') }}
+ {{ encore_entry_script_tags('mod_entity_workflow_subscribe') }}
+ {{ encore_entry_script_tags('page_workflow_show') }}
+ {{ encore_entry_script_tags('mod_wopi_link') }}
+{% endblock %}
+
+{% block css %}
+ {{ parent() }}
+ {{ encore_entry_link_tags('mod_pickentity_type') }}
+ {{ encore_entry_link_tags('mod_entity_workflow_subscribe') }}
+ {{ encore_entry_link_tags('page_workflow_show') }}
+ {{ encore_entry_link_tags('mod_wopi_link') }}
+{% endblock %}
+
+{% block content %}
+
+
{{ block('title') }}
+
+ {# handler_template:
+ - src/Bundle/ChillPersonBundle/Resources/views/Workflow/_evaluation.html.twig
+ - src/Bundle/ChillPersonBundle/Resources/views/Workflow/_accompanying_period_work.html.twig
+ - src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.html.twig
+ #}
+
+
+ {% include handler_template_title with handler_template_data|merge({'breadcrumb': true }) %}
+
+ {% include handler_template with handler_template_data|merge({'display_action': true }) %}
+
+
+
{% include '@ChillMain/Workflow/_follow.html.twig' %}
+
{% include '@ChillMain/Workflow/_decision.html.twig' %} {#
+
{% include '@ChillMain/Workflow/_comment.html.twig' %} #}
+
{% include '@ChillMain/Workflow/_history.html.twig' %}
+
+ {# useful ?
+
+ #}
+
+{% endblock %}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/list.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/list.html.twig
new file mode 100644
index 000000000..6da68e390
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/list.html.twig
@@ -0,0 +1,97 @@
+{% extends 'ChillMainBundle::layout.html.twig' %}
+
+{% import '@ChillMain/Workflow/macro_breadcrumb.html.twig' as macro %}
+
+{% block title %}
+ {{ 'workflow.My workflows'|trans }}
+{% endblock %}
+
+{% block content %}
+
+
+
{{ block('title') }}
+
+
+
+ {% if workflows|length == 0 %}
+
{{ 'workflow.No workflow'|trans }}
+ {% else %}
+
+ {% for l in workflows %}
+
+
+
+
+
+ {% include l.handler.template(l.entity_workflow) with l.handler.templateData(l.entity_workflow)|merge({
+ 'display_action': false
+ }) %}
+
+
+
+
+ {% if l.entity_workflow.isUserSubscribedToStep(app.user) %}
+
+ {{ 'workflow.you subscribed to all steps'|trans }}
+ {% endif %}
+
+
+ {% if l.entity_workflow.isUserSubscribedToFinal(app.user) %}
+
+ {{ 'workflow.you subscribed to final step'|trans }}
+ {% endif %}
+
+
+
+
+
+
+
+ {% endfor %}
+
+ {% endif %}
+
+{% endblock %}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/macro_breadcrumb.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/macro_breadcrumb.html.twig
new file mode 100644
index 000000000..436394012
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/macro_breadcrumb.html.twig
@@ -0,0 +1,45 @@
+{% macro popoverContent(step) %}
+
+
+ {{ 'By'|trans ~ ' : ' }}
+ {{ step.transitionBy|chill_entity_render_box }}
+
+
+ {{ 'Le'|trans ~ ' : ' }}
+ {{ step.transitionAt|format_datetime('short', 'short') }}
+
+
+{% endmacro %}
+
+{% macro breadcrumb(_ctx) %}
+
+ {% for step in _ctx.entity_workflow.stepsChained %}
+ {% if step.previous is null %}
+ {#
+ {% set popContent = "Point de départ du workflow" %}
+ {{ dump(step) }}
+ #}
+ {% set popContent = _self.popoverContent(step) %}
+ {% else %}
+ {% set popContent = _self.popoverContent(step.previous) %}
+ {% endif %}
+
+ {% if step.currentStep == 'initial' %}
+
+ {% endif %}
+ {{ step.currentStep }}
+
+ {% if not loop.last %}
+ →
+ {% endif %}
+ {% endfor %}
+
+{% endmacro %}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_content.fr.txt.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_content.fr.txt.twig
new file mode 100644
index 000000000..1e8469968
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_content.fr.txt.twig
@@ -0,0 +1,13 @@
+{{ dest.label }},
+
+Un suivi "{{ workflow.text }}" a atteint une nouvelle étape: {{ workflow.text }}
+{%- if is_dest %}
+
+Vous êtes invités à valider cette étape au plus tôt.
+{% endif %}
+
+Vous pouvez visualiser le workflow sur cette page:
+
+{{ absolute_url(path('chill_main_workflow_show', {'id': entity_workflow.id})) }}
+
+Cordialement,
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_title.fr.txt.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_title.fr.txt.twig
new file mode 100644
index 000000000..4960b6138
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_title.fr.txt.twig
@@ -0,0 +1,5 @@
+{%- if is_dest -%}
+Un suivi {{ workflow.text }} demande votre attention
+{%- else -%}
+Un suivi {{ workflow.text }} a atteint une nouvelle étape: {{ place.text }}
+{%- endif -%}
diff --git a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php
index ae2eb1f2c..9b6d89e83 100644
--- a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php
+++ b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php
@@ -69,6 +69,15 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
'counter' => $nbNotifications,
]);
+ $menu
+ ->addChild(
+ $this->translator->trans('workflow.My workflows'),
+ ['route' => 'chill_main_workflow_list_dest']
+ )
+ ->setExtras([
+ 'order' => 700,
+ ]);
+
$menu
->addChild(
'Change password',
diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/EntityWorkflowVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/EntityWorkflowVoter.php
new file mode 100644
index 000000000..9542d3acb
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Security/Authorization/EntityWorkflowVoter.php
@@ -0,0 +1,70 @@
+manager = $manager;
+ $this->security = $security;
+ }
+
+ protected function supports($attribute, $subject)
+ {
+ return $subject instanceof EntityWorkflow && in_array($attribute, self::getRoles(), true);
+ }
+
+ protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
+ {
+ switch ($attribute) {
+ case self::CREATE:
+ case self::SEE:
+ $handler = $this->manager->getHandler($subject);
+
+ $entityAttribute = $handler->getRoleShow($subject);
+
+ if (null === $entityAttribute) {
+ return true;
+ }
+
+ return $this->security->isGranted($entityAttribute, $handler->getRelatedEntity($subject));
+
+ default:
+ throw new UnexpectedValueException("attribute {$attribute} not supported");
+ }
+ }
+
+ private static function getRoles(): array
+ {
+ return [
+ self::SEE,
+ self::CREATE,
+ ];
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Service/Mailer/ChillMailer.php b/src/Bundle/ChillMainBundle/Service/Mailer/ChillMailer.php
new file mode 100644
index 000000000..9e1648753
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Service/Mailer/ChillMailer.php
@@ -0,0 +1,50 @@
+initial = $initial;
+ $this->chillLogger = $chillLogger;
+ }
+
+ public function send(RawMessage $message, ?Envelope $envelope = null): void
+ {
+ if ($message instanceof Email) {
+ $message->subject($this->prefix . $message->getSubject());
+ }
+
+ $this->chillLogger->info('chill email sent', [
+ 'to' => array_map(static function (Address $address) {
+ return $address->getAddress();
+ }, $message->getTo()),
+ 'subject' => $message->getSubject(),
+ ]);
+
+ $this->initial->send($message, $envelope);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/EntityWorkflowTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/EntityWorkflowTest.php
new file mode 100644
index 000000000..c89101c5b
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Tests/Entity/Workflow/EntityWorkflowTest.php
@@ -0,0 +1,81 @@
+getCurrentStep()->setFinalizeAfter(true);
+ $entityWorkflow->setStep('final');
+
+ $this->assertTrue($entityWorkflow->isFinalize());
+ }
+
+ public function testIsFinalizeWith4Steps()
+ {
+ $entityWorkflow = new EntityWorkflow();
+
+ $this->assertFalse($entityWorkflow->isFinalize());
+
+ $entityWorkflow->setStep('two');
+
+ $this->assertFalse($entityWorkflow->isFinalize());
+
+ $entityWorkflow->setStep('previous_final');
+
+ $this->assertFalse($entityWorkflow->isFinalize());
+
+ $entityWorkflow->getCurrentStep()->setFinalizeAfter(true);
+ $entityWorkflow->setStep('final');
+
+ $this->assertTrue($entityWorkflow->isFinalize());
+ }
+
+ public function testIsFreeze()
+ {
+ $entityWorkflow = new EntityWorkflow();
+
+ $this->assertFalse($entityWorkflow->isFreeze());
+
+ $entityWorkflow->setStep('step_one');
+
+ $this->assertFalse($entityWorkflow->isFreeze());
+
+ $entityWorkflow->setStep('step_three');
+
+ $this->assertFalse($entityWorkflow->isFreeze());
+
+ $entityWorkflow->getCurrentStep()->setFreezeAfter(true);
+
+ $this->assertFalse($entityWorkflow->isFreeze());
+
+ $entityWorkflow->setStep('freezed');
+
+ $this->assertTrue($entityWorkflow->isFreeze());
+
+ $entityWorkflow->setStep('after_freeze');
+
+ $this->assertTrue($entityWorkflow->isFreeze());
+
+ $this->assertTrue($entityWorkflow->getCurrentStep()->isFreezeAfter());
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php
index 94266077a..82693b9c1 100644
--- a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php
+++ b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php
@@ -44,7 +44,9 @@ use function count;
final class ExportManagerTest extends KernelTestCase
{
use PrepareCenterTrait;
+
use PrepareScopeTrait;
+
use PrepareUserTrait;
private Prophet $prophet;
diff --git a/src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php b/src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php
index f4984167f..32382713a 100644
--- a/src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php
+++ b/src/Bundle/ChillMainBundle/Tests/Security/Authorization/AuthorizationHelperTest.php
@@ -34,8 +34,11 @@ use function in_array;
final class AuthorizationHelperTest extends KernelTestCase
{
use PrepareCenterTrait;
+
use PrepareScopeTrait;
+
use PrepareUserTrait;
+
use ProphecyTrait;
protected function setUp(): void
diff --git a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php
new file mode 100644
index 000000000..2e97f77af
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php
@@ -0,0 +1,39 @@
+handlers = $handlers;
+ $this->registry = $registry;
+ }
+
+ public function getHandler(EntityWorkflow $entityWorkflow, array $options = []): EntityWorkflowHandlerInterface
+ {
+ foreach ($this->handlers as $handler) {
+ if ($handler->supports($entityWorkflow, $options)) {
+ return $handler;
+ }
+ }
+
+ throw new HandlerNotFoundException();
+ }
+
+ public function getSupportedWorkflows(EntityWorkflow $entityWorkflow): array
+ {
+ return $this->registry->all($entityWorkflow);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php
new file mode 100644
index 000000000..fba36924a
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php
@@ -0,0 +1,114 @@
+chillLogger = $chillLogger;
+ $this->security = $security;
+ $this->userRender = $userRender;
+ }
+
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ 'workflow.transition' => 'onTransition',
+ 'workflow.guard' => [
+ ['guardEntityWorkflow', 0],
+ ],
+ ];
+ }
+
+ public function guardEntityWorkflow(GuardEvent $event)
+ {
+ if (!$event->getSubject() instanceof EntityWorkflow) {
+ return;
+ }
+
+ /** @var EntityWorkflow $entityWorkflow */
+ $entityWorkflow = $event->getSubject();
+
+ if ($entityWorkflow->isFinalize()) {
+ $event->addTransitionBlocker(
+ new TransitionBlocker(
+ 'workflow.The workflow is finalized',
+ 'd6306280-7535-11ec-a40d-1f7bee26e2c0'
+ )
+ );
+
+ return;
+ }
+
+ if (!$entityWorkflow->getCurrentStep()->getDestUser()->contains($this->security->getUser())) {
+ if (!$event->getMarking()->has('initial')) {
+ $event->addTransitionBlocker(new TransitionBlocker(
+ 'workflow.You are not allowed to apply a transition on this workflow. Only those users are allowed: %users%',
+ 'f3eeb57c-7532-11ec-9495-e7942a2ac7bc',
+ [
+ '%users%' => implode(
+ ', ',
+ $entityWorkflow->getCurrentStep()->getDestUser()->map(function (User $u) {
+ return $this->userRender->renderString($u, []);
+ })->toArray()
+ ),
+ ]
+ ));
+ }
+ }
+ }
+
+ public function onTransition(Event $event)
+ {
+ if (!$event->getSubject() instanceof EntityWorkflow) {
+ return;
+ }
+
+ /** @var EntityWorkflow $entityWorkflow */
+ $entityWorkflow = $event->getSubject();
+ $step = $entityWorkflow->getCurrentStep();
+
+ $step
+ ->setTransitionAfter($event->getTransition()->getName())
+ ->setTransitionAt(new DateTimeImmutable('now'))
+ ->setTransitionBy($this->security->getUser());
+
+ $this->chillLogger->info('[workflow] apply transition on entityWorkflow', [
+ 'relatedEntityClass' => $entityWorkflow->getRelatedEntityClass(),
+ 'relatedEntityId' => $entityWorkflow->getRelatedEntityId(),
+ 'transition' => $event->getTransition()->getName(),
+ 'by_user' => $this->security->getUser(),
+ 'entityWorkflow' => $entityWorkflow->getId(),
+ ]);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php
new file mode 100644
index 000000000..fd251bca6
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php
@@ -0,0 +1,103 @@
+entityManager = $entityManager;
+ $this->engine = $engine;
+ $this->metadataExtractor = $metadataExtractor;
+ $this->registry = $registry;
+ $this->security = $security;
+ }
+
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ 'workflow.completed' => 'onCompleted',
+ ];
+ }
+
+ public function onCompleted(Event $event): void
+ {
+ if (!$event->getSubject() instanceof EntityWorkflow) {
+ return;
+ }
+
+ /** @var EntityWorkflow $entityWorkflow */
+ $entityWorkflow = $event->getSubject();
+
+ $dests = array_merge(
+ $entityWorkflow->getSubscriberToStep()->toArray(),
+ $entityWorkflow->isFinalize() ? $entityWorkflow->getSubscriberToFinal()->toArray() : [],
+ $entityWorkflow->getCurrentStep()->getDestUser()->toArray()
+ );
+
+ $place = $this->metadataExtractor->buildArrayPresentationForPlace($entityWorkflow);
+ $workflow = $this->metadataExtractor->buildArrayPresentationForWorkflow(
+ $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName())
+ );
+
+ $visited = [];
+
+ foreach ($dests as $subscriber) {
+ if (
+ $this->security->getUser() === $subscriber
+ || in_array($subscriber->getId(), $visited, true)
+ ) {
+ continue;
+ }
+
+ $context = [
+ 'entity_workflow' => $entityWorkflow,
+ 'dest' => $subscriber,
+ 'place' => $place,
+ 'workflow' => $workflow,
+ 'is_dest' => $entityWorkflow->getCurrentStep()->getDestUser()->contains($subscriber),
+ ];
+
+ $notification = new Notification();
+ $notification
+ ->setRelatedEntityId($entityWorkflow->getId())
+ ->setRelatedEntityClass(EntityWorkflow::class)
+ ->setTitle($this->engine->render('@ChillMain/Workflow/workflow_notification_on_transition_completed_title.fr.txt.twig', $context))
+ ->setMessage($this->engine->render('@ChillMain/Workflow/workflow_notification_on_transition_completed_content.fr.txt.twig', $context))
+ ->addAddressee($subscriber);
+ $this->entityManager->persist($notification);
+
+ $visited[] = $subscriber->getId();
+ }
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/Exception/HandlerNotFoundException.php b/src/Bundle/ChillMainBundle/Workflow/Exception/HandlerNotFoundException.php
new file mode 100644
index 000000000..99ad5b88c
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/Exception/HandlerNotFoundException.php
@@ -0,0 +1,18 @@
+registry = $registry;
+ $this->translatableStringHelper = $translatableStringHelper;
+ }
+
+ public function availableWorkflowFor(string $relatedEntityClass, ?int $relatedEntityId = 0): array
+ {
+ $blankEntityWorkflow = new EntityWorkflow();
+ $blankEntityWorkflow
+ ->setRelatedEntityId($relatedEntityId)
+ ->setRelatedEntityClass($relatedEntityClass);
+
+ // build the list of available workflows, and extract their names from metadata
+ $workflows = $this->registry->all($blankEntityWorkflow);
+ $workflowsList = [];
+
+ foreach ($workflows as $workflow) {
+ $metadata = $workflow->getMetadataStore()->getWorkflowMetadata();
+ $text = array_key_exists('label', $metadata) ?
+ $this->translatableStringHelper->localize($metadata['label']) : $workflow->getName();
+
+ $workflowsList[] = ['name' => $workflow->getName(), 'text' => $text];
+ }
+
+ return $workflowsList;
+ }
+
+ public function buildArrayPresentationForPlace(EntityWorkflow $entityWorkflow): array
+ {
+ $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
+
+ $markingMetadata = $workflow->getMetadataStore()->getPlaceMetadata($entityWorkflow->getCurrentStep()->getCurrentStep());
+
+ $text = array_key_exists('label', $markingMetadata) ?
+ $this->translatableStringHelper->localize($markingMetadata['label']) : $entityWorkflow->getCurrentStep()->getCurrentStep();
+
+ return ['name' => $entityWorkflow->getCurrentStep()->getCurrentStep(), 'text' => $text];
+ }
+
+ public function buildArrayPresentationForWorkflow(WorkflowInterface $workflow): array
+ {
+ $metadata = $workflow->getMetadataStore()->getWorkflowMetadata();
+ $text = array_key_exists('label', $metadata) ?
+ $this->translatableStringHelper->localize($metadata['label']) : $workflow->getName();
+
+ return ['name' => $workflow->getName(), 'text' => $text];
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/Notification/WorkflowNotificationHandler.php b/src/Bundle/ChillMainBundle/Workflow/Notification/WorkflowNotificationHandler.php
new file mode 100644
index 000000000..756d79fbf
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/Notification/WorkflowNotificationHandler.php
@@ -0,0 +1,45 @@
+entityWorkflowRepository->find($notification->getRelatedEntityId());
+
+ return [
+ 'entity_workflow' => $entityWorkflow,
+ 'handler' => $this->entityWorkflowManager->getHandler($entityWorkflow),
+ ];
+ }
+
+ public function supports(Notification $notification, array $options = []): bool
+ {
+ return $notification->getRelatedEntityClass() === EntityWorkflow::class;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/RelatedEntityWorkflowSupportsStrategy.php b/src/Bundle/ChillMainBundle/Workflow/RelatedEntityWorkflowSupportsStrategy.php
new file mode 100644
index 000000000..310c0126b
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/RelatedEntityWorkflowSupportsStrategy.php
@@ -0,0 +1,35 @@
+getMetadataStore()->getWorkflowMetadata()['related_entity']
+ as $relatedEntityClass) {
+ if ($subject->getRelatedEntityClass() === $relatedEntityClass) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtension.php b/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtension.php
new file mode 100644
index 000000000..09dce0c6e
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtension.php
@@ -0,0 +1,29 @@
+ true, 'is_safe' => ['html']]
+ ),
+ ];
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtensionRuntime.php b/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtensionRuntime.php
new file mode 100644
index 000000000..0f034267f
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtensionRuntime.php
@@ -0,0 +1,79 @@
+entityWorkflowManager = $entityWorkflowManager;
+ $this->registry = $registry;
+ $this->repository = $repository;
+ $this->metadataExtractor = $metadataExtractor;
+ $this->normalizer = $normalizer;
+ }
+
+ public function listWorkflows(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string
+ {
+ $blankEntityWorkflow = new EntityWorkflow();
+ $blankEntityWorkflow
+ ->setRelatedEntityId($relatedEntityId)
+ ->setRelatedEntityClass($relatedEntityClass);
+
+ $workflowsList = $this->metadataExtractor->availableWorkflowFor($relatedEntityClass, $relatedEntityId);
+
+ // get the related entity already created
+ $entityWorkflows = [];
+
+ foreach ($entityWorkflowsNaked = $this->repository->findBy(
+ ['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId]
+ ) as $entityWorkflow) {
+ $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,
+ ]);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Workflow/Validator/EntityWorkflowCreation.php b/src/Bundle/ChillMainBundle/Workflow/Validator/EntityWorkflowCreation.php
new file mode 100644
index 000000000..dc01f9019
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Workflow/Validator/EntityWorkflowCreation.php
@@ -0,0 +1,35 @@
+entityWorkflowManager = $entityWorkflowManager;
+ }
+
+ /**
+ * @param EntityWorkflow $value
+ * @param Constraint|EntityWorkflowCreation $constraint
+ */
+ public function validate($value, Constraint $constraint)
+ {
+ if (!$value instanceof EntityWorkflow) {
+ throw new UnexpectedValueException($value, EntityWorkflow::class);
+ }
+
+ if (!$constraint instanceof EntityWorkflowCreation) {
+ throw new UnexpectedTypeException($constraint, EntityWorkflowCreation::class);
+ }
+
+ try {
+ $handler = $this->entityWorkflowManager->getHandler($value);
+ } catch (HandlerNotFoundException $e) {
+ $this->context->buildViolation($constraint->messageHandlerNotFound)
+ ->addViolation();
+
+ return;
+ }
+
+ if (null === $handler->getRelatedEntity($value)) {
+ $this->context->buildViolation($constraint->messageEntityNotFound)
+ ->addViolation();
+ }
+
+ $workflows = $this->entityWorkflowManager->getSupportedWorkflows($value);
+
+ $matched = array_filter($workflows, static function (WorkflowInterface $workflow) use ($value) {
+ return $workflow->getName() === $value->getWorkflowName();
+ });
+
+ if (0 === count($matched)) {
+ $this->context->buildViolation($constraint->messageWorkflowNotAvailable)
+ ->addViolation();
+ }
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/chill.api.specs.yaml b/src/Bundle/ChillMainBundle/chill.api.specs.yaml
index 753cb855c..4f2e3648e 100644
--- a/src/Bundle/ChillMainBundle/chill.api.specs.yaml
+++ b/src/Bundle/ChillMainBundle/chill.api.specs.yaml
@@ -116,7 +116,15 @@ components:
type: number
minItems: 2
maxItems: 2
-
+ UserJob:
+ type: object
+ properties:
+ id:
+ type: integer
+ label:
+ type: object
+ type:
+ type: string
paths:
/1.0/search.json:
@@ -771,5 +779,16 @@ paths:
description: "accepted"
403:
description: "unauthorized"
-
+ /1.0/main/user-job.json:
+ get:
+ tags:
+ - user
+ summary: Return a list of all user jobs
+ responses:
+ 200:
+ description: "ok"
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UserJob'
diff --git a/src/Bundle/ChillMainBundle/chill.webpack.config.js b/src/Bundle/ChillMainBundle/chill.webpack.config.js
index 47aa32865..113d326c1 100644
--- a/src/Bundle/ChillMainBundle/chill.webpack.config.js
+++ b/src/Bundle/ChillMainBundle/chill.webpack.config.js
@@ -52,6 +52,7 @@ module.exports = function(encore, entries)
// Page entrypoints
encore.addEntry('page_login', __dirname + '/Resources/public/page/login/index.js');
encore.addEntry('page_location', __dirname + '/Resources/public/page/location/index.js');
+ encore.addEntry('page_workflow_show', __dirname + '/Resources/public/page/workflow-show/index.js');
buildCKEditor(encore);
@@ -64,6 +65,9 @@ module.exports = function(encore, entries)
encore.addEntry('mod_input_address', __dirname + '/Resources/public/vuejs/Address/mod_input_address_index.js');
encore.addEntry('mod_notification_toggle_read_status', __dirname + '/Resources/public/module/notification/toggle_read.js');
encore.addEntry('mod_pickentity_type', __dirname + '/Resources/public/module/pick-entity/index.js');
+ encore.addEntry('mod_entity_workflow_subscribe', __dirname + '/Resources/public/module/entity-workflow-subscribe/index.js');
+ encore.addEntry('mod_entity_workflow_pick', __dirname + '/Resources/public/module/entity-workflow-pick/index.js');
+ encore.addEntry('mod_wopi_link', __dirname + '/Resources/public/module/wopi-link/index.js');
// Vue entrypoints
encore.addEntry('vue_address', __dirname + '/Resources/public/vuejs/Address/index.js');
diff --git a/src/Bundle/ChillMainBundle/composer.sha1.json b/src/Bundle/ChillMainBundle/composer.sha1.json
deleted file mode 100644
index 70b1161de..000000000
--- a/src/Bundle/ChillMainBundle/composer.sha1.json
+++ /dev/null
@@ -1,1253 +0,0 @@
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA256
-
-{
- "ignoredPath": [
- "\/(\\.\\\/)?(.git)\/",
- "\/(\\.\\\/)?(bin)\/",
- "\/(\\.\\\/)?(vendor)\/"
- ],
- "files": {
- ".\/ChillMainBundle.php": {
- "sha1": "486f7a6815f7084e53ea4642c8a6e88f5fb20d74",
- "size": 712
- },
- ".\/DependencyInjection\/ChillMainExtension.php": {
- "sha1": "6d8c57da39ccec4ae97f21fada5871d9e99582fc",
- "size": 3201
- },
- ".\/DependencyInjection\/ConfigConsistencyCompilerPass.php": {
- "sha1": "0fc4d499ab93f4bf490f1361e6481c3c16b57bc1",
- "size": 2322
- },
- ".\/DependencyInjection\/Configuration.php": {
- "sha1": "baf9d0d3e4077c2fc2bdabf3481cf48c43a2600d",
- "size": 1312
- },
- ".\/DependencyInjection\/TimelineCompilerClass.php": {
- "sha1": "ef4db599d98c540605c945d1f0bc545430980af9",
- "size": 2211
- },
- ".\/DependencyInjection\/SearchableServicesCompilerPass.php": {
- "sha1": "899c75116bfcc6fb8ab0ccb469be00d241979388",
- "size": 2692
- },
- ".\/DependencyInjection\/MissingBundleException.php": {
- "sha1": "74f7f36011c1eeede38c42b5e9d2326387f0ea43",
- "size": 380
- },
- ".\/Controller\/ExportController.php": {
- "sha1": "5dd5b298e82892fec1940f4750f2eedfeb2000f9",
- "size": 1312
- },
- ".\/Controller\/MenuController.php": {
- "sha1": "8888d0b18cd20d802b3b33943e3f06153c20a197",
- "size": 496
- },
- ".\/Controller\/DefaultController.php": {
- "sha1": "4500ee40057591f3c368055a00f5ddab82bd4277",
- "size": 408
- },
- ".\/Controller\/AdminController.php": {
- "sha1": "d1dd266a6f652354de519756e4e51bea33c049ed",
- "size": 1329
- },
- ".\/Controller\/SearchController.php": {
- "sha1": "33be3572dd5dccfd5db6c8f9dd7dc9b57dcc570f",
- "size": 3288
- },
- ".\/Controller\/LoginController.php": {
- "sha1": "c806eca9a0b85818fc6dcd10c0a3fb7abad98775",
- "size": 1601
- },
- ".\/LICENSE": {
- "sha1": "78e50e186b04c8fe1defaa098f1c192181b3d837",
- "size": 34520
- },
- ".\/phpdoc.dist.xml": {
- "sha1": "96cb03bf47a0cca3fd66b2c7330d0bbfbc34a6ed",
- "size": 351
- },
- ".\/Timeline\/TimelineBuilder.php": {
- "sha1": "a428bd05c209612a39e91d74ca3efcc5240e692b",
- "size": 8640
- },
- ".\/Timeline\/TimelineProviderInterface.php": {
- "sha1": "10dad8db49165cfb53052b8c049b8135bfaf8f2b",
- "size": 4306
- },
- ".\/README.md": {
- "sha1": "e359295a2a709abcd9a01fd68f4f0405402ae9aa",
- "size": 381
- },
- ".\/Test\/ProphecyTrait.php": {
- "sha1": "a1861f5852a259f2569056572db91dd9b922e4a1",
- "size": 1391
- },
- ".\/Test\/PrepareCenterTrait.php": {
- "sha1": "96f691033f3588fa94bcc5144c249fc0c41b7390",
- "size": 1721
- },
- ".\/Test\/PrepareScopeTrait.php": {
- "sha1": "3652891d046db9b54e2e4e407b981fe59f61c687",
- "size": 1702
- },
- ".\/Test\/PrepareUserTrait.php": {
- "sha1": "ff9ed48c6442f9dcb00ad7ebaef9037c34aa8a2e",
- "size": 2873
- },
- ".\/Templating\/TranslatableStringHelper.php": {
- "sha1": "357660d0cbab2174cd1a386e23e288b22f459cbe",
- "size": 2531
- },
- ".\/Templating\/CSVCellTwig.php": {
- "sha1": "1a4e005c46bd2efbac5d86081943f02027fd84b6",
- "size": 1928
- },
- ".\/Templating\/TranslatableStringTwig.php": {
- "sha1": "70972a18ebc1d62cb186697bee0df9861c6a0024",
- "size": 1733
- },
- ".\/Tests\/DependencyInjection\/ConfigConsistencyCompilerPassTest.php": {
- "sha1": "16ea80d7008b828863c715544788d17861b53d7d",
- "size": 6079
- },
- ".\/Tests\/Controller\/LoginControllerTest.php": {
- "sha1": "7de42a92f401b07c85ee4c6552fe05530662c303",
- "size": 2220
- },
- ".\/Tests\/Controller\/SearchControllerTest.php": {
- "sha1": "4a5a577addbda7e586125d6d73ac4cd3d5805d61",
- "size": 4694
- },
- ".\/Tests\/Controller\/DefaultControllerTest.php": {
- "sha1": "cc3ad194e09b728ac9c6a46fdf6075928f07d401",
- "size": 212
- },
- ".\/Tests\/Services\/ChillMenuTwigFunctionTest.php": {
- "sha1": "2f19b804a52d82d8203f8a9d7a9f3cf814b1e340",
- "size": 3392
- },
- ".\/Tests\/Services\/MenuComposerTest.php": {
- "sha1": "b0f4e212746fa9acb5d07a2d6ae0270151a3e5f7",
- "size": 2675
- },
- ".\/Tests\/TestHelper.php": {
- "sha1": "06585f5422b9321ea9004d85d044ee2bba49da67",
- "size": 1479
- },
- ".\/Tests\/bootstrap.php": {
- "sha1": "6327576d659198d71eb4fa0d5ba0b6841d7ba93a",
- "size": 213
- },
- ".\/Tests\/Routing\/Loader\/RouteLoaderTest.php": {
- "sha1": "0c46f4ad46bcc192f40813f3036f8c6d6ec876dd",
- "size": 1565
- },
- ".\/Tests\/Security\/Authorization\/AuthorizationHelperTest.php": {
- "sha1": "c14f3a8ab498b9b7278174dc083f8820d677a3aa",
- "size": 16488
- },
- ".\/Tests\/Form\/Type\/CenterTypeTest.php": {
- "sha1": "ac5f720c17566d91682966a6978a71f819ef5ac2",
- "size": 4959
- },
- ".\/Tests\/Search\/AbstractSearchTest.php": {
- "sha1": "49aa6a04cf27338d1820ece8178832afef1b88c3",
- "size": 1703
- },
- ".\/Tests\/Search\/SearchProviderTest.php": {
- "sha1": "4fcc41bbe832c7f4cdebdc268716714e6a06a73e",
- "size": 8571
- },
- ".\/Tests\/Fixtures\/Resources\/views\/menus\/fakeTemplate.html.twig": {
- "sha1": "7201545189fd2e8c6e20707a6ed39653e17fb403",
- "size": 13
- },
- ".\/Tests\/Fixtures\/Resources\/views\/menus\/normalMenu.html.twig": {
- "sha1": "a035e9811fe2df5fd10e95ffac452ec468aa9b51",
- "size": 99
- },
- ".\/Tests\/Fixtures\/Resources\/views\/menus\/overrideTemplate.html.twig": {
- "sha1": "15edeaa5d40de82f2160d81610348aa7e98470a9",
- "size": 79
- },
- ".\/Tests\/Fixtures\/App\/console": {
- "sha1": "9972b621198701ee1000772e24737dbf4eba8c54",
- "size": 867
- },
- ".\/Tests\/Fixtures\/App\/config\/parameters.yml.dist": {
- "sha1": "42d64174c4ad195e35325457c3fd1aa7873464fe",
- "size": 285
- },
- ".\/Tests\/Fixtures\/App\/config\/parameters.travis.yml": {
- "sha1": "34981ee196b34e80fbcbf1c35171b4670ad5b921",
- "size": 350
- },
- ".\/Tests\/Fixtures\/App\/config\/routing.yml": {
- "sha1": "c9183faa67e5caab5777d7c96995829fe1dfd5eb",
- "size": 1066
- },
- ".\/Tests\/Fixtures\/App\/config\/config.yml": {
- "sha1": "36ce2c00cce5a3282a4029b70532015f949a2fe3",
- "size": 1229
- },
- ".\/Tests\/Fixtures\/App\/config\/config_test.yml": {
- "sha1": "b944ba9ee470967d240bb6da828e4394b5cf3867",
- "size": 1238
- },
- ".\/Tests\/Fixtures\/App\/Resources\/views\/base.html.twig": {
- "sha1": "10bcd2379bf8dc71e7c19edbf5cf0621632b8c7a",
- "size": 386
- },
- ".\/Tests\/Fixtures\/App\/AppKernel.php": {
- "sha1": "118464a22a59801a99b2de96eaa3499dbd3c3213",
- "size": 1424
- },
- ".\/Command\/LoadCountriesCommand.php": {
- "sha1": "86bbe6f915d1062b496df9ef9e9231ffba457d8d",
- "size": 2428
- },
- ".\/Command\/SetPasswordCommand.php": {
- "sha1": "7bb2bac31b5b0fbe7a7ea4cc0cefae7680a918b4",
- "size": 2959
- },
- ".\/Command\/LoadAndUpdateLanguagesCommand.php": {
- "sha1": "4431e609d878ebd1914038d2aa0b665832dd44b3",
- "size": 4771
- },
- ".\/Entity\/PermissionsGroup.php": {
- "sha1": "2382ba3881d79adec22d9266e337c49fd676c6e0",
- "size": 1876
- },
- ".\/Entity\/Scope.php": {
- "sha1": "0611043ecfce4e99ad224c916c24132e61d64ccf",
- "size": 2025
- },
- ".\/Entity\/Language.php": {
- "sha1": "dd5e53e9f0bf0a63b618b04910d41850b1bbb76d",
- "size": 1612
- },
- ".\/Entity\/GroupCenter.php": {
- "sha1": "1d7179096c7171b2c81b054ba92685134dd6960e",
- "size": 2511
- },
- ".\/Entity\/HasCenterInterface.php": {
- "sha1": "a0e4d97fe9362681126459e4c08caeb6242eac6f",
- "size": 1064
- },
- ".\/Entity\/Center.php": {
- "sha1": "96842ba00ee197693b9b5e319fe86aa7ca324105",
- "size": 1836
- },
- ".\/Entity\/RoleScope.php": {
- "sha1": "21fe11f5a6df42bf176d3e2634e4ea83e6b0481f",
- "size": 1918
- },
- ".\/Entity\/User.php": {
- "sha1": "0b7ddaf07e29e3ad6585605dddabdd7c5b72a8aa",
- "size": 2953
- },
- ".\/Entity\/HasScopeInterface.php": {
- "sha1": "66e35485cc2a961071f432bd25f81128d0022332",
- "size": 1049
- },
- ".\/Entity\/Country.php": {
- "sha1": "3de779f6a4ee183084da6ff4aab07f164d85ae5e",
- "size": 1138
- },
- ".\/Routing\/Loader\/ChillRoutesLoader.php": {
- "sha1": "307a90b4fff9be2cf1fe61997c8740efe1eab233",
- "size": 2006
- },
- ".\/Routing\/MenuTwig.php": {
- "sha1": "e75011fbfc6db57a2250f176f248920bac60b502",
- "size": 3143
- },
- ".\/Routing\/MenuComposer.php": {
- "sha1": "e1de1a8eb2c21992d54d5807a467453fa2678ca2",
- "size": 3013
- },
- ".\/Resources\/bower.json": {
- "sha1": "2b86f7ee2263b664e12450aed9ec0553ac6dda69",
- "size": 523
- },
- ".\/Resources\/translations\/messages.fr.yml": {
- "sha1": "57eaf17c9e548db79d7924e3e1bbdbba102706a3",
- "size": 952
- },
- ".\/Resources\/translations\/messages.nl.yml": {
- "sha1": "ce96fd61aa3e2d7ef35991e5d720c6f7fb344783",
- "size": 290
- },
- ".\/Resources\/migrations\/Version20141128194409.php": {
- "sha1": "5fa1e785c10d00eaf884290c525f5e62c78e32c9",
- "size": 5065
- },
- ".\/Resources\/public\/fonts\/fontawesome-webfont.ttf": {
- "sha1": "9088143b19979779b2116cef38b661f72d982e19",
- "size": 122092
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/MerriweatherSans-Light.ttf": {
- "sha1": "a8b0774b5221d5c1180dba6372af6976036bb563",
- "size": 47112
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/MerriweatherSans-Italic.ttf": {
- "sha1": "167286c2b48567b3a6c9cf6a4aabca1a76f43aeb",
- "size": 47560
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/MerriweatherSans-Bold.ttf": {
- "sha1": "cb4ff3d7cc8286d986d623fffe8449bdc7b8462b",
- "size": 51068
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/MerriweatherSans-ExtraBold.ttf": {
- "sha1": "b8e71400925eb44937ce1eede2cb6b722e4a4260",
- "size": 50968
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/MerriweatherSans-LightItalic.ttf": {
- "sha1": "5528127785870d5d7631751991c9cfa84ddc5e9b",
- "size": 47888
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/MerriweatherSans-BoldItalic.ttf": {
- "sha1": "230a83d2c219e2be34c617db60c3bdedc020dcf1",
- "size": 47572
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/MerriweatherSans-Regular.ttf": {
- "sha1": "2874cc397a1fb10ca8c6d8666a148ae9b968c741",
- "size": 48528
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/MerriweatherSans-ExtraBoldItalic.ttf": {
- "sha1": "c1b68263fbd7e85be22a71ebba94e98e142266a0",
- "size": 47668
- },
- ".\/Resources\/public\/fonts\/Merriweather_Sans\/OFL.txt": {
- "sha1": "4b027cb6b2bf71c3034a82524509684698222efa",
- "size": 4539
- },
- ".\/Resources\/public\/fonts\/fontawesome-webfont.svg": {
- "sha1": "26bb1c5ef100a1cd71923f1d5dc29fc797ef4639",
- "size": 313398
- },
- ".\/Resources\/public\/fonts\/Oxygen\/Oxygen-Regular.ttf": {
- "sha1": "07b6892d6da15cea77801ed906786f441c31cbac",
- "size": 48092
- },
- ".\/Resources\/public\/fonts\/Oxygen\/Oxygen-Light.ttf": {
- "sha1": "75852fb55e25f255c3e146f2e13c25b36c0800be",
- "size": 43852
- },
- ".\/Resources\/public\/fonts\/Oxygen\/Oxygen-Bold.ttf": {
- "sha1": "7fc4a1c8bc74506181ce2095416336e459aacb32",
- "size": 48812
- },
- ".\/Resources\/public\/fonts\/Oxygen\/OFL.txt": {
- "sha1": "ccc1f19b592311ff018ece9a31e88d62a78e705d",
- "size": 4489
- },
- ".\/Resources\/public\/fonts\/fontawesome-webfont.woff": {
- "sha1": "56ce13e71c2150d81bc972940584915181bd6081",
- "size": 71508
- },
- ".\/Resources\/public\/fonts\/Oxygen_Mono\/OxygenMono-Regular.ttf": {
- "sha1": "23ed21425e8ab102734c021cbf47eec3be777c56",
- "size": 68940
- },
- ".\/Resources\/public\/fonts\/Oxygen_Mono\/OFL.txt": {
- "sha1": "ccc1f19b592311ff018ece9a31e88d62a78e705d",
- "size": 4489
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/MerriweatherSans-Light.ttf": {
- "sha1": "a8b0774b5221d5c1180dba6372af6976036bb563",
- "size": 47112
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/MerriweatherSans-Italic.ttf": {
- "sha1": "167286c2b48567b3a6c9cf6a4aabca1a76f43aeb",
- "size": 47560
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/MerriweatherSans-Bold.ttf": {
- "sha1": "cb4ff3d7cc8286d986d623fffe8449bdc7b8462b",
- "size": 51068
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/MerriweatherSans-ExtraBold.ttf": {
- "sha1": "b8e71400925eb44937ce1eede2cb6b722e4a4260",
- "size": 50968
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/MerriweatherSans-LightItalic.ttf": {
- "sha1": "5528127785870d5d7631751991c9cfa84ddc5e9b",
- "size": 47888
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/MerriweatherSans-BoldItalic.ttf": {
- "sha1": "230a83d2c219e2be34c617db60c3bdedc020dcf1",
- "size": 47572
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/MerriweatherSans-Regular.ttf": {
- "sha1": "2874cc397a1fb10ca8c6d8666a148ae9b968c741",
- "size": 48528
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/MerriweatherSans-ExtraBoldItalic.ttf": {
- "sha1": "c1b68263fbd7e85be22a71ebba94e98e142266a0",
- "size": 47668
- },
- ".\/Resources\/public\/fonts\/icons\/Merriweather_Sans\/OFL.txt": {
- "sha1": "4b027cb6b2bf71c3034a82524509684698222efa",
- "size": 4539
- },
- ".\/Resources\/public\/fonts\/icons\/Oxygen\/Oxygen-Regular.ttf": {
- "sha1": "07b6892d6da15cea77801ed906786f441c31cbac",
- "size": 48092
- },
- ".\/Resources\/public\/fonts\/icons\/Oxygen\/Oxygen-Light.ttf": {
- "sha1": "75852fb55e25f255c3e146f2e13c25b36c0800be",
- "size": 43852
- },
- ".\/Resources\/public\/fonts\/icons\/Oxygen\/Oxygen-Bold.ttf": {
- "sha1": "7fc4a1c8bc74506181ce2095416336e459aacb32",
- "size": 48812
- },
- ".\/Resources\/public\/fonts\/icons\/Oxygen\/OFL.txt": {
- "sha1": "ccc1f19b592311ff018ece9a31e88d62a78e705d",
- "size": 4489
- },
- ".\/Resources\/public\/fonts\/icons\/Oxygen_Mono\/OxygenMono-Regular.ttf": {
- "sha1": "23ed21425e8ab102734c021cbf47eec3be777c56",
- "size": 68940
- },
- ".\/Resources\/public\/fonts\/icons\/Oxygen_Mono\/OFL.txt": {
- "sha1": "ccc1f19b592311ff018ece9a31e88d62a78e705d",
- "size": 4489
- },
- ".\/Resources\/public\/fonts\/icons\/entypo.woff": {
- "sha1": "9729b28b6872d971935ff58f460cf3c26dcb7380",
- "size": 45152
- },
- ".\/Resources\/public\/fonts\/icons\/entypo.ttf": {
- "sha1": "2c4554343a7bee0fa98872c42bdabad6e459c7e0",
- "size": 75800
- },
- ".\/Resources\/public\/fonts\/icons\/entypo.eot": {
- "sha1": "0c2dfe5c970ec940f2e20a7059fd34d942bfe0a0",
- "size": 76038
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bolditalic-webfont.woff2": {
- "sha1": "eecdb6a12815fc98c4722fccbc0fcc1a6d6af678",
- "size": 20520
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-light-webfont.ttf": {
- "sha1": "fd6b68abd8f0355691d292cf11439979d1470c75",
- "size": 44716
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabold-webfont.woff2": {
- "sha1": "4f072690858399f18f8de839a37aadfd2bb92ec3",
- "size": 19548
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabolditalic-webfont.ttf": {
- "sha1": "689d15106a4b7d004c3bff509056ef38012381d5",
- "size": 49776
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibold-webfont.ttf": {
- "sha1": "33e9132bc2bfe3b1cdd855c1b2785805612f30dc",
- "size": 45232
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabolditalic-webfont.woff": {
- "sha1": "ce6eeb24a749d65d640de99e532da9365bdd3b54",
- "size": 27192
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-italic-webfont.svg": {
- "sha1": "c8f3e4d79cb1200d2c06f6d3ca907e44d996c2d5",
- "size": 121564
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabold-webfont.woff": {
- "sha1": "5310f3e76b0384ca772f753167ef2c0f7c599c8a",
- "size": 25836
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-lightitalic-webfont.eot": {
- "sha1": "22fc5b5288a5932a82123ebc0250fb011fc4e72e",
- "size": 23610
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bold-webfont.eot": {
- "sha1": "588ad51db55ac6f4f9cee09e7ad8ee4f0c24995b",
- "size": 21929
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabold-webfont.svg": {
- "sha1": "aec79988d057f7500989f93a81e92348f7d39028",
- "size": 117713
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-lightitalic-webfont.woff": {
- "sha1": "6f7611793bdd2f9ac1ea10ac547e754522f8a59a",
- "size": 27536
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-light-webfont.eot": {
- "sha1": "2e856d871e37f2c7bfdfdcbd776f99b7ec3149ac",
- "size": 20615
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibold-webfont.svg": {
- "sha1": "5f7e52194b62d2a43c1dc290c572674c786d112f",
- "size": 117062
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibolditalic-webfont.svg": {
- "sha1": "07d4224491c305fc22d71698bd9bdca430df8a2d",
- "size": 121508
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabold-webfont.ttf": {
- "sha1": "bb9aa7e824366f50f7fcef8da56d96e68e1e37c0",
- "size": 46656
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibolditalic-webfont.eot": {
- "sha1": "3eeb16af6da8df0371e1e3e55b04e20d8a944227",
- "size": 23648
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-italic-webfont.ttf": {
- "sha1": "1527142403d02bfe0864d24f86fc1530bf80f61d",
- "size": 51484
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibold-webfont.woff2": {
- "sha1": "5643975eb6248e58e1437033a3f3281dbabeb675",
- "size": 18928
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bolditalic-webfont.svg": {
- "sha1": "f8c35bdd221ffe638953fc34d5c0fcce257f0d55",
- "size": 120222
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-light-webfont.svg": {
- "sha1": "e5dda5dca2e71bd6426dec33308cb47babd7359a",
- "size": 115890
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-italic-webfont.woff2": {
- "sha1": "8cbe6c4ff28666b8c25d31610952c9fa49c6755d",
- "size": 20876
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabold-webfont.eot": {
- "sha1": "0f1c11999051d12a0b2443ff662b0ce0638aeaed",
- "size": 21978
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-regular-webfont.ttf": {
- "sha1": "6bdfd4fd85d18913ca71a25dbbf2576664a251b2",
- "size": 45184
- },
- ".\/Resources\/public\/fonts\/OpenSans\/OpenSans.css": {
- "sha1": "c450392c124e5e78c3694eee07bf68fc60e37824",
- "size": 5129
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bold-webfont.woff": {
- "sha1": "67614f2d69007a5f4eaec3528f39a3dd265d96de",
- "size": 25716
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bolditalic-webfont.woff": {
- "sha1": "66245a6e5b0ef68d8f28d8a1810a75cdea8a8ee9",
- "size": 26976
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bold-webfont.ttf": {
- "sha1": "1d421bc0b5dbe811f3bff2e31d85572cc4c2f795",
- "size": 46864
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabolditalic-webfont.eot": {
- "sha1": "a3e5fbd60afaf9a99ab7b664588cd182f78d547e",
- "size": 23239
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bold-webfont.svg": {
- "sha1": "a7b581cdb410766fd6ff88d8e6c43977a97cec2f",
- "size": 116917
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-italic-webfont.woff": {
- "sha1": "c77ab3816c6d7a748bde8a9ae15ff7c5846cd2bd",
- "size": 27372
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bold-webfont.woff2": {
- "sha1": "3cebeb208baa82643d3d906e010c3199e0d1a9e8",
- "size": 19440
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibolditalic-webfont.woff2": {
- "sha1": "0d10b4099e20d33c8ebeab1f8993a643ec640281",
- "size": 20944
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-light-webfont.woff2": {
- "sha1": "49e96b5a50a5c194d90ed923e84416eca1aa9502",
- "size": 18208
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-regular-webfont.eot": {
- "sha1": "f42276272ec0fbfbeebffd90bd321343a97eb8e7",
- "size": 21101
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibold-webfont.woff": {
- "sha1": "ff787e7957976f91ccd5a3d00a6eb92803444ed2",
- "size": 24956
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-italic-webfont.eot": {
- "sha1": "d0d45f6c6fb2009948824b905bba2c7dd29bfcf1",
- "size": 23495
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabolditalic-webfont.woff2": {
- "sha1": "55a3fbbe4aab675dc8b62f26a1bdc3b68946c8db",
- "size": 20620
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bolditalic-webfont.eot": {
- "sha1": "1f9c4daec26125cf352ea73778d1e46a6c1730dd",
- "size": 23041
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-lightitalic-webfont.ttf": {
- "sha1": "cc162b19e6ce33a1b1ce129c088e31039abb5b4a",
- "size": 52160
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-lightitalic-webfont.woff2": {
- "sha1": "da125101f197fcc1df4cdf0bf304c02e4b4fa83c",
- "size": 20928
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibolditalic-webfont.ttf": {
- "sha1": "edc193ecf72f19a90effec1fc24505392f66acfb",
- "size": 52216
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-bolditalic-webfont.ttf": {
- "sha1": "1b83cecefbe4cd46839ba71a89c2aae7b8772078",
- "size": 49632
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-regular-webfont.woff": {
- "sha1": "596f44ac5bd5a625e75ae176f32235533e69892e",
- "size": 24776
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-regular-webfont.woff2": {
- "sha1": "9e6a93d65a3de7796ac03570ef384f3e70d7979e",
- "size": 18840
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibolditalic-webfont.woff": {
- "sha1": "2df8d3ae0782ea7df70f8ce9666f7815ecf0b87e",
- "size": 27480
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-lightitalic-webfont.svg": {
- "sha1": "c7b92e3253d77c9b80283cb7d8c0eba83390f3c5",
- "size": 121360
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-regular-webfont.svg": {
- "sha1": "c30c4b7cf9558fe14ded2307f048db3f6bf1c6c2",
- "size": 117868
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-semibold-webfont.eot": {
- "sha1": "35e8ebdb4affd9cd1fd4bc4ae5f012c665bb0a62",
- "size": 21293
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-extrabolditalic-webfont.svg": {
- "sha1": "65298988f19b3fb354d4caa9c2dcc48b734cedf5",
- "size": 120509
- },
- ".\/Resources\/public\/fonts\/OpenSans\/opensans-light-webfont.woff": {
- "sha1": "bd908e8e8a8ec1ec9ffef4b38b39d72bfa5b3df1",
- "size": 24196
- },
- ".\/Resources\/public\/fonts\/fontawesome-webfont.eot": {
- "sha1": "3e63fc9b3de4580f1f3bec0631436f755b80f167",
- "size": 60767
- },
- ".\/Resources\/public\/fonts\/fontawesome-webfont.woff2": {
- "sha1": "1075231650f579955905bb2f6527148a8e2b4b16",
- "size": 56780
- },
- ".\/Resources\/public\/fonts\/FontAwesome.otf": {
- "sha1": "cde9eb92c8a3ba23d648f76ea3931511f30813f4",
- "size": 93888
- },
- ".\/Resources\/public\/js\/moment.js": {
- "sha1": "d587d3c7e2114b650c235e649de1b1fcf6f15a18",
- "size": 96381
- },
- ".\/Resources\/public\/js\/pikaday\/plugins\/pikaday.jquery.js": {
- "sha1": "87bad4d74a96d788bad0b1c8c8ab5d585010dcf5",
- "size": 1411
- },
- ".\/Resources\/public\/js\/pikaday\/pikaday.js": {
- "sha1": "fd77fbf635d182ca36f86df12c161c57f3dde874",
- "size": 27787
- },
- ".\/Resources\/public\/js\/chill.js": {
- "sha1": "9ee120a953c0c6109e254327d5c55326db0767f0",
- "size": 5958
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_fr.js": {
- "sha1": "64fadedc0bd42eb1b08f044fe622a4bba958b405",
- "size": 1077
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ug-CN.js": {
- "sha1": "b1ab83ca9c141423033e76413a112234051a2b0e",
- "size": 904
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_pt-BR.js": {
- "sha1": "86899cad28fbc19233633a564cced787a9566903",
- "size": 969
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_mk.js": {
- "sha1": "512b233cecf53743177a6ad6a18467045c1de25b",
- "size": 1064
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_rs.js": {
- "sha1": "81696be04c861a8d60ca556f5a01993181ffedeb",
- "size": 1058
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ja.js": {
- "sha1": "5b39ea598748cc2eb0fd5da6d168ce682a2761fa",
- "size": 812
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_nl.js": {
- "sha1": "56ab9b9b4349ede06052e653d2379256e6271923",
- "size": 846
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_es.js": {
- "sha1": "b78094388ed64e0f2f74ef2f099bdfc8f64f67e3",
- "size": 1177
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_sv.js": {
- "sha1": "8135ece942a372576fef799fdafffcf16cc75e71",
- "size": 847
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ca.js": {
- "sha1": "998168451ff787f5342796c576a00165d1c23918",
- "size": 952
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_sk.js": {
- "sha1": "1810766036db1364b14732b5010fe122bdc88670",
- "size": 1948
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ko.js": {
- "sha1": "1de5b031a040441691e506d2cae4bf7fc1077693",
- "size": 871
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ka.js": {
- "sha1": "809654587189992e6197e6fe2047e10b895a3400",
- "size": 1078
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_hr.js": {
- "sha1": "bc9ff6062b18beb7f08c496886d4185c2b6f1884",
- "size": 1002
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_eu.js": {
- "sha1": "2770a615f2f199d19652e6fe0e7b2f05dc2f6212",
- "size": 1313
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ms.js": {
- "sha1": "8ef32f03e0e21fb5ea9f1efc5894ba19f8027991",
- "size": 1122
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ar.js": {
- "sha1": "abeef61ed6675a714362dcb5a13456f25cf664b0",
- "size": 1389
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_da.js": {
- "sha1": "4983046d2ea03d08d98c5827eae039fede2d77e2",
- "size": 853
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_gl.js": {
- "sha1": "e48cfe6899dcfd0d3918b2a4139cf341d04a2448",
- "size": 1339
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_uk.js": {
- "sha1": "55d0ac6a060d03ab841669ac20278124d72f8f6b",
- "size": 1415
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_fa.js": {
- "sha1": "482f04766f7d241ffc8f2adf1c55a91977ea370e",
- "size": 1207
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_is.js": {
- "sha1": "85b3be10cbc230245addafaeb0760311793a8210",
- "size": 855
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_id.js": {
- "sha1": "2dded6bfe6e1ddaa27badf94434a28b9d642aab5",
- "size": 1111
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_lv.js": {
- "sha1": "b00350f87ed4c85dffd7ede5895bf07b57d61b50",
- "size": 999
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_vi.js": {
- "sha1": "17906f79ce744d18b79d7a002d08d35911f9a68a",
- "size": 910
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ru.js": {
- "sha1": "bd87199718b3845c0b175916fe7fb55e83e56de5",
- "size": 1171
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_bg.js": {
- "sha1": "e55e1ab4387a8d8e691080ec711e9ef402e936ce",
- "size": 1081
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_et.js": {
- "sha1": "7e1db028938005e3fbad7016aa8e427b22a38bf2",
- "size": 886
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_fi.js": {
- "sha1": "70fd803d1f84165c7c925de4634c6bce2cfc321e",
- "size": 940
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_el.js": {
- "sha1": "752f92bed8924a2b3076bb3fc98e7f7e19e986e5",
- "size": 1128
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_hu.js": {
- "sha1": "f9958a636643a6af88a7d448fdcb95e0af3ff2b2",
- "size": 802
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_de.js": {
- "sha1": "5c27f5981abd033707eac2c106fe85787a0fe3b4",
- "size": 1014
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_ro.js": {
- "sha1": "be15b675f621678dce60e2d849f7deffab61c099",
- "size": 902
- },
- ".\/Resources\/public\/js\/select2\/select2.js": {
- "sha1": "ae6475fe1b20c9d39a614ed3500725c73361c19e",
- "size": 156346
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_zh-CN.js": {
- "sha1": "b598c867f46c26bfc59bbb1d9ad9c23f49e4a2ce",
- "size": 762
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_az.js": {
- "sha1": "09fe790bad3a44f33c1148e7d2ec64e045f75687",
- "size": 1003
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_lt.js": {
- "sha1": "b4e0be7db776ffbc02c7195ee3f730a8a3b92228",
- "size": 1144
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_it.js": {
- "sha1": "165e56e4e8c8cfc7146d1c74b0d221240a0119ad",
- "size": 866
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_pl.js": {
- "sha1": "af66be146a4a47796b42f471bd66675fde8f127f",
- "size": 2015
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_tr.js": {
- "sha1": "10ae82c6f378e073bdb57da194a019fda5be509b",
- "size": 1070
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_th.js": {
- "sha1": "e6df841a8e090937ec18c6ca6ade798e1d7a3401",
- "size": 1063
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_cs.js": {
- "sha1": "4a1bf28b03611ecafc89071342fac4551aa6cd07",
- "size": 1988
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_zh-TW.js": {
- "sha1": "53561f1eb31c5ca88bd3313c72303644d8e8f7ba",
- "size": 774
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_pt-PT.js": {
- "sha1": "6c931c874035adc06320d70c7a2aa45455c7e36f",
- "size": 891
- },
- ".\/Resources\/public\/js\/select2\/select2.min.js": {
- "sha1": "a8ff979cde9687377942b3d22b622988be4fa798",
- "size": 70179
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_he.js": {
- "sha1": "aacce7183cab5a9027a6ab348a40ce2b107735c4",
- "size": 885
- },
- ".\/Resources\/public\/js\/select2\/select2_locale_nb.js": {
- "sha1": "168875332dcec217f5197d26137448edf49941fb",
- "size": 1145
- },
- ".\/Resources\/public\/js\/jquery.js": {
- "sha1": "0fed45ad7a48ace869bc725ca474ad86a1ef1562",
- "size": 247597
- },
- ".\/Resources\/public\/sass\/_custom.scss": {
- "sha1": "81c149492e0232e27f2987beb283e7d488689417",
- "size": 1656
- },
- ".\/Resources\/public\/sass\/custom\/modules\/_forms.scss": {
- "sha1": "c53a8702ef1fd321764a54015de78058bf651cc6",
- "size": 32
- },
- ".\/Resources\/public\/sass\/custom\/modules\/_navigation.scss": {
- "sha1": "a971e0a281f3ed0315a20c95f1a4a12626db475a",
- "size": 838
- },
- ".\/Resources\/public\/sass\/custom\/modules\/_buttons.scss": {
- "sha1": "051b20365b39e2d8974edab6bb313f2127faa842",
- "size": 29
- },
- ".\/Resources\/public\/sass\/custom\/_timeline.scss": {
- "sha1": "13a4da4ac2ecf849e4363ced1f01dd7cc8da20b3",
- "size": 907
- },
- ".\/Resources\/public\/sass\/custom\/config\/_variables.scss": {
- "sha1": "789fc50314370742bf73108a317b1d849a9e2bc2",
- "size": 1092
- },
- ".\/Resources\/public\/sass\/custom\/config\/_colors.scss": {
- "sha1": "e6c71f75d872f3e452bfc8a5d40260a0aa7cc651",
- "size": 741
- },
- ".\/Resources\/public\/img\/favicon.ico": {
- "sha1": "bdd00e45ca1aadc6ea83544b1fff828cc2be364b",
- "size": 4286
- },
- ".\/Resources\/public\/img\/logo-chill-sans-slogan_white_2.svg": {
- "sha1": "e56fa6643ecec8051c88bdb0765250eaec278a09",
- "size": 8730
- },
- ".\/Resources\/public\/img\/logo-chill-sans-slogan_white.png": {
- "sha1": "85c8db8742273c17a6063eb0c686e47222f3cd19",
- "size": 12386
- },
- ".\/Resources\/public\/img\/logo-chill-outil-accompagnement_white.png": {
- "sha1": "e27e38169e67a4d6a305b6e6ffda1275d833ecb4",
- "size": 16075
- },
- ".\/Resources\/public\/img\/logo-chill-sans-slogan_2.svg": {
- "sha1": "1906716911bbd68ee4f226773bd4b463805d6d71",
- "size": 8718
- },
- ".\/Resources\/public\/img\/logo-chill-sans-slogan_white_2.png": {
- "sha1": "5d7a0a21f31730d1040376a49f9de14da83446f5",
- "size": 12201
- },
- ".\/Resources\/public\/img\/favicon-32.xcf": {
- "sha1": "592cea2e988a4a7aec48929a2678b255bfda9836",
- "size": 3252
- },
- ".\/Resources\/public\/img\/logo-chill-outil-accompagnement_white.svg": {
- "sha1": "d6b024cf70c97a81b366404cd68767e51e2622d8",
- "size": 9020
- },
- ".\/Resources\/public\/img\/logo-chill-sans-slogan_2.png": {
- "sha1": "34e019bb122b46cafc82e2cacfd21ffbd0de2cea",
- "size": 12695
- },
- ".\/Resources\/public\/img\/logo-chill-sans-slogan_white.svg": {
- "sha1": "d150ee021ce31f6aa42c4087667fa043052af0dc",
- "size": 8748
- },
- ".\/Resources\/public\/img\/logo-chill-sans-slogan.svg": {
- "sha1": "b162c13b990565466e1eba2cf0cabf16a2818b84",
- "size": 8718
- },
- ".\/Resources\/public\/img\/logo-chill-outil-accompagnement.svg": {
- "sha1": "ad8e75f8ed21df8d22bd4b4839bde9efd67cd143",
- "size": 8988
- },
- ".\/Resources\/public\/img\/logo-chill-outil-accompagnement.png": {
- "sha1": "515305b40689f0572518f44d6b0ede0f23352635",
- "size": 20335
- },
- ".\/Resources\/public\/img\/background\/sun.jpg": {
- "sha1": "ae3e64aa5e4b78dbc3e5d7279a7ad6923bd7bbf9",
- "size": 4532785
- },
- ".\/Resources\/public\/img\/background\/kids-2.jpg": {
- "sha1": "2c4914b05884d27550dba67efe8e1f6f74a1abcb",
- "size": 1551610
- },
- ".\/Resources\/public\/img\/background\/desert.jpg": {
- "sha1": "2c4c4f49a1a05ffaabe164c8fbb8ce063fb8dbf7",
- "size": 2898416
- },
- ".\/Resources\/public\/img\/background\/kids-1.jpg": {
- "sha1": "96fe9c5e7f55049faf46c147f62a3cc7726dfc2c",
- "size": 727626
- },
- ".\/Resources\/public\/img\/favicon.svg": {
- "sha1": "41a2dddeca8a3a7af0fac3dbf73c64e059870395",
- "size": 7852
- },
- ".\/Resources\/public\/img\/logo-chill-sans-slogan.png": {
- "sha1": "5d7a124c49a97a70d7ab1963f2e4d183e9b26551",
- "size": 12847
- },
- ".\/Resources\/public\/css\/scratch.css": {
- "sha1": "e4cfa08aca0114a6c0107303d75895850bdafe4f",
- "size": 298341
- },
- ".\/Resources\/public\/css\/scratch.css.map": {
- "sha1": "d4e833b9e3bbd931374a041abc69b997de0be51c",
- "size": 52387
- },
- ".\/Resources\/public\/css\/pikaday.css": {
- "sha1": "490c0096b51d5fad2b7e76e77e7a86e89e7bb5c8",
- "size": 3462
- },
- ".\/Resources\/public\/css\/chillmain.css": {
- "sha1": "eb3257109ccfccf87ffa81fd06e28a0a3aa0ee0f",
- "size": 1291
- },
- ".\/Resources\/public\/css\/select2\/select2.css": {
- "sha1": "286226130acbf166a528af8d2b65507da584b5a0",
- "size": 19223
- },
- ".\/Resources\/public\/css\/select2\/select2-bootstrap.css": {
- "sha1": "77b264b623cfceb1fd31c26b2e4b9216f4514f3d",
- "size": 3347
- },
- ".\/Resources\/public\/css\/select2\/select2.png": {
- "sha1": "2d350341a645ad33ab5604aca16c05f22a83ff51",
- "size": 613
- },
- ".\/Resources\/Gruntfile.js": {
- "sha1": "432dff5a19a36eb0b9015705d96ac4d19d9c4226",
- "size": 4638
- },
- ".\/Resources\/README.md": {
- "sha1": "762c48e9ad85f91464e6a2a5b65e89b3a0eba88d",
- "size": 242
- },
- ".\/Resources\/config\/routing.yml": {
- "sha1": "b418fe406d448f1cc01b548881e0d56e4cd24add",
- "size": 1430
- },
- ".\/Resources\/config\/services.yml": {
- "sha1": "e47f73a03f2bea7fefd81a5f8875f7bd80a68883",
- "size": 3753
- },
- ".\/Resources\/config\/doctrine\/Language.orm.yml": {
- "sha1": "0dc6c67270a6882b515080220d172f6f37250ae0",
- "size": 196
- },
- ".\/Resources\/config\/doctrine\/Scope.orm.yml": {
- "sha1": "4d326280a05f4c0cf5e42b09f10fb595011490b7",
- "size": 365
- },
- ".\/Resources\/config\/doctrine\/PermissionsGroup.orm.yml": {
- "sha1": "4cada6e0593557dfd8985c95866c49902c4eed3d",
- "size": 393
- },
- ".\/Resources\/config\/doctrine\/User.orm.yml": {
- "sha1": "dc4a6cfd238b4e999d22c916c9fcc39b07675995",
- "size": 689
- },
- ".\/Resources\/config\/doctrine\/Country.orm.yml": {
- "sha1": "20d6148a3ba33ec449df1659c72ca616a2b8177e",
- "size": 331
- },
- ".\/Resources\/config\/doctrine\/GroupCenter.orm.yml": {
- "sha1": "f29e58e46a38985b79857382135bab3f44f69849",
- "size": 434
- },
- ".\/Resources\/config\/doctrine\/Center.orm.yml": {
- "sha1": "649aa08daf81d0dc6aae61e28c3e43d20653e5be",
- "size": 398
- },
- ".\/Resources\/config\/doctrine\/RoleScope.orm.yml": {
- "sha1": "e3d64737ffc323be8c865f0c4b4dc5790780ba89",
- "size": 392
- },
- ".\/Resources\/package.json": {
- "sha1": "edd9e2b56695da4b038b4e1aa7e11cbb7134e516",
- "size": 483
- },
- ".\/Resources\/views\/Admin\/layout.html.twig": {
- "sha1": "0a5cdadad0517844f5fc751da02e5742bb2fcc44",
- "size": 1186
- },
- ".\/Resources\/views\/Timeline\/index.html.twig": {
- "sha1": "43347bde2a7766027b88e467c91305e614059fb3",
- "size": 156
- },
- ".\/Resources\/views\/Export\/layout.html.twig": {
- "sha1": "19ee656960f43f829e9cbd7f00df95b356e6668b",
- "size": 1190
- },
- ".\/Resources\/views\/Menu\/defaultMenu.html.twig": {
- "sha1": "db4e5b73466e9d1ed44820e291777866b0f2005c",
- "size": 992
- },
- ".\/Resources\/views\/Menu\/verticalMenu.html.twig": {
- "sha1": "58eeda3317a2af745a1c461127038fcfd93fe78b",
- "size": 1510
- },
- ".\/Resources\/views\/Menu\/export.html.twig": {
- "sha1": "d62080461d04b6b62c7ac28babb2dfb35ac209f5",
- "size": 927
- },
- ".\/Resources\/views\/Menu\/homepage.html.twig": {
- "sha1": "0279dcdfe33464a8fd6203679c743029ddbf4ac5",
- "size": 1171
- },
- ".\/Resources\/views\/Menu\/section.html.twig": {
- "sha1": "ba4fad2b99ea75608b384062b43c8fc5e5e312a1",
- "size": 2130
- },
- ".\/Resources\/views\/Menu\/user.html.twig": {
- "sha1": "5b5486856bfdc76c75d90c1f022339075a826d07",
- "size": 1313
- },
- ".\/Resources\/views\/Menu\/admin.html.twig": {
- "sha1": "293f16f50c858250f7faf5e1f4b7383fbfd0313e",
- "size": 926
- },
- ".\/Resources\/views\/Login\/login.html.twig": {
- "sha1": "d434b72455c5a7ec65ba38c1525d444abe4e3111",
- "size": 3795
- },
- ".\/Resources\/views\/layout.html.twig": {
- "sha1": "a21a3d6c7bbd34eb70e168c9e1d59977738128ec",
- "size": 6949
- },
- ".\/Resources\/views\/layoutWithVerticalMenu.html.twig": {
- "sha1": "2c17bc75ba28c12bad5d9898395d9d50d0064d90",
- "size": 2318
- },
- ".\/Resources\/views\/Form\/fields.html.twig": {
- "sha1": "a7e1b0266090f36b1003a13be980abc5bb4dd164",
- "size": 4289
- },
- ".\/Resources\/views\/Search\/error.html.twig": {
- "sha1": "a7739a05fff643d3112c1fe317a463fdda169c80",
- "size": 950
- },
- ".\/Resources\/views\/Search\/list.html.twig": {
- "sha1": "40d6aa4a381e5ea8adc9b0f416a824e3f5c565c0",
- "size": 1193
- },
- ".\/Resources\/doc\/index.rst": {
- "sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
- "size": 0
- },
- ".\/Doctrine\/DQL\/Unaccent.php": {
- "sha1": "89c0c4ffd8be3e50e710d4fd2cf54106d26cd77e",
- "size": 1676
- },
- ".\/composer.json": {
- "sha1": "95e356d37e6d570d1c806c117efd1291f77a2431",
- "size": 1927
- },
- ".\/.travis.yml": {
- "sha1": "bec25fa4634f0b3236747a2147a9db6e468f1e6d",
- "size": 678
- },
- ".\/Security\/Authorization\/ChillVoterInterface.php": {
- "sha1": "6ff2482f1a6812dc3e38d5995a0219df90d57bf9",
- "size": 989
- },
- ".\/Security\/Authorization\/AbstractChillVoter.php": {
- "sha1": "0c10abdec22d7c8a3dcddf897a1c293248d7ba35",
- "size": 1173
- },
- ".\/Security\/Authorization\/AuthorizationHelper.php": {
- "sha1": "3144fd33eb06ad95d1fa3f03f4d29fecb761498e",
- "size": 7420
- },
- ".\/Form\/Type\/Select2EntityType.php": {
- "sha1": "3fa560881c2d9df862b0d2d04a974ae8906cfbdd",
- "size": 1481
- },
- ".\/Form\/Type\/Select2ChoiceType.php": {
- "sha1": "24f38d9cbbce8e9fd848d33ad53ec62605582551",
- "size": 1458
- },
- ".\/Form\/Type\/AppendScopeChoiceTypeTrait.php": {
- "sha1": "91e2d1e0982e9a3c49c7a1c36711935c465b5250",
- "size": 5482
- },
- ".\/Form\/Type\/CenterType.php": {
- "sha1": "6b6c21a22de0b399ce6677df8dc784e2eefed36a",
- "size": 4031
- },
- ".\/Form\/Type\/Select2CountryType.php": {
- "sha1": "2684d96d1b88cda7d21189adcff39fcc561ee81d",
- "size": 2702
- },
- ".\/Form\/Type\/Select2LanguageType.php": {
- "sha1": "b1ae27e5729e3dbb2eaf831f0ef9168c13b7e8f7",
- "size": 2633
- },
- ".\/Form\/Type\/DataTransformer\/MultipleObjectsToIdTransformer.php": {
- "sha1": "8de5f538238963c1fc5ff5b8648c8b9cac2880aa",
- "size": 2183
- },
- ".\/Form\/Type\/DataTransformer\/ObjectToIdTransformer.php": {
- "sha1": "80eec7d2b088f536b7b1370343fae73a8f255485",
- "size": 2173
- },
- ".\/Form\/Type\/DataTransformer\/CenterTransformer.php": {
- "sha1": "3538c5c5b08e77970b66030440c434116bfc438f",
- "size": 1919
- },
- ".\/Form\/Type\/DataTransformer\/ScopeTransformer.php": {
- "sha1": "661d11f5fbcc7b614ca22a1f43fa2e46e34b89bb",
- "size": 2027
- },
- ".\/Form\/Type\/TranslatableStringFormType.php": {
- "sha1": "5df61ec76f0b0ef39b7b5b5f577da105f0e0bcb2",
- "size": 1020
- },
- ".\/DataFixtures\/ORM\/LoadGroupCenters.php": {
- "sha1": "8628bfb536f74b0fad1dba1c3515d97c558cf865",
- "size": 2245
- },
- ".\/DataFixtures\/ORM\/LoadLanguages.php": {
- "sha1": "8f2d2fcbdbaf1e2888f3c22817a286550ff25303",
- "size": 2430
- },
- ".\/DataFixtures\/ORM\/LoadCountries.php": {
- "sha1": "55fd885f28c5823e746056f6d622f084470b428d",
- "size": 1268
- },
- ".\/DataFixtures\/ORM\/LoadScopes.php": {
- "sha1": "6ec608f789be3a4cbbb0d6f7b73025c5b0b00981",
- "size": 2368
- },
- ".\/DataFixtures\/ORM\/LoadUsers.php": {
- "sha1": "b136ac4cc4040ad985a5684625470c62d01c0a84",
- "size": 2975
- },
- ".\/DataFixtures\/ORM\/LoadPermissionsGroup.php": {
- "sha1": "263800715c73fb22da72e9578708fc1843d6783e",
- "size": 2949
- },
- ".\/DataFixtures\/ORM\/LoadRoleScopes.php": {
- "sha1": "194543fa158a68439177fc09d71a0e14283ab094",
- "size": 2854
- },
- ".\/DataFixtures\/ORM\/LoadCenters.php": {
- "sha1": "acd39400134a3814d29d51bef62e70e3008e2306",
- "size": 2089
- },
- ".\/phpunit.xml.dist": {
- "sha1": "073c6b6608e34f5de0b3165ad18c5bd2acbaa879",
- "size": 723
- },
- ".\/Search\/SearchInterface.php": {
- "sha1": "270aa309b1143d6a35bf3e06674f4b19ad4d7805",
- "size": 2371
- },
- ".\/Search\/AbstractSearch.php": {
- "sha1": "b5bb068a880f485f41e66829e6de063c79735943",
- "size": 3021
- },
- ".\/Search\/ParsingException.php": {
- "sha1": "35e20a94a7e64a5efa646bc394b8d5fd72182d56",
- "size": 87
- },
- ".\/Search\/UnknowSearchNameException.php": {
- "sha1": "d82308b63873652255a1c049cd8f8bcfa84b043d",
- "size": 1290
- },
- ".\/Search\/UnknowSearchDomainException.php": {
- "sha1": "e750bdc3b3541b9b2f9d1390b0c092e26ca7e5b2",
- "size": 1291
- },
- ".\/Search\/SearchProvider.php": {
- "sha1": "67e0441aec06f7024b2a84a11ec83294390d312a",
- "size": 21810
- }
- }
-}
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v2
-
-iQIcBAEBCAAGBQJV9s7dAAoJEL+8y7VSV380AUwP/i2VyuMPI84/poU5ZuKoj4CC
-/2z1xHA/KAgBGGHGTWTmOBN1aAJQL0eShnkZI8aS1+fTPcyHty1BesLLy7+//f+s
-XykwAOFRdopS9lgFseGr8RHahP4pC706lP33PdAxvshacyuwjjHhHE2NpTU8YHjL
-d9yrh6PlL+egAcs4r4toDS018doINUEYcG4aAhAVIQS+1kUW+MA4EChoHrIDQ2+9
-WpoKdft+/gzdOGpgrXkU1klUxVQxl5U1Xa4QVeTOh2sjfE1bRNJmI/anbZliCL6x
-FT5Ztxwsv5wDi8gjJPhNfAlYmsF9HkFO4pek4ePkTXB6QHVDBTnwhTgx3M9P2cxP
-Dh637dftay0Prymkp+iy0VmnLMQ8kDdOuxB8T3+ahW6gHBxWRMkMMHd9pKfDZ1yT
-1Jz66F5q49RhpXUrJmGm5ycWz3PXNZipsiLC3tCSSNFKKBqB7wWV484XU+IHK2Fe
-iyT4zc6M9oFq5i/g0r7Q6V4IGBwG635XRZBGu++iHXBUXIFcFK10UfiXQddFIpYm
-uvAe3hzgkDAsUi1Bm4v496LsuUckBeXd2Wo3p3Rp7r0Z76Ru/WCDWWtuX+D9w54g
-7E8yYvYsifbjlzd51f7VfnPpCw0CQeEOLNUASoYCAoG/GgazrObvwvdzi669yx3T
-GySTC4dykgb5uT+VE2V9
-=xHiY
------END PGP SIGNATURE-----
diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml
index adf38988e..dd2407d2b 100644
--- a/src/Bundle/ChillMainBundle/config/services.yaml
+++ b/src/Bundle/ChillMainBundle/config/services.yaml
@@ -26,6 +26,20 @@ services:
tags:
- { name: 'doctrine.event_subscriber' }
+ # workflow related
+ Chill\MainBundle\Workflow\:
+ resource: '../Workflow/'
+ autowire: true
+ autoconfigure: true
+
+ Chill\MainBundle\Workflow\EntityWorkflowManager:
+ autoconfigure: true
+ autowire: true
+ arguments:
+ $handlers: !tagged_iterator chill_main.workflow_handler
+
+ # other stuffes
+
chill.main.helper.translatable_string:
class: Chill\MainBundle\Templating\TranslatableStringHelper
diff --git a/src/Bundle/ChillMainBundle/config/services/form.yaml b/src/Bundle/ChillMainBundle/config/services/form.yaml
index 0d0a17201..7efc9a69f 100644
--- a/src/Bundle/ChillMainBundle/config/services/form.yaml
+++ b/src/Bundle/ChillMainBundle/config/services/form.yaml
@@ -141,3 +141,5 @@ services:
autoconfigure: true
Chill\MainBundle\Form\Type\LocationFormType: ~
+
+ Chill\MainBundle\Form\WorkflowStepType: ~
diff --git a/src/Bundle/ChillMainBundle/config/services/mailer.yaml b/src/Bundle/ChillMainBundle/config/services/mailer.yaml
new file mode 100644
index 000000000..d7305f4b1
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/config/services/mailer.yaml
@@ -0,0 +1,10 @@
+services:
+ _defaults:
+ autowire: true
+ autoconfigure: true
+
+ Chill\MainBundle\Service\Mailer\:
+ resource: '../../Service/Mailer'
+
+ Chill\MainBundle\Service\Mailer\ChillMailer:
+ decorates: Symfony\Component\Mailer\MailerInterface
diff --git a/src/Bundle/ChillMainBundle/config/services/security.yaml b/src/Bundle/ChillMainBundle/config/services/security.yaml
index 30eed05c0..b884a92fc 100644
--- a/src/Bundle/ChillMainBundle/config/services/security.yaml
+++ b/src/Bundle/ChillMainBundle/config/services/security.yaml
@@ -26,6 +26,8 @@ services:
Chill\MainBundle\Security\Authorization\NotificationVoter: ~
+ Chill\MainBundle\Security\Authorization\EntityWorkflowVoter: ~
+
Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface: '@Chill\MainBundle\Security\Authorization\DefaultVoterHelperFactory'
chill.main.security.authorization.helper:
diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220112123436.php b/src/Bundle/ChillMainBundle/migrations/Version20220112123436.php
new file mode 100644
index 000000000..7358a3b4b
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/migrations/Version20220112123436.php
@@ -0,0 +1,72 @@
+addSql('ALTER TABLE chill_main_workflow_entity_subscriber_to_final DROP CONSTRAINT FK_C2CE504C7D99CE94');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_subscriber_to_step DROP CONSTRAINT FK_ECB8F5417D99CE94');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_step DROP CONSTRAINT FK_440AA6FEFB054143');
+ $this->addSql('ALTER TABLE chill_main_entity_workflow_step_user DROP CONSTRAINT FK_A9F001FA7E6AF9D4');
+ $this->addSql('DROP SEQUENCE chill_main_workflow_entity_id_seq CASCADE');
+ $this->addSql('DROP SEQUENCE chill_main_workflow_entity_step_id_seq CASCADE');
+ $this->addSql('DROP TABLE chill_main_workflow_entity');
+ $this->addSql('DROP TABLE chill_main_workflow_entity_subscriber_to_final');
+ $this->addSql('DROP TABLE chill_main_workflow_entity_subscriber_to_step');
+ $this->addSql('DROP TABLE chill_main_workflow_entity_step');
+ $this->addSql('DROP TABLE chill_main_entity_workflow_step_user');
+ }
+
+ public function getDescription(): string
+ {
+ return 'Create tables for workflow';
+ }
+
+ public function up(Schema $schema): void
+ {
+ $this->addSql('CREATE SEQUENCE chill_main_workflow_entity_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
+ $this->addSql('CREATE SEQUENCE chill_main_workflow_entity_step_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
+ $this->addSql('CREATE TABLE chill_main_workflow_entity (id INT NOT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, relatedEntityClass VARCHAR(255) NOT NULL, relatedEntityId INT NOT NULL, updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, workflowName TEXT NOT NULL, createdBy_id INT DEFAULT NULL, updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))');
+ $this->addSql('CREATE INDEX IDX_5F087D553174800F ON chill_main_workflow_entity (createdBy_id)');
+ $this->addSql('CREATE INDEX IDX_5F087D5565FF1AEC ON chill_main_workflow_entity (updatedBy_id)');
+ $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity.createdAt IS \'(DC2Type:datetime_immutable)\'');
+ $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity.updatedAt IS \'(DC2Type:datetime_immutable)\'');
+ $this->addSql('CREATE TABLE chill_main_workflow_entity_subscriber_to_final (entityworkflow_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(entityworkflow_id, user_id))');
+ $this->addSql('CREATE INDEX IDX_C2CE504C7D99CE94 ON chill_main_workflow_entity_subscriber_to_final (entityworkflow_id)');
+ $this->addSql('CREATE INDEX IDX_C2CE504CA76ED395 ON chill_main_workflow_entity_subscriber_to_final (user_id)');
+ $this->addSql('CREATE TABLE chill_main_workflow_entity_subscriber_to_step (entityworkflow_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(entityworkflow_id, user_id))');
+ $this->addSql('CREATE INDEX IDX_ECB8F5417D99CE94 ON chill_main_workflow_entity_subscriber_to_step (entityworkflow_id)');
+ $this->addSql('CREATE INDEX IDX_ECB8F541A76ED395 ON chill_main_workflow_entity_subscriber_to_step (user_id)');
+ $this->addSql('CREATE TABLE chill_main_workflow_entity_step (id INT NOT NULL, currentStep TEXT NOT NULL, destEmail JSON NOT NULL, finalizeAfter BOOLEAN DEFAULT \'false\' NOT NULL, freezeAfter BOOLEAN DEFAULT \'false\' NOT NULL, transitionAfter TEXT DEFAULT NULL, transitionByEmail TEXT DEFAULT NULL, transitionAt TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, entityWorkflow_id INT DEFAULT NULL, transitionBy_id INT DEFAULT NULL, PRIMARY KEY(id))');
+ $this->addSql('CREATE INDEX IDX_440AA6FEFB054143 ON chill_main_workflow_entity_step (entityWorkflow_id)');
+ $this->addSql('CREATE INDEX IDX_440AA6FE8829EF37 ON chill_main_workflow_entity_step (transitionBy_id)');
+ $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_step.transitionAt IS \'(DC2Type:datetime_immutable)\'');
+ $this->addSql('CREATE TABLE chill_main_entity_workflow_step_user (entityworkflowstep_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(entityworkflowstep_id, user_id))');
+ $this->addSql('CREATE INDEX IDX_A9F001FA7E6AF9D4 ON chill_main_entity_workflow_step_user (entityworkflowstep_id)');
+ $this->addSql('CREATE INDEX IDX_A9F001FAA76ED395 ON chill_main_entity_workflow_step_user (user_id)');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity ADD CONSTRAINT FK_5F087D553174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity ADD CONSTRAINT FK_5F087D5565FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_subscriber_to_final ADD CONSTRAINT FK_C2CE504C7D99CE94 FOREIGN KEY (entityworkflow_id) REFERENCES chill_main_workflow_entity (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_subscriber_to_final ADD CONSTRAINT FK_C2CE504CA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_subscriber_to_step ADD CONSTRAINT FK_ECB8F5417D99CE94 FOREIGN KEY (entityworkflow_id) REFERENCES chill_main_workflow_entity (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_subscriber_to_step ADD CONSTRAINT FK_ECB8F541A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_step ADD CONSTRAINT FK_440AA6FEFB054143 FOREIGN KEY (entityWorkflow_id) REFERENCES chill_main_workflow_entity (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_step ADD CONSTRAINT FK_440AA6FE8829EF37 FOREIGN KEY (transitionBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_entity_workflow_step_user ADD CONSTRAINT FK_A9F001FA7E6AF9D4 FOREIGN KEY (entityworkflowstep_id) REFERENCES chill_main_workflow_entity_step (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_entity_workflow_step_user ADD CONSTRAINT FK_A9F001FAA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220114132105.php b/src/Bundle/ChillMainBundle/migrations/Version20220114132105.php
new file mode 100644
index 000000000..2e7d7a99c
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/migrations/Version20220114132105.php
@@ -0,0 +1,47 @@
+addSql('DROP SEQUENCE chill_main_workflow_entity_comment_id_seq CASCADE');
+ $this->addSql('DROP TABLE chill_main_workflow_entity_comment');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_step DROP comment');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_step_user RENAME TO chill_main_entity_workflow_step_user');
+ }
+
+ public function getDescription(): string
+ {
+ return 'Add comment to entity workflow';
+ }
+
+ public function up(Schema $schema): void
+ {
+ $this->addSql('CREATE SEQUENCE chill_main_workflow_entity_comment_id_seq INCREMENT BY 1 MINVALUE 1 START 1;');
+ $this->addSql('CREATE TABLE chill_main_workflow_entity_comment (id INT NOT NULL, comment TEXT NOT NULL DEFAULT \'\', createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL, entityWorkflow_id INT DEFAULT NULL, updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))');
+ $this->addSql('CREATE INDEX IDX_2655252F3174800F ON chill_main_workflow_entity_comment (createdBy_id)');
+ $this->addSql('CREATE INDEX IDX_2655252FFB054143 ON chill_main_workflow_entity_comment (entityWorkflow_id)');
+ $this->addSql('CREATE INDEX IDX_2655252F65FF1AEC ON chill_main_workflow_entity_comment (updatedBy_id)');
+ $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_comment.createdAt IS \'(DC2Type:datetime_immutable)\'');
+ $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_comment.updatedAt IS \'(DC2Type:datetime_immutable)\'');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_comment ADD CONSTRAINT FK_2655252F3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_comment ADD CONSTRAINT FK_2655252FFB054143 FOREIGN KEY (entityWorkflow_id) REFERENCES chill_main_workflow_entity (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_comment ADD CONSTRAINT FK_2655252F65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_step ADD comment TEXT NOT NULL DEFAULT \'\'');
+ $this->addSql('ALTER TABLE chill_main_entity_workflow_step_user RENAME TO chill_main_workflow_entity_step_user');
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220114165950.php b/src/Bundle/ChillMainBundle/migrations/Version20220114165950.php
new file mode 100644
index 000000000..5489d3f1c
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/migrations/Version20220114165950.php
@@ -0,0 +1,37 @@
+addSql('ALTER TABLE chill_main_workflow_entity_step ALTER transitionat SET NOT NULL');
+ }
+
+ public function getDescription(): string
+ {
+ return 'remove not null on transition at';
+ }
+
+ public function up(Schema $schema): void
+ {
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_step ALTER transitionat DROP NOT NULL');
+ $this->addSql('ALTER TABLE chill_main_workflow_entity_step ALTER transitionat SET DEFAULT NULL');
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220120155303.php b/src/Bundle/ChillMainBundle/migrations/Version20220120155303.php
new file mode 100644
index 000000000..617d24b27
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/migrations/Version20220120155303.php
@@ -0,0 +1,33 @@
+addSql('DROP INDEX chill_main_notification_related_entity_idx');
+ }
+
+ public function getDescription(): string
+ {
+ return 'Create index for counting notifications';
+ }
+
+ public function up(Schema $schema): void
+ {
+ $this->addSql('CREATE INDEX chill_main_notification_related_entity_idx ON chill_main_notification (relatedentityclass, relatedentityid DESC)');
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml
index 4f25b46da..7a5b328f4 100644
--- a/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml
+++ b/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml
@@ -13,3 +13,19 @@ notification:
few {# notifications}
other {# notifications}
}
+
+ counter total notifications: >-
+ {total, plural,
+ =0 {Aucune notification}
+ one {# notification}
+ few {# notifications}
+ other {# notifications}
+ }
+
+ counter unread notifications: >-
+ {unread, plural,
+ =0 {Aucune non-lue}
+ one {# non-lue}
+ few {# non-lues}
+ other {# non-lues}
+ }
diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml
index af0dc77d7..5c214367c 100644
--- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml
+++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml
@@ -36,8 +36,9 @@ Choose an user: Choisir un utilisateur
"You are going to leave a page with unsubmitted data. Are you sure you want to leave ?": "Vous allez quitter la page alors que des données n'ont pas été enregistrées. Êtes vous sûr de vouloir partir ?"
No value: Aucune information
Last updated by: Dernière mise à jour par
-Last updated on: Dernière mise à jour le
on: "le "
+Last updated on: Dernière mise à jour le
+by_user: "par "
Edit: Modifier
Update: Mettre à jour
@@ -58,6 +59,7 @@ comment: commentaire
Comment: Commentaire
Pinned comment: Commentaire épinglé
Any comment: Aucun commentaire
+Read more: Lire la suite
# comment embeddable
No comment associated: Aucun commentaire
@@ -353,6 +355,43 @@ For: Pour
Created for: Créé pour
Created by: Créé par
+
+# Workflows 💊
+Workflow: Workflow — chemin de décision
+Workflow n°%id%: 'Workflow (n°%id%)'
+workflow_: Workflow
+target: ' (cible)'
+Decision: Décision
+Join a comment: Laisser un commentaire
+Follow workflow: Suivre la décision
+Workflow history: Historique de la décision
+
+workflow:
+ Created by: Créé par
+ Transition: Prochaine étape
+ dest for next steps: Utilisateurs qui valideront la prochaine étape
+ Freeze: Geler
+ The associated element will be freezed: L'élément associé sera gelé et ne pourra plus être modifié après cette décision.
+ Finalize: Étape finale
+ The workflow will be finalized: Le suivi est clôturé lors de cette décision.
+ No transitions: Aucune transition
+ Comment added: Commentaire ajouté
+ This workflow is finalized: Ce suivi est finalisé.
+ You are not allowed to apply a transition on this workflow: Vous n'êtes pas autorisé à appliquer une décision pour ce suivi
+ Only those users are allowed: Seuls ces utilisateurs sont autorisés
+ My workflows: Mes workflows
+ No workflow: Aucun workflow
+ Evaluation (n°%eval%): "Évaluation (n°%eval%)"
+ Document (n°%doc%): "Document (n°%doc%)"
+ Work (n°%w%): "Action d'accompagnement (n°%w%)"
+ subscribed: Souscrit
+ dest: Destinataire de l'étape finale
+ you subscribed to all steps: Vous recevrez une notification à chaque étape
+ you subscribed to final step: Vous recevrez une notification à l'étape finale
+
+Subscribe final: Recevoir une notification à l'étape finale
+Subscribe all steps: Recevoir une notification à chaque étape
+
notification:
Notification: Notification
My own notifications: Mes notifications
diff --git a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Workflow/WorkflowEventSubscriber.php b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Events/UserRefEventSubscriber.php
similarity index 53%
rename from src/Bundle/ChillPersonBundle/AccompanyingPeriod/Workflow/WorkflowEventSubscriber.php
rename to src/Bundle/ChillPersonBundle/AccompanyingPeriod/Events/UserRefEventSubscriber.php
index 3b7626903..590874ab4 100644
--- a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Workflow/WorkflowEventSubscriber.php
+++ b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Events/UserRefEventSubscriber.php
@@ -9,19 +9,20 @@
declare(strict_types=1);
-namespace Chill\PersonBundle\AccompanyingPeriod\Workflow;
+namespace Chill\PersonBundle\AccompanyingPeriod\Events;
use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Workflow\Event\EnteredEvent;
use Symfony\Contracts\Translation\TranslatorInterface;
-class WorkflowEventSubscriber implements EventSubscriberInterface
+class UserRefEventSubscriber implements EventSubscriberInterface
{
private EntityManagerInterface $em;
@@ -55,23 +56,46 @@ class WorkflowEventSubscriber implements EventSubscriberInterface
}
}
+ public function postUpdate(AccompanyingPeriod $period, LifecycleEventArgs $args): void
+ {
+ if ($period->hasPreviousUser()
+ && $period->getUser() !== $this->security->getUser()
+ && $period->getStep() !== AccompanyingPeriod::STEP_DRAFT
+ ) {
+ $this->generateNotificationToUser($period);
+ }
+
+ // we are just out of a flush operation. Launch a new one
+ $this->em->flush();
+ }
+
+ private function generateNotificationToUser(AccompanyingPeriod $period)
+ {
+ $notification = new Notification();
+
+ $urgentStatement =
+ $period->isEmergency() ? strtoupper($this->translator->trans('accompanying_period.emergency')) . ' ' : '';
+
+ $notification
+ ->setRelatedEntityId($period->getId())
+ ->setRelatedEntityClass(AccompanyingPeriod::class)
+ ->setTitle($urgentStatement . $this->translator->trans('period_notification.period_designated_subject'))
+ ->setMessage($this->engine->render(
+ '@ChillPerson/Notification/accompanying_course_designation.md.twig',
+ [
+ 'accompanyingCourse' => $period,
+ ]
+ ))
+ ->addAddressee($period->getUser());
+
+ $this->em->persist($notification);
+ }
+
private function onPeriodConfirmed(AccompanyingPeriod $period)
{
if ($period->getUser() instanceof User
&& $period->getUser() !== $this->security->getUser()) {
- $notification = new Notification();
- $notification
- ->setRelatedEntityId($period->getId())
- ->setRelatedEntityClass(AccompanyingPeriod::class)
- ->setTitle($this->translator->trans('period_notification.period_designated_subject'))
- ->setMessage($this->engine->render(
- '@ChillPerson/Notification/accompanying_course_designation.md.twig',
- [
- 'accompanyingCourse' => $period,
- ]
- ))
- ->addAddressee($period->getUser());
- $this->em->persist($notification);
+ $this->generateNotificationToUser($period);
}
}
}
diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php
index 9abd85f64..7ae5c5f73 100644
--- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php
+++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php
@@ -275,4 +275,34 @@ class AccompanyingCourseController extends Controller
'accompanying_period_id' => $period->getId(),
]);
}
+
+ /**
+ * @Route("/{_locale}/parcours/{accompanying_period_id}/open", name="chill_person_accompanying_course_reopen")
+ * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
+ */
+ public function reOpenAction(AccompanyingPeriod $accompanyingCourse, Request $request): Response
+ {
+ $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingCourse);
+
+ if (null === $accompanyingCourse) {
+ throw $this->createNotFoundException('period not found');
+ }
+
+ $form = $this->createFormBuilder([])->getForm();
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $accompanyingCourse->reOpen();
+ $this->getDoctrine()->getManager()->flush();
+
+ return $this->redirectToRoute('chill_person_accompanying_course_index', [
+ 'accompanying_period_id' => $accompanyingCourse->getId(),
+ ]);
+ }
+
+ return $this->render('@ChillPerson/AccompanyingCourse/re_open.html.twig', [
+ 'form' => $form->createView(),
+ 'accompanyingCourse' => $accompanyingCourse,
+ ]);
+ }
}
diff --git a/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionController.php b/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionController.php
new file mode 100644
index 000000000..c74113a05
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionController.php
@@ -0,0 +1,149 @@
+security = $security;
+ $this->householdCompositionRepository = $householdCompositionRepository;
+ $this->paginatorFactory = $paginatorFactory;
+ $this->formFactory = $formFactory;
+ $this->entityManager = $entityManager;
+ $this->translator = $translator;
+ $this->engine = $engine;
+ $this->urlGenerator = $urlGenerator;
+ }
+
+ /**
+ * @Route("/{_locale}/person/household/{id}/composition/index", name="chill_person_household_composition_index")
+ */
+ public function index(Household $household, Request $request): Response
+ {
+ if (!$this->security->isGranted(HouseholdVoter::SEE, $household)) {
+ throw new AccessDeniedException('not allowed to edit an household');
+ }
+
+ $count = $this->householdCompositionRepository->countByHousehold($household);
+ $paginator = $this->paginatorFactory->create($count);
+ $compositions = $this->householdCompositionRepository->findByHousehold(
+ $household,
+ ['startDate' => 'DESC', 'id' => 'DESC'],
+ $paginator->getItemsPerPage(),
+ $paginator->getCurrentPageFirstItemNumber()
+ );
+
+ if ($this->security->isGranted(HouseholdVoter::EDIT, $household)) {
+ $isEdit = $request->query->has('edit');
+
+ if ($isEdit) {
+ $householdCompositions = $household->getCompositions()->filter(static function (HouseholdComposition $composition) use ($request) {
+ return $composition->getId() === $request->query->getInt('edit');
+ });
+
+ if ($householdCompositions->count() !== 1) {
+ throw new BadRequestHttpException('could not find the composition with this id associated to the household');
+ }
+ $householdComposition = $householdCompositions->first();
+ } else {
+ $householdComposition = (new HouseholdComposition())
+ ->setStartDate(new DateTimeImmutable());
+ }
+ $form = $this->formFactory->create(HouseholdCompositionType::class, $householdComposition);
+
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ if (!$isEdit) {
+ $this->entityManager->persist($householdComposition);
+ $household->addComposition($householdComposition);
+ }
+
+ $this->entityManager->flush();
+
+ $request->getSession()->getFlashBag()->add(
+ 'success',
+ $this->translator->trans('household_composition.Composition added')
+ );
+
+ return new RedirectResponse(
+ $this->urlGenerator->generate('chill_person_household_composition_index', [
+ 'id' => $household->getId(),
+ ])
+ );
+ }
+
+ if ($form->isSubmitted() && !$form->isValid()) {
+ $request->getSession()->getFlashBag()->add(
+ 'warning',
+ $this->translator->trans('This form contains errors')
+ );
+ }
+ }
+
+ return new Response($this->engine->render(
+ '@ChillPerson/HouseholdComposition/index.html.twig',
+ [
+ 'household' => $household,
+ 'compositions' => $compositions,
+ 'form' => isset($form) ? $form->createView() : null,
+ 'isPosted' => isset($form) ? $form->isSubmitted() : false,
+ 'editId' => $request->query->getInt('edit', -1),
+ ]
+ ));
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionTypeApiController.php b/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionTypeApiController.php
new file mode 100644
index 000000000..418d04531
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionTypeApiController.php
@@ -0,0 +1,36 @@
+andWhere($query->expr()->eq('e.active', "'TRUE'"));
+
+ break;
+
+ default:
+ throw new UnexpectedValueException('unexepcted action: ' . $action);
+ }
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHouseholdCompositionType.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHouseholdCompositionType.php
new file mode 100644
index 000000000..b1d306ef8
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHouseholdCompositionType.php
@@ -0,0 +1,48 @@
+ 'Couple avec enfant(s)'],
+ ['fr' => 'Couple sans enfant'],
+ ['fr' => 'Mère seule'],
+ ['fr' => 'Père seul'],
+ ['fr' => 'Mère isolée'],
+ ['fr' => 'Père isolé'],
+ ['fr' => 'Homme seul'],
+ ['fr' => 'Femme seule'],
+ ];
+
+ public static function getGroups(): array
+ {
+ return ['composition-type'];
+ }
+
+ public function load(ObjectManager $manager)
+ {
+ foreach (self::TYPES as $type) {
+ $manager->persist(
+ (new HouseholdCompositionType())
+ ->setLabel($type)
+ );
+ }
+
+ $manager->flush();
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
index 5982fcebe..01df460e3 100644
--- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
+++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
@@ -13,6 +13,7 @@ namespace Chill\PersonBundle\DependencyInjection;
use Chill\MainBundle\DependencyInjection\MissingBundleException;
use Chill\MainBundle\Security\Authorization\ChillExportVoter;
+use Chill\PersonBundle\Controller\HouseholdCompositionTypeApiController;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodResourceVoter;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
@@ -760,6 +761,21 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
],
],
],
+ [
+ 'class' => \Chill\PersonBundle\Entity\Household\HouseholdCompositionType::class,
+ 'name' => 'household_composition',
+ 'base_path' => '/api/1.0/person/houehold/composition/type',
+ 'base_role' => 'ROLE_USER',
+ 'controller' => HouseholdCompositionTypeApiController::class,
+ 'actions' => [
+ '_index' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true,
+ ],
+ ],
+ ],
+ ],
],
]);
}
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
index 06e4f0c8b..728ef7322 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
@@ -20,6 +20,7 @@ use Chill\MainBundle\Entity\HasScopesInterface;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
+use Chill\MainBundle\Entity\UserJob;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
@@ -44,7 +45,6 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\GroupSequenceProviderInterface;
use UnexpectedValueException;
-use function count;
use function in_array;
use const SORT_REGULAR;
@@ -198,6 +198,15 @@ class AccompanyingPeriod implements
*/
private $intensity = self::INTENSITY_OCCASIONAL;
+ /**
+ * @ORM\ManyToOne(
+ * targetEntity=UserJob::class
+ * )
+ * @Groups({"read", "write"})
+ * @Assert\NotBlank(groups={AccompanyingPeriod::STEP_CONFIRMED})
+ */
+ private ?UserJob $job = null;
+
/**
* @var DateTime
*
@@ -327,6 +336,13 @@ class AccompanyingPeriod implements
*/
private ?User $user = null;
+ /**
+ * Temporary field, which is filled when the user is changed.
+ *
+ * Used internally for listener when user change
+ */
+ private ?User $userPrevious = null;
+
/**
* @ORM\OneToMany(
* targetEntity=AccompanyingPeriodWork::class,
@@ -528,17 +544,17 @@ class AccompanyingPeriod implements
public function getAvailablePersonLocation(): Collection
{
return $this->getOpenParticipations()
- ->filter(static function (AccompanyingPeriodParticipation $p) {
- return $p->getPerson()->hasCurrentHouseholdAddress();
- })
- ->map(static function (AccompanyingPeriodParticipation $p) {
- return $p->getPerson();
- });
+ ->filter(
+ static fn (AccompanyingPeriodParticipation $p): bool => $p->getPerson()->hasCurrentHouseholdAddress()
+ )
+ ->map(
+ static fn (AccompanyingPeriodParticipation $p): ?Person => $p->getPerson()
+ );
}
public function getCenter(): ?Center
{
- if (count($this->getPersons()) === 0) {
+ if ($this->getPersons()->count() === 0) {
return null;
}
@@ -579,9 +595,13 @@ class AccompanyingPeriod implements
*/
public function getComments(): Collection
{
- return $this->comments->filter(function (Comment $c) {
- return $c !== $this->pinnedComment;
- });
+ $pinnedComment = $this->pinnedComment;
+
+ return $this
+ ->comments
+ ->filter(
+ static fn (Comment $c): bool => $c !== $pinnedComment
+ );
}
public function getCreatedAt(): ?DateTime
@@ -612,7 +632,7 @@ class AccompanyingPeriod implements
return [[self::STEP_DRAFT, self::STEP_CONFIRMED]];
}
- throw new LogicException('no validation group permitted with this step');
+ throw new LogicException('no validation group permitted with this step: ' . $this->getStep());
}
public function getId(): ?int
@@ -625,6 +645,11 @@ class AccompanyingPeriod implements
return $this->intensity;
}
+ public function getJob(): ?UserJob
+ {
+ return $this->job;
+ }
+
/**
* Get the location, taking precedence into account.
*
@@ -717,9 +742,7 @@ class AccompanyingPeriod implements
return $this
->getParticipations()
->filter(
- static function (AccompanyingPeriodParticipation $participation) use ($person): bool {
- return $participation->getPerson() === $person;
- }
+ static fn (AccompanyingPeriodParticipation $participation): bool => $participation->getPerson() === $person
);
}
@@ -741,9 +764,7 @@ class AccompanyingPeriod implements
return $this
->participations
->map(
- static function (AccompanyingPeriodParticipation $participation): Person {
- return $participation->getPerson();
- }
+ static fn (AccompanyingPeriodParticipation $participation): ?Person => $participation->getPerson()
);
}
@@ -755,6 +776,11 @@ class AccompanyingPeriod implements
return $this->pinnedComment;
}
+ public function getPreviousUser(): ?User
+ {
+ return $this->userPrevious;
+ }
+
/**
* @return Collection|SocialAction[] All the descendant social actions of all
* the descendants of the entity
@@ -868,6 +894,11 @@ class AccompanyingPeriod implements
return $this->works;
}
+ public function hasPreviousUser(): bool
+ {
+ return null !== $this->userPrevious;
+ }
+
/**
* Returns true if the closing date is after the opening date.
*/
@@ -981,6 +1012,7 @@ class AccompanyingPeriod implements
{
$this->setClosingDate(null);
$this->setClosingMotive(null);
+ $this->setStep(AccompanyingPeriod::STEP_CONFIRMED);
}
/**
@@ -1058,6 +1090,13 @@ class AccompanyingPeriod implements
return $this;
}
+ public function setJob(?UserJob $job): self
+ {
+ $this->job = $job;
+
+ return $this;
+ }
+
/**
* Set openingDate.
*
@@ -1099,6 +1138,9 @@ class AccompanyingPeriod implements
}
if ($comment instanceof Comment) {
+ if (null !== $this->pinnedComment) {
+ $this->addComment($this->pinnedComment);
+ }
$this->addComment($comment);
}
@@ -1172,6 +1214,10 @@ class AccompanyingPeriod implements
public function setUser(User $user): self
{
+ if ($this->user !== $user) {
+ $this->userPrevious = $this->user;
+ }
+
$this->user = $user;
return $this;
diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php
index 865526250..ef718bc2e 100644
--- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php
+++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php
@@ -11,6 +11,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Entity\Household;
+use ArrayIterator;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\PersonBundle\Validator\Constraints\Household\MaxHolder;
@@ -23,8 +24,8 @@ use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
-use Symfony\Component\Validator\Context\ExecutionContextInterface;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
use function count;
/**
@@ -56,6 +57,18 @@ class Household
*/
private CommentEmbeddable $commentMembers;
+ /**
+ * @ORM\OneToMany(
+ * targetEntity=HouseholdComposition::class,
+ * mappedBy="household",
+ * orphanRemoval=true,
+ * cascade={"persist"}
+ * )
+ * @ORM\OrderBy({"startDate": "DESC"})
+ * @Assert\Valid(traverse=true, groups={"household_composition"})
+ */
+ private Collection $compositions;
+
/**
* @ORM\Id
* @ORM\GeneratedValue
@@ -90,6 +103,7 @@ class Household
$this->addresses = new ArrayCollection();
$this->members = new ArrayCollection();
$this->commentMembers = new CommentEmbeddable();
+ $this->compositions = new ArrayCollection();
}
/**
@@ -108,6 +122,18 @@ class Household
return $this;
}
+ public function addComposition(HouseholdComposition $composition): self
+ {
+ if (!$this->compositions->contains($composition)) {
+ $composition->setHousehold($this);
+ $this->compositions[] = $composition;
+ }
+
+ $this->householdCompositionConsistency();
+
+ return $this;
+ }
+
public function addMember(HouseholdMember $member): self
{
if (!$this->members->contains($member)) {
@@ -136,6 +162,14 @@ class Household
return $this->commentMembers;
}
+ /**
+ * @return ArrayCollection|Collection|HouseholdComposition[]
+ */
+ public function getCompositions(): Collection
+ {
+ return $this->compositions;
+ }
+
/**
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\SerializedName("current_address")
@@ -157,6 +191,31 @@ class Household
return null;
}
+ public function getCurrentComposition(?DateTimeImmutable $at = null): ?HouseholdComposition
+ {
+ $at ??= new DateTimeImmutable('today');
+ $criteria = new Criteria();
+ $expr = Criteria::expr();
+
+ $criteria->where(
+ $expr->andX(
+ $expr->orX(
+ $expr->isNull('endDate'),
+ $expr->gt('endDate', $at)
+ ),
+ $expr->lte('startDate', $at)
+ )
+ );
+
+ $compositions = $this->compositions->matching($criteria);
+
+ if ($compositions->count() > 0) {
+ return $compositions->first();
+ }
+
+ return null;
+ }
+
/**
* @Serializer\Groups({"docgen:read"})
*/
@@ -369,11 +428,54 @@ class Household
return $this->waitingForBirthDate;
}
+ /**
+ * @internal
+ */
+ public function householdCompositionConsistency(): void
+ {
+ $compositionOrdered = $this->compositions->toArray();
+
+ usort(
+ $compositionOrdered,
+ static function (HouseholdComposition $a, HouseholdComposition $b) {
+ return $a->getStartDate() <=> $b->getStartDate();
+ }
+ );
+
+ $iterator = new ArrayIterator($compositionOrdered);
+ $iterator->rewind();
+
+ /** @var ?HouseholdComposition $previous */
+ $previous = null;
+
+ do {
+ /** @var ?HouseholdComposition $current */
+ $current = $iterator->current();
+
+ if (null !== $previous) {
+ if (null === $previous->getEndDate() || $previous->getEndDate() > $current->getStartDate()) {
+ $previous->setEndDate($current->getStartDate());
+ }
+ }
+ $previous = $current;
+ $iterator->next();
+ } while ($iterator->valid());
+ }
+
public function removeAddress(Address $address)
{
$this->addresses->removeElement($address);
}
+ public function removeComposition(HouseholdComposition $composition): self
+ {
+ if ($this->compositions->removeElement($composition)) {
+ $composition->setHousehold(null);
+ }
+
+ return $this;
+ }
+
public function removeMember(HouseholdMember $member): self
{
if ($this->members->removeElement($member)) {
diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdComposition.php b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdComposition.php
new file mode 100644
index 000000000..8aef3a0fa
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdComposition.php
@@ -0,0 +1,172 @@
+comment = new CommentEmbeddable();
+ }
+
+ public function getComment(): CommentEmbeddable
+ {
+ return $this->comment;
+ }
+
+ public function getEndDate(): ?DateTimeImmutable
+ {
+ return $this->endDate;
+ }
+
+ public function getHousehold(): ?Household
+ {
+ return $this->household;
+ }
+
+ public function getHouseholdCompositionType(): ?HouseholdCompositionType
+ {
+ return $this->householdCompositionType;
+ }
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getNumberOfChildren(): ?int
+ {
+ return $this->numberOfChildren;
+ }
+
+ public function getStartDate(): ?DateTimeImmutable
+ {
+ return $this->startDate;
+ }
+
+ public function setComment(CommentEmbeddable $comment): HouseholdComposition
+ {
+ $this->comment = $comment;
+
+ return $this;
+ }
+
+ public function setEndDate(?DateTimeImmutable $endDate): HouseholdComposition
+ {
+ $this->endDate = $endDate;
+
+ if (null !== $this->household) {
+ $this->household->householdCompositionConsistency();
+ }
+
+ return $this;
+ }
+
+ public function setHousehold(?Household $household): HouseholdComposition
+ {
+ $this->household = $household;
+
+ return $this;
+ }
+
+ public function setHouseholdCompositionType(?HouseholdCompositionType $householdCompositionType): HouseholdComposition
+ {
+ $this->householdCompositionType = $householdCompositionType;
+
+ return $this;
+ }
+
+ public function setNumberOfChildren(?int $numberOfChildren): HouseholdComposition
+ {
+ $this->numberOfChildren = $numberOfChildren;
+
+ return $this;
+ }
+
+ public function setStartDate(?DateTimeImmutable $startDate): HouseholdComposition
+ {
+ $this->startDate = $startDate;
+
+ if (null !== $this->household) {
+ $this->household->householdCompositionConsistency();
+ }
+
+ return $this;
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdCompositionType.php b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdCompositionType.php
new file mode 100644
index 000000000..03d1a401a
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdCompositionType.php
@@ -0,0 +1,76 @@
+id;
+ }
+
+ public function getLabel(): array
+ {
+ return $this->label;
+ }
+
+ public function isActive(): bool
+ {
+ return $this->active;
+ }
+
+ public function setActive(bool $active): HouseholdCompositionType
+ {
+ $this->active = $active;
+
+ return $this;
+ }
+
+ public function setLabel(array $label): HouseholdCompositionType
+ {
+ $this->label = $label;
+
+ return $this;
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php
index 91e430a2f..c588b70a3 100644
--- a/src/Bundle/ChillPersonBundle/Entity/Person.php
+++ b/src/Bundle/ChillPersonBundle/Entity/Person.php
@@ -687,7 +687,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
$result = new ArrayCollection();
if ($asParticipantOpen) {
- foreach ($this->getOpenedParticipations()
+ foreach ($this->getAccompanyingPeriodParticipations()
->map(fn (AccompanyingPeriodParticipation $app) => $app->getAccompanyingPeriod())
as $period
) {
@@ -1142,7 +1142,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
->where(
$expr->eq('shareHousehold', true)
)
- ->orderBy(['startDate' => Criteria::DESC]);
+ ->orderBy(['startDate' => Criteria::DESC, 'id' => Criteria::DESC]);
return $this->getHouseholdParticipations()
->matching($criteria);
diff --git a/src/Bundle/ChillPersonBundle/Form/HouseholdCompositionType.php b/src/Bundle/ChillPersonBundle/Form/HouseholdCompositionType.php
new file mode 100644
index 000000000..0348b0c39
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Form/HouseholdCompositionType.php
@@ -0,0 +1,60 @@
+householdCompositionTypeRepository = $householdCompositionTypeRepository;
+ $this->translatableStringHelper = $translatableStringHelper;
+ }
+
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $types = $this->householdCompositionTypeRepository->findAllActive();
+
+ $builder
+ ->add('householdCompositionType', EntityType::class, [
+ 'class' => \Chill\PersonBundle\Entity\Household\HouseholdCompositionType::class,
+ 'choices' => $types,
+ 'choice_label' => function (\Chill\PersonBundle\Entity\Household\HouseholdCompositionType $type) {
+ return $this->translatableStringHelper->localize($type->getLabel());
+ },
+ 'label' => 'household_composition.Household composition',
+ ])
+ ->add('startDate', ChillDateType::class, [
+ 'required' => true,
+ 'input' => 'datetime_immutable',
+ ])
+ ->add('numberOfChildren', IntegerType::class, [
+ 'required' => true,
+ 'label' => 'household_composition.numberOfChildren',
+ ])
+ ->add('comment', CommentType::class, [
+ 'required' => false,
+ ]);
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Household/MembersEditor.php b/src/Bundle/ChillPersonBundle/Household/MembersEditor.php
index 4d855a980..adc97a4ac 100644
--- a/src/Bundle/ChillPersonBundle/Household/MembersEditor.php
+++ b/src/Bundle/ChillPersonBundle/Household/MembersEditor.php
@@ -29,6 +29,8 @@ class MembersEditor
{
public const VALIDATION_GROUP_AFFECTED = 'household_memberships';
+ public const VALIDATION_GROUP_COMPOSITION = 'household_composition';
+
public const VALIDATION_GROUP_CREATED = 'household_memberships_created';
private ?Household $household = null;
@@ -77,6 +79,15 @@ class MembersEditor
$this->oldMembershipsHashes[] = spl_object_hash($participation);
}
}
+
+ foreach ($person->getHouseholdParticipationsNotShareHousehold() as $participation) {
+ if ($participation->getHousehold() === $this->household
+ && $participation->getEndDate() === null || $participation->getEndDate() > $membership->getStartDate()
+ && $participation->getStartDate() <= $membership->getStartDate()
+ ) {
+ $participation->setEndDate($membership->getStartDate());
+ }
+ }
}
$this->membershipsAffected[] = $membership;
@@ -129,7 +140,7 @@ class MembersEditor
{
if ($this->hasHousehold()) {
$list = $this->validator
- ->validate($this->getHousehold(), null, [self::VALIDATION_GROUP_AFFECTED]);
+ ->validate($this->getHousehold(), null, [self::VALIDATION_GROUP_AFFECTED, self::VALIDATION_GROUP_COMPOSITION]);
} else {
$list = new ConstraintViolationList();
}
diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php
index 60455b201..0efbf9ac3 100644
--- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php
+++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php
@@ -82,6 +82,15 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
$workflow = $this->registry->get($period, 'accompanying_period_lifecycle');
+ if (null !== $period->getClosingDate()) {
+ $menu->addChild($this->translator->trans('Re-open accompanying course'), [
+ 'route' => 'chill_person_accompanying_course_reopen',
+ 'routeParameters' => [
+ 'accompanying_period_id' => $period->getId(),
+ ], ])
+ ->setExtras(['order' => 99998]);
+ }
+
if ($workflow->can($period, 'close')) {
$menu->addChild($this->translator->trans('Close Accompanying Course'), [
'route' => 'chill_person_accompanying_course_close',
diff --git a/src/Bundle/ChillPersonBundle/Menu/HouseholdMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/HouseholdMenuBuilder.php
index a144bf65b..a4d3f8d03 100644
--- a/src/Bundle/ChillPersonBundle/Menu/HouseholdMenuBuilder.php
+++ b/src/Bundle/ChillPersonBundle/Menu/HouseholdMenuBuilder.php
@@ -29,6 +29,7 @@ class HouseholdMenuBuilder implements LocalMenuBuilderInterface
public function buildMenu($menuId, MenuItem $menu, array $parameters): void
{
+ /** @var \Chill\PersonBundle\Entity\Household\Household $household */
$household = $parameters['household'];
$menu->addChild($this->translator->trans('household.Household summary'), [
@@ -38,6 +39,20 @@ class HouseholdMenuBuilder implements LocalMenuBuilderInterface
], ])
->setExtras(['order' => 10]);
+ $menu->addChild($this->translator->trans('household.Relationship'), [
+ 'route' => 'chill_person_household_relationship',
+ 'routeParameters' => [
+ 'household_id' => $household->getId(),
+ ], ])
+ ->setExtras(['order' => 15]);
+
+ $menu->addChild($this->translator->trans('household_composition.Compositions'), [
+ 'route' => 'chill_person_household_composition_index',
+ 'routeParameters' => [
+ 'id' => $household->getId(),
+ ], ])
+ ->setExtras(['order' => 17]);
+
$menu->addChild($this->translator->trans('household.Accompanying period'), [
'route' => 'chill_person_household_accompanying_period',
'routeParameters' => [
@@ -51,13 +66,6 @@ class HouseholdMenuBuilder implements LocalMenuBuilderInterface
'household_id' => $household->getId(),
], ])
->setExtras(['order' => 30]);
-
- $menu->addChild($this->translator->trans('household.Relationship'), [
- 'route' => 'chill_person_household_relationship',
- 'routeParameters' => [
- 'household_id' => $household->getId(),
- ], ])
- ->setExtras(['order' => 15]);
}
public static function getMenuIds(): array
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationRepository.php
index ca1a01157..f8a2e7f33 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationRepository.php
@@ -40,7 +40,7 @@ class AccompanyingPeriodWorkEvaluationRepository implements ObjectRepository
}
/**
- * @return array|object[]|AccompanyingPeriodWorkEvaluation[]
+ * @return array|AccompanyingPeriodWorkEvaluation[]
*/
public function findAll(): array
{
@@ -50,8 +50,7 @@ class AccompanyingPeriodWorkEvaluationRepository implements ObjectRepository
/**
* @param int $limit
* @param int $offset
- *
- * @return array|AccompanyingPeriodWorkEvaluation[]|object[]
+ * @return array|AccompanyingPeriodWorkEvaluation[]
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
{
diff --git a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdACLAwareRepository.php b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdACLAwareRepository.php
index 0649dae1c..dc5e8f5d3 100644
--- a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdACLAwareRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdACLAwareRepository.php
@@ -39,7 +39,7 @@ final class HouseholdACLAwareRepository implements HouseholdACLAwareRepositoryIn
{
$centers = $this->authorizationHelper->getReachableCenters(
$this->security->getUser(),
- HouseholdVoter::SHOW
+ HouseholdVoter::SEE
);
if ([] === $centers) {
diff --git a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepository.php b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepository.php
new file mode 100644
index 000000000..23f79c7f5
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionRepository.php
@@ -0,0 +1,75 @@
+repository = $entityManager->getRepository(HouseholdComposition::class);
+ }
+
+ public function countByHousehold(Household $household): int
+ {
+ return $this->repository->count(['household' => $household]);
+ }
+
+ public function find($id): ?HouseholdComposition
+ {
+ return $this->repository->find($id);
+ }
+
+ /**
+ * @return array|HouseholdComposition[]
+ */
+ public function findAll(): array
+ {
+ return $this->repository->findAll();
+ }
+
+ /**
+ * @param int $limit
+ * @param int $offset
+ *
+ * @return array|object[]|HouseholdComposition[]
+ */
+ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
+ {
+ return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
+ }
+
+ /**
+ * @return array|HouseholdComposition[]|object[]
+ */
+ public function findByHousehold(Household $household, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
+ {
+ return $this->findBy(['household' => $household], $orderBy, $limit, $offset);
+ }
+
+ public function findOneBy(array $criteria): ?HouseholdComposition
+ {
+ return $this->repository->findOneBy($criteria);
+ }
+
+ public function getClassName(): string
+ {
+ return HouseholdComposition::class;
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionTypeRepository.php b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionTypeRepository.php
new file mode 100644
index 000000000..45931b4da
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdCompositionTypeRepository.php
@@ -0,0 +1,69 @@
+repository = $entityManager->getRepository(HouseholdCompositionType::class);
+ }
+
+ public function find($id): ?HouseholdCompositionType
+ {
+ return $this->repository->find($id);
+ }
+
+ /**
+ * @return array|HouseholdCompositionType[]|object[]
+ */
+ public function findAll(): array
+ {
+ return $this->repository->findAll();
+ }
+
+ /**
+ * @return array|HouseholdCompositionType[]
+ */
+ public function findAllActive(): array
+ {
+ return $this->findBy(['active' => true]);
+ }
+
+ /**
+ * @param $limit
+ * @param $offset
+ *
+ * @return array|HouseholdCompositionType[]|object[]
+ */
+ 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): ?HouseholdCompositionType
+ {
+ return $this->repository->findOneBy($criteria);
+ }
+
+ public function getClassName(): string
+ {
+ return HouseholdCompositionType::class;
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js
index 8e4134205..ab95229a2 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js
@@ -15,21 +15,15 @@ const getAccompanyingCourse = (id) => {
});
};
-const getUsers = () => {
- const url = `/api/1.0/main/user.json`;
-
- return fetchResults(url);
-};
+const getUsers = () => fetchResults('/api/1.0/main/user.json');
const getReferrersSuggested = (course) => {
const url = `/api/1.0/person/accompanying-course/${course.id}/referrers-suggested.json`;
-
return fetchResults(url);
}
-/*
-* Endpoint
-*/
+const getUserJobs = () => fetchResults('/api/1.0/main/user-job.json');
+
const getSocialIssues = () => {
const url = `/api/1.0/person/social-work/social-issue.json`;
return fetch(url)
@@ -54,4 +48,5 @@ export {
getAccompanyingCourse,
getUsers,
getReferrersSuggested,
+ getUserJobs
};
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue
index 201360a6e..9b3a6ade9 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue
@@ -19,6 +19,9 @@
:options="options"
group-values="locations"
group-label="locationCategories"
+ :select-label="$t('multiselect.select_label')"
+ :deselect-label="$t('multiselect.deselect_label')"
+ :selected-label="$t('multiselect.selected_label')"
@select="updateAdminLocation">
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue
index 8c4aa2910..faba287c5 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue
@@ -59,9 +59,35 @@
{{ $t('confirm.sure_description') }}
+
+
+
{{ $t('confirm.no_suggested_referrer') }}
+
+
+
{{ $t('confirm.one_suggested_referrer') }}:
+
+
{{ $t('confirm.choose_suggested_referrer') }}
+
+
+
+ {{ $t('confirm.choose_button') }}
+
+
+
+
+ {{ $t('confirm.do_not_choose_button') }}
+
+
+
+
+
-
+
{{ $t('confirm.ok') }}
@@ -74,11 +100,13 @@
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue
index c0ad9c9e7..fd856141e 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue
@@ -61,6 +61,32 @@
+
+
{{ $t('Documents') }} :
+
+
+
+
+
{{ d.template.name.fr }}
+
+
Créé par {{ d.createdBy.text }}
+ Le {{ $d(ISOToDatetime(d.createdAt.datetime), 'long') }}
+
+
+
+
+
+
+
+
{
let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id;
return Promise.resolve({entityId: evaluationId});
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js
index a4beb223e..21ec4b168 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js
@@ -205,6 +205,8 @@ const store = createStore({
warningInterval: null,
comment: "",
editEvaluation: true,
+ workflows_availables: state.work.workflows_availables_evaluation,
+ documents: [],
};
state.evaluationsPicked.push(e);
},
@@ -371,7 +373,8 @@ const store = createStore({
if (typeof(callback) !== 'undefined') {
return callback(data);
} else {
- console.info('nothing to do here, bye bye');window.location.assign(`/fr/person/accompanying-period/${state.work.accompanyingPeriod.id}/work`);
+ console.info('nothing to do here, bye bye');
+ window.location.assign(`/fr/person/accompanying-period/${state.work.accompanyingPeriod.id}/work`);
}
}).catch(error => {
console.log('error on submit', error);
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/App.vue
index 22b4f54a6..52047c630 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/App.vue
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/App.vue
@@ -32,7 +32,7 @@
-
+
{{ $t('household_members_editor.app.save') }}
@@ -104,6 +104,13 @@ export default {
return false;
},
+ lastStepIsSaveAllowed() {
+ let r = !this.$store.getters.isHouseholdNew ||
+ (this.$store.state.numberOfChildren !== null && this.$store.state.householdCompositionType !== null);
+ console.log('is saved allowed ?', r);
+
+ return r;
+ },
},
methods: {
goToNext() {
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Concerned.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Concerned.vue
index 3be8822be..74271954f 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Concerned.vue
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Concerned.vue
@@ -7,18 +7,14 @@
-
- {{ $t('household_members_editor.concerned.persons_will_be_moved') }} :
-
-
-
-
-
-
-
-
-
-
+
{{ $t('household_members_editor.concerned.persons_will_be_moved') }} :
+
+
+
+ {{ c.person.text }}
+
+
+
{{ $t('household_members_editor.concerned.persons_with_household') }}
@@ -108,9 +104,14 @@ export default {
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
},
- removePerson(person) {
- console.log('remove person in concerned', person);
- this.$store.dispatch('removePerson', person);
+ removeConcerned(concerned) {
+ console.log('removedconcerned', concerned);
+
+ if (!concerned.allowRemove) {
+ return;
+ }
+
+ this.$store.dispatch('removePerson', concerned.person);
},
makeHouseholdLink(id) {
return `/fr/person/household/${id}/summary`
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Dates.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Dates.vue
index c40cc9cbc..94c1a146a 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Dates.vue
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Dates.vue
@@ -4,17 +4,38 @@
{{ $t('household_members_editor.dates.dates_title') }}
-
-
+
+
+
+
{{ $t('household_members_editor.composition.composition') }}
+
+
{{ $t('household_members_editor.composition.household_composition') }}
+
+
+ {{ t.label.fr }}
+
+
+
+
+
{{ $t('household_members_editor.composition.number_of_children') }}
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Positioning.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Positioning.vue
index 8e033234c..487fb230e 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Positioning.vue
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Positioning.vue
@@ -3,15 +3,15 @@
{{ $t('household_members_editor.positioning.persons_to_positionnate')}}
-
+
-
+
-
+
{{ conc.person.text }}
+
+
+
{{ $t('household_members_editor.positioning.comment') }}
+
+
+
@@ -46,12 +52,14 @@ import MemberDetails from './MemberDetails.vue';
import {mapGetters, mapState} from "vuex";
import CurrentHousehold from "./CurrentHousehold";
import PersonRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue';
+import PersonComment from './PersonComment';
export default {
name: "Positioning",
components: {
CurrentHousehold,
PersonRenderBox,
+ PersonComment,
},
computed: {
...mapState([
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/js/i18n.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/js/i18n.js
index e09fddf0f..66246058f 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/js/i18n.js
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/js/i18n.js
@@ -52,6 +52,7 @@ const appMessages = {
positioning: {
persons_to_positionnate: 'Usagers à positionner',
holder: "Titulaire",
+ comment: "Commentaire",
},
app: {
next: 'Suivant',
@@ -77,7 +78,12 @@ const appMessages = {
dates: {
start_date: "Début de validité",
end_date: "Fin de validité",
- dates_title: "Période de validité",
+ dates_title: "Depuis le",
+ },
+ composition: {
+ composition: "Composition familiale",
+ household_composition: "Composition du ménage",
+ number_of_children: "Nombre d'enfants mineurs au sein du ménage",
},
confirmation: {
save: "Enregistrer",
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/store/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/store/index.js
index cd43ee29c..cc5ab497c 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/store/index.js
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/store/index.js
@@ -1,5 +1,6 @@
import { createStore } from 'vuex';
import { householdMove, fetchHouseholdSuggestionByAccompanyingPeriod, fetchAddressSuggestionByPerson} from './../api.js';
+import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods.js'
import { fetchHouseholdByAddressReference } from 'ChillPersonAssets/lib/household.js';
import { datetimeToISO } from 'ChillMainAssets/chill/js/date.js';
@@ -54,8 +55,11 @@ const store = createStore({
*/
householdSuggestionByAccompanyingPeriod: [], // TODO rename into householdsSuggestion
showHouseholdSuggestion: window.household_members_editor_expand_suggestions === 1,
+ householdCompositionType: null,
+ numberOfChildren: 0,
addressesSuggestion: [],
showAddressSuggestion: true,
+ householdCompositionTypes: [],
warnings: [],
errors: []
},
@@ -250,7 +254,8 @@ const store = createStore({
payload_conc,
payload = {
concerned: [],
- destination: null
+ destination: null,
+ composition: null,
}
;
@@ -261,7 +266,6 @@ const store = createStore({
};
if (getters.isHouseholdNew && state.household.current_address !== null) {
- console.log(state.household);
payload.destination.forceAddress = { id: state.household.current_address.address_id };
}
}
@@ -290,6 +294,19 @@ const store = createStore({
payload.concerned.push(payload_conc);
}
+ if (getters.isHouseholdNew) {
+ payload.composition = {
+ household_composition_type: {
+ type: state.householdCompositionType.type,
+ id: state.householdCompositionType.id,
+ },
+ number_of_children: state.numberOfChildren,
+ start_date: {
+ datetime: datetimeToISO(state.startDate),
+ },
+ };
+ }
+
return payload;
},
},
@@ -409,6 +426,15 @@ const store = createStore({
setErrors(state, errors) {
state.errors = errors;
},
+ setHouseholdCompositionTypes(state, types) {
+ state.householdCompositionTypes = types;
+ },
+ setHouseholdCompositionType(state, id) {
+ state.householdCompositionType = state.householdCompositionTypes.find(t => t.id = id);
+ },
+ setNumberOfChildren(state, number) {
+ state.numberOfChildren = Number.parseInt(number);
+ },
addAddressesSuggestion(state, addresses) {
let existingIds = state.addressesSuggestion
.map(a => a.address_id);
@@ -570,4 +596,8 @@ if (concerned.length > 0) {
});
}
+fetchResults(`/api/1.0/person/houehold/composition/type.json`).then(types => {
+ store.commit('setHouseholdCompositionTypes', types);
+})
+
export { store };
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/Comment/index.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/Comment/index.html.twig
index cbebe5364..a8a5d78b8 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/Comment/index.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/Comment/index.html.twig
@@ -7,7 +7,7 @@
{% import '@ChillPerson/AccompanyingCourse/Comment/macro_showItem.html.twig' as m %}
{% macro recordAction(comment, isPinned) %}
- {% if isPinned is defined and isPinned == 'true' %}
+ {% if isPinned is defined and isPinned == true %}
{% else %}