diff --git a/CHANGELOG.md b/CHANGELOG.md
index ddc97b7b6..e26c866f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,11 +12,67 @@ and this project adheres to
* [main] avoid address reference search on undefined post code (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/561)
+* [Documents] Validate storedObject and allow for null data (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/565)
+* [Activity form] invert 'incoming' and 'receiving' in Activity form
+* [Activity form] keep the same order for 'attendee' field in new and edit form
+* [list with period] use "sameas" test operator to introduce requestor in list
+
+## Test releases
+
+### 2021-04-26
+
+* [Datepickers] datepickers fixed when using keyboard to enter date (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/545)
+* [social_action] Display 'agents traitants' in parcours resumé and social action list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/568)
+* [Person_search] Closed parcours shown within an accordeon that can be opened/closed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/574)
+
+### 2021-04-24
+
+* [notification email on course designation] allow raw string in email content generation
+* [Accompanying period work] list evaluations associated to a work by startDate, and then by id, from the most recent to older
+* [Documents] Change wording 'créer' to 'enregistrer' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/634)
+* [Parcours]: The number of 'mes parcours' displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/572)
+* [Hompage_widget]: Renaming of tabs and removal of social actions tab (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/570)
+* [activity]: Ignore thirdparties when creating a social action via an activity (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/573)
+* [parcours]: change wording of warning message and button when user is not associated to a household yet (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/590#note_918370943)
+* [Accompanying period work evaluations] list documents associated to a work by creation date, and then by id, from the most recent to older
+* [Course comment] add validationConstraint NotNull and NotBlank on comment content, to avoid sql error
+* [Notifications] delay the sending of notificaiton to kernel.terminate
+* [Notifications / Period user change] fix the sending of notification when user changes
+* [Activity form] invert 'incoming' and 'receiving' in Activity form
+* [Activity form] keep the same order for 'attendee' field in new and edit form
+* [list with period] use "sameas" test operator to introduce requestor in list
+* [notification email on course designation] allow raw string in email content generation
+* [Accompanying period work] list evaluations associated to a work by startDate, and then by id, from the most recent to older
+
+
+### 2021-04-13
+
+* [person] household address: add a form for editing the validFrom date (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/541)
+* [person] householdmemberseditor: fix composition type bug in select form (vuejs) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/543)
+* [docgen] add more persons choices in docgen for course: amongst requestor (if person), resources of course (if person), and PersonResource (if person);
+* [docgen] add a new context with a list of activities in course
+* [docgen] add a comment in budget lines
+* [notifications] allow to send a notification to an email address. The address receive an access link
+* [adresses] add constraints in database to avoid errors later: postcode not null, and validfrom <= validto
+* [accompanying work editor] add a label on document title input
+
+### 2021-04-07
+
+* notification list: move action buttons outside of the toggle
+* fix detecting of non-read notification
+* filter users which are disabled in search user api
+* order query for location and add pagination in list
+* allow every person which has part for a workflow to see the workflow page
+* able to see the workflow if the evaluation document has been deleted
+* hardcode the list of supported mime types for edition with collabora
+* list of accompanying course: allow to see the pinned comment in list_item
+
+### 2021-04-06
+
* [main] notification toggle read: correct js syntax for compilation in production (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/548)
* [parcours] Display of interlocuteurs changed to flex-table in parcours edit page to prevent cut-off of information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/535)
* [activity] espace entre les boutons pour supprimer les documents
-## Test releases
### continuous release in February and March
@@ -61,6 +117,7 @@ and this project adheres to
* [household] bugfix if position of member is null, renderbox no longer throws an error (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/480)
* [parcours] location cannot be removed if linked to a user (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/478)
* [person] email added to twig personRenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/490)
+* [activity] Only youngest descendant is kept for social issues and actions (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/471)
* [person] Add link to current household in person banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/484)
* [address] person badge in address history changed to open OnTheFly with all person info (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/489)
* [person] Change 'personne' with 'usager' and '&' with 'ET' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/499)
@@ -88,6 +145,9 @@ and this project adheres to
* [notification] Display of social action within workflow notification set to display block (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/537)
* [onthefly] trim trailing whitespace in email of person and thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/542)
+* [action] Only youngest descendant is kept for social issues and actions (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/471)
+## Test releases
+
### test release 2022-02-21
* [notifications] Word 'un' changed to number '1' for notifications in user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/483)
diff --git a/composer.json b/composer.json
index c96738df8..d0fc51c0e 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,7 @@
"knplabs/knp-time-bundle": "^1.12",
"league/csv": "^9.7.1",
"nyholm/psr7": "^1.4",
- "ocramius/package-versions": "^1.10",
+ "ocramius/package-versions": "^1.10 || ^2",
"odolbeau/phone-number-bundle": "^3.6",
"phpoffice/phpspreadsheet": "^1.16",
"ramsey/uuid-doctrine": "^1.7",
@@ -33,6 +33,7 @@
"symfony/expression-language": "^4.4",
"symfony/form": "^4.4",
"symfony/framework-bundle": "^4.4",
+ "symfony/http-foundation": "^4.4",
"symfony/intl": "^4.4",
"symfony/mailer": "^5.4",
"symfony/mime": "^5.4",
@@ -103,7 +104,8 @@
"ergebnis/composer-normalize": true,
"ocramius/package-versions": true,
"phpro/grumphp": true,
- "phpstan/extension-installer": true
+ "phpstan/extension-installer": true,
+ "roave/you-are-using-it-wrong": true
},
"bin-dir": "bin",
"optimize-autoloader": true,
diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php
index 0a344236c..0c0632722 100644
--- a/src/Bundle/ChillActivityBundle/Entity/Activity.php
+++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php
@@ -35,6 +35,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;
+use Symfony\Component\Validator\Constraints as Assert;
/**
* Class Activity.
@@ -93,6 +94,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToMany(targetEntity="Chill\DocStoreBundle\Entity\StoredObject", cascade={"persist"})
+ * @Assert\Valid(traverse=true)
*/
private Collection $documents;
@@ -231,11 +233,22 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
{
if (!$this->socialActions->contains($socialAction)) {
$this->socialActions[] = $socialAction;
+ $this->ensureSocialActionConsistency();
}
return $this;
}
+ /**
+ * Add a social issue.
+ *
+ * Note: the social issue consistency (the fact that only yougest social issues
+ * are kept) is processed by an entity listener:
+ *
+ * @see{\Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener}
+ *
+ * @return $this
+ */
public function addSocialIssue(SocialIssue $socialIssue): self
{
if (!$this->socialIssues->contains($socialIssue)) {
@@ -631,4 +644,13 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this;
}
+
+ private function ensureSocialActionConsistency(): void
+ {
+ $ancestors = SocialAction::findAncestorSocialActions($this->getSocialActions());
+
+ foreach ($ancestors as $ancestor) {
+ $this->removeSocialAction($ancestor);
+ }
+ }
}
diff --git a/src/Bundle/ChillActivityBundle/EntityListener/ActivityEntityListener.php b/src/Bundle/ChillActivityBundle/EntityListener/ActivityEntityListener.php
index ab370e89e..88adf9723 100644
--- a/src/Bundle/ChillActivityBundle/EntityListener/ActivityEntityListener.php
+++ b/src/Bundle/ChillActivityBundle/EntityListener/ActivityEntityListener.php
@@ -66,9 +66,6 @@ class ActivityEntityListener
$newAction->addPerson($person);
}
- foreach ($associatedThirdparties as $thirdparty) {
- $newAction->setHandlingThierparty($thirdparty);
- }
$this->em->persist($newAction);
$this->em->flush();
}
diff --git a/src/Bundle/ChillActivityBundle/Form/ActivityType.php b/src/Bundle/ChillActivityBundle/Form/ActivityType.php
index 1e1daead5..6e75bde25 100644
--- a/src/Bundle/ChillActivityBundle/Form/ActivityType.php
+++ b/src/Bundle/ChillActivityBundle/Form/ActivityType.php
@@ -373,8 +373,8 @@ class ActivityType extends AbstractType
'label' => $activityType->getLabel('sentReceived'),
'required' => $activityType->isRequired('sentReceived'),
'choices' => [
- 'Sent' => Activity::SENTRECEIVED_SENT,
'Received' => Activity::SENTRECEIVED_RECEIVED,
+ 'Sent' => Activity::SENTRECEIVED_SENT,
],
]);
}
diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php b/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
index 48f750c26..d960a0c21 100644
--- a/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
+++ b/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
@@ -12,13 +12,20 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\Activity;
+use Chill\ActivityBundle\Entity\ActivityPresence;
+use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
+use Chill\MainBundle\Entity\Location;
+use Chill\MainBundle\Entity\LocationType;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
+use Doctrine\DBAL\Types\Types;
+use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Security;
@@ -72,6 +79,86 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
->findByAccompanyingPeriod($period, $scopes, true, $limit, $start, $orderBy);
}
+ public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array
+ {
+ $rsm = new ResultSetMappingBuilder($this->em);
+
+ $sql = '
+ SELECT
+ a.id AS activity_id,
+ date,
+ CASE WHEN durationtime IS NOT NULL THEN (EXTRACT(EPOCH from durationtime) / 60)::int ELSE 0 END AS durationtimeminute,
+ attendee_id,
+ comment_comment,
+ emergency,
+ sentreceived,
+ CASE WHEN traveltime IS NOT NULL THEN (EXTRACT(EPOCH from traveltime) / 60)::int ELSE 0 END AS traveltimeminute,
+ t.id AS type_id, t.name as type_name,
+ p.id AS presence_id, p.name AS presence_name,
+ location.id AS location_id, location.address_id, location.name AS location_name, location.phonenumber1, location.phonenumber2, location.email,
+ location.locationtype_id, locationtype.title AS locationtype_title,
+ users.userids AS userids,
+ thirdparties.thirdpartyids,
+ persons.personids,
+ actions.socialactionids,
+ issues.socialissueids
+
+ FROM activity a
+ LEFT JOIN chill_main_location location ON a.location_id = location.id
+ LEFT JOIN chill_main_location_type locationtype ON location.locationtype_id = locationtype.id
+ LEFT JOIN activitytpresence p ON a.attendee_id = p.id
+ LEFT JOIN activitytype t ON a.type_id = t.id
+ LEFT JOIN LATERAL (SELECT jsonb_agg(user_id) userids, activity_id FROM activity_user AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS users ON TRUE
+ LEFT JOIN LATERAL (SELECT jsonb_agg(thirdparty_id) thirdpartyids, activity_id FROM activity_thirdparty AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS thirdparties ON TRUE
+ LEFT JOIN LATERAL (SELECT jsonb_agg(person_id) personids, activity_id FROM activity_person AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS persons ON TRUE
+ LEFT JOIN LATERAL (SELECT jsonb_agg(socialaction_id) socialactionids, activity_id FROM chill_activity_activity_chill_person_socialaction AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS actions ON TRUE
+ LEFT JOIN LATERAL (SELECT jsonb_agg(socialissue_id) socialissueids, activity_id FROM chill_activity_activity_chill_person_socialissue AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS issues ON TRUE
+
+ WHERE accompanyingperiod_id = ?
+ ORDER BY a.date DESC, a.id DESC
+ LIMIT ?
+ ';
+
+ $rsm
+ ->addEntityResult(Activity::class, 'a')
+ ->addFieldResult('a', 'activity_id', 'id')
+ ->addFieldResult('a', 'date', 'date')
+ ->addFieldResult('a', 'comment', 'comment')
+ ->addFieldResult('a', 'sentreceived', 'sentReceived')
+ ->addFieldResult('a', 'emergency', 'emergency')
+ ->addJoinedEntityResult(Location::class, 'location', 'a', 'location')
+ ->addFieldResult('location', 'location_id', 'id')
+ ->addFieldResult('location', 'location_name', 'name')
+ ->addFieldResult('location', 'phonenumber1', 'phonenumber1')
+ ->addFieldResult('location', 'phonenumber2', 'phonenumber2')
+ ->addFieldResult('location', 'email', 'email')
+ ->addJoinedEntityResult(LocationType::class, 'locationType', 'location', 'locationType')
+ ->addFieldResult('locationType', 'locationtype_id', 'id')
+ ->addFieldResult('locationType', 'locationtype_title', 'title')
+ ->addJoinedEntityResult(ActivityType::class, 'activityType', 'a', 'activityType')
+ ->addFieldResult('activityType', 'type_id', 'id')
+ ->addFieldResult('activityType', 'type_name', 'name')
+ ->addJoinedEntityResult(ActivityPresence::class, 'activityPresence', 'a', 'attendee')
+ ->addFieldResult('activityPresence', 'presence_id', 'id')
+ ->addFieldResult('activityPresence', 'presence_name', 'name')
+
+ // results which cannot be mapped into entity
+ ->addScalarResult('comment_comment', 'comment', Types::TEXT)
+ ->addScalarResult('userids', 'userIds', Types::JSON)
+ ->addScalarResult('thirdpartyids', 'thirdPartyIds', Types::JSON)
+ ->addScalarResult('personids', 'personIds', Types::JSON)
+ ->addScalarResult('socialactionids', 'socialActionIds', Types::JSON)
+ ->addScalarResult('socialissueids', 'socialIssueIds', Types::JSON)
+ ->addScalarResult('durationtimeminute', 'durationTimeMinute', Types::INTEGER)
+ ->addScalarResult('traveltimeminute', 'travelTimeMinute', Types::INTEGER);
+
+ $nq = $this->em->createNativeQuery($sql, $rsm);
+
+ $nq->setParameter(0, $period->getId())->setParameter(1, $limit);
+
+ return $nq->getResult(AbstractQuery::HYDRATE_ARRAY);
+ }
+
/**
* @param array $orderBy
*
diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepositoryInterface.php b/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepositoryInterface.php
index 56fb112f9..1fc6d22b1 100644
--- a/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepositoryInterface.php
+++ b/src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepositoryInterface.php
@@ -21,6 +21,15 @@ interface ActivityACLAwareRepositoryInterface
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
+ /**
+ * Return a list of activities, simplified as array (not object).
+ *
+ * The aim of this method is to get a long list of activities and keep performance.
+ *
+ * @return array an array of array, each item representing an activity
+ */
+ public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array;
+
/**
* @return Activity[]|array
*/
diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue
index 51957b010..4691a860a 100644
--- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue
+++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue
@@ -54,19 +54,19 @@
{{ $t('activity.select_first_a_social_issue') }}
-
-
-
-
+
+
+
+
- {{ $t('activity.social_action_list_empty') }}
+ {{ $t('activity.social_action_list_empty') }}
@@ -111,7 +111,7 @@ export default {
return this.$store.state.socialIssuesOther;
},
socialActionsList() {
- return this.$store.state.socialActionsList;
+ return this.$store.getters.socialActionsListSorted;
},
socialActionsSelected() {
return this.$store.state.activity.socialActions;
diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js
index f94381fc2..ca56a5dae 100644
--- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js
+++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js
@@ -101,6 +101,9 @@ const store = createStore({
state.activity.activityType.thirdPartiesVisible !== 0)
);
},
+ socialActionsListSorted(state) {
+ return [ ...state.socialActionsList].sort((a, b) => a.ordering - b.ordering);
+ },
},
mutations: {
// SocialIssueAcc
@@ -131,7 +134,6 @@ const store = createStore({
state.socialActionsList = [];
},
addActionInList(state, action) {
- //console.log('add action list', action.id);
state.socialActionsList.push(action);
},
updateActionsSelected(state, actions) {
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/new.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/new.html.twig
index a7751cd5a..8e078702c 100644
--- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/new.html.twig
+++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/new.html.twig
@@ -85,15 +85,15 @@
{{ form_row(form.comment) }}
{% endif %}
+{%- if form.attendee is defined -%}
+ {{ form_row(form.attendee) }}
+{% endif %}
+
{%- if form.documents is defined -%}
{{ form_row(form.documents) }}
{% endif %}
-{%- if form.attendee is defined -%}
- {{ form_row(form.attendee) }}
-{% endif %}
-
{# TODO .. status #}
diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php
new file mode 100644
index 000000000..0a442cec7
--- /dev/null
+++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php
@@ -0,0 +1,286 @@
+accompanyingPeriodContext = $accompanyingPeriodContext;
+ $this->activityACLAwareRepository = $activityACLAwareRepository;
+ $this->normalizer = $normalizer;
+ $this->personRepository = $personRepository;
+ $this->socialActionRepository = $socialActionRepository;
+ $this->socialIssueRepository = $socialIssueRepository;
+ $this->thirdPartyRepository = $thirdPartyRepository;
+ $this->translatableStringHelper = $translatableStringHelper;
+ $this->userRepository = $userRepository;
+ }
+
+ public function adminFormReverseTransform(array $data): array
+ {
+ return $this->accompanyingPeriodContext->adminFormReverseTransform($data);
+ }
+
+ public function adminFormTransform(array $data): array
+ {
+ return $this->accompanyingPeriodContext->adminFormTransform($data);
+ }
+
+ public function buildAdminForm(FormBuilderInterface $builder): void
+ {
+ $this->accompanyingPeriodContext->buildAdminForm($builder);
+ }
+
+ public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
+ {
+ $this->accompanyingPeriodContext->buildPublicForm($builder, $template, $entity);
+ }
+
+ public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
+ {
+ $data = $this->accompanyingPeriodContext->getData($template, $entity, $contextGenerationData);
+
+ $data['activities'] = $this->getActivitiesSimplified($entity);
+
+ return $data;
+ }
+
+ public function getDescription(): string
+ {
+ return 'docgen.Accompanying period with a list of activities description';
+ }
+
+ public function getEntityClass(): string
+ {
+ return AccompanyingPeriod::class;
+ }
+
+ public function getFormData(DocGeneratorTemplate $template, $entity): array
+ {
+ return $this->accompanyingPeriodContext->getFormData($template, $entity);
+ }
+
+ public static function getKey(): string
+ {
+ return self::class;
+ }
+
+ public function getName(): string
+ {
+ return 'docgen.Accompanying period with a list of activities';
+ }
+
+ public function hasAdminForm(): bool
+ {
+ return $this->accompanyingPeriodContext->hasAdminForm();
+ }
+
+ public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
+ {
+ return $this->accompanyingPeriodContext->hasPublicForm($template, $entity);
+ }
+
+ public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
+ {
+ $this->accompanyingPeriodContext->storeGenerated($template, $storedObject, $entity, $contextGenerationData);
+ }
+
+ private function getActivitiesSimplified(AccompanyingPeriod $period)
+ {
+ $activities =
+ $this->activityACLAwareRepository->findByAccompanyingPeriodSimplified($period);
+ $results = [];
+
+ foreach ($activities as $row) {
+ $activity = $row[0];
+
+ $activity['date'] = $this->normalizer->normalize($activity['date'], 'docgen', [
+ AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => DateTime::class,
+ ]);
+
+ if (null === $activity['location']) {
+ $activity['location'] = $this->normalizer->normalize(null, 'docgen', [
+ AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => Location::class,
+ ]);
+ $activity['location']['type'] = 'location';
+ } else {
+ $activity['location']['isNull'] = false;
+ $activity['location']['type'] = 'location';
+
+ foreach (['1', '2'] as $key) {
+ $activity['location']['phonenumber' . $key] = $this->normalizer->normalize(
+ $activity['location']['phonenumber' . $key],
+ 'docgen',
+ [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => PhoneNumber::class]
+ );
+ }
+ }
+
+ if (is_numeric($activity['location']['locationType']['id'])) {
+ $activity['location']['locationType']['title'] = $this->translatableStringHelper->localize(
+ $activity['location']['locationType']['title']
+ );
+ $activity['location']['locationType']['isNull'] = false;
+ $activity['location']['locationType']['type'] = 'locationType';
+ }
+
+ if (null !== $activity['activityType']) {
+ $activity['activityType']['name'] = $this->translatableStringHelper->localize(
+ $activity['activityType']['name']
+ );
+ $activity['activityType']['isNull'] = false;
+ $activity['activityType']['type'] = 'activityType';
+ } else {
+ $activity['activityType'] = $this->normalizer->normalize(null, 'docgen', [
+ AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ActivityType::class,
+ ]);
+ }
+
+ if (null !== $activity['attendee']) {
+ $activity['attendee']['name'] = $this->translatableStringHelper->localize(
+ $activity['attendee']['name']
+ );
+ $activity['attendee']['isNull'] = false;
+ $activity['attendee']['type'] = 'activityPresence';
+ } else {
+ $activity['attendee'] = $this->normalizer->normalize(null, 'docgen', [
+ AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ActivityPresence::class,
+ ]);
+ }
+
+ $activity['comment'] = (string) $row['comment'];
+ $activity['travelTimeMinute'] = $row['travelTimeMinute'];
+ $activity['durationTimeMinute'] = $row['durationTimeMinute'];
+
+ if (null !== $row['userIds']) {
+ foreach ($row['userIds'] as $id) {
+ $activity['users'][] = $this->normalizer->normalize(
+ $this->userRepository->find($id),
+ 'docgen',
+ [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => User::class]
+ );
+ }
+ } else {
+ $activity['users'] = [];
+ }
+
+ if (null !== $row['personIds']) {
+ foreach ($row['personIds'] as $id) {
+ $activity['persons'][] = $this->normalizer->normalize(
+ $this->personRepository->find($id),
+ 'docgen',
+ [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => Person::class]
+ );
+ }
+ } else {
+ $activity['persons'] = [];
+ }
+
+ if (null !== $row['thirdPartyIds']) {
+ foreach ($row['thirdPartyIds'] as $id) {
+ $activity['thirdParties'][] = $this->normalizer->normalize(
+ $this->thirdPartyRepository->find($id),
+ 'docgen',
+ [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ThirdParty::class]
+ );
+ }
+ } else {
+ $activity['thirdParties'] = [];
+ }
+
+ if (null !== $row['socialActionIds']) {
+ foreach ($row['socialActionIds'] as $id) {
+ $activity['socialActions'][] = $this->normalizer->normalize(
+ $this->socialActionRepository->find($id),
+ 'docgen',
+ [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => SocialAction::class]
+ );
+ }
+ } else {
+ $activity['socialActions'] = [];
+ }
+
+ if (null !== $row['socialIssueIds']) {
+ foreach ($row['socialIssueIds'] as $id) {
+ $activity['socialIssues'][] = $this->normalizer->normalize(
+ $this->socialIssueRepository->find($id),
+ 'docgen',
+ [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => SocialIssue::class]
+ );
+ }
+ } else {
+ $activity['socialIssues'] = [];
+ }
+
+ $results[] = $activity;
+ }
+
+ return $results;
+ }
+}
diff --git a/src/Bundle/ChillActivityBundle/Tests/Entity/ActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Entity/ActivityTest.php
new file mode 100644
index 000000000..7a68a36fc
--- /dev/null
+++ b/src/Bundle/ChillActivityBundle/Tests/Entity/ActivityTest.php
@@ -0,0 +1,114 @@
+addChild($child);
+ $grandChild = new SocialAction();
+ $child->addChild($grandChild);
+
+ $activity = new Activity();
+
+ $activity->addSocialAction($parent);
+
+ $this->assertCount(1, $activity->getSocialActions());
+ $this->assertContains($parent, $activity->getSocialActions());
+
+ $activity->addSocialAction($grandChild);
+
+ $this->assertCount(1, $activity->getSocialActions());
+ $this->assertContains($grandChild, $activity->getSocialActions());
+ $this->assertNotContains($parent, $activity->getSocialActions());
+
+ $activity->addSocialAction($child);
+
+ $this->assertCount(1, $activity->getSocialActions());
+ $this->assertContains($grandChild, $activity->getSocialActions());
+ $this->assertNotContains($parent, $activity->getSocialActions());
+ $this->assertNotContains($child, $activity->getSocialActions());
+
+ $activity->addSocialAction($another = new SocialAction());
+
+ $this->assertCount(2, $activity->getSocialActions());
+ $this->assertContains($grandChild, $activity->getSocialActions());
+ $this->assertContains($another, $activity->getSocialActions());
+ $this->assertNotContains($parent, $activity->getSocialActions());
+ $this->assertNotContains($child, $activity->getSocialActions());
+ }
+
+ public function testHierarchySocialIssues(): void
+ {
+ $listener = new AccompanyingPeriodSocialIssueConsistencyEntityListener();
+ $event = $this->prophesize(LifecycleEventArgs::class)->reveal();
+
+ $parent = new SocialIssue();
+ $child = new SocialIssue();
+
+ $parent->addChild($child);
+ $grandChild = new SocialIssue();
+ $child->addChild($grandChild);
+
+ $activity = new Activity();
+ $activity->setAccompanyingPeriod(new AccompanyingPeriod());
+
+ $activity->addSocialIssue($parent);
+ $listener->preUpdate($activity, $event);
+
+ $this->assertCount(1, $activity->getSocialIssues());
+ $this->assertContains($parent, $activity->getSocialIssues());
+
+ $activity->addSocialIssue($grandChild);
+ $listener->preUpdate($activity, $event);
+
+ $this->assertCount(1, $activity->getSocialIssues());
+ $this->assertContains($grandChild, $activity->getSocialIssues());
+ $this->assertNotContains($parent, $activity->getSocialIssues());
+
+ $activity->addSocialIssue($child);
+ $listener->preUpdate($activity, $event);
+
+ $this->assertCount(1, $activity->getSocialIssues());
+ $this->assertContains($grandChild, $activity->getSocialIssues());
+ $this->assertNotContains($parent, $activity->getSocialIssues());
+ $this->assertNotContains($child, $activity->getSocialIssues());
+
+ $activity->addSocialIssue($another = new SocialIssue());
+ $listener->preUpdate($activity, $event);
+
+ $this->assertCount(2, $activity->getSocialIssues());
+ $this->assertContains($grandChild, $activity->getSocialIssues());
+ $this->assertContains($another, $activity->getSocialIssues());
+ $this->assertNotContains($parent, $activity->getSocialIssues());
+ $this->assertNotContains($child, $activity->getSocialIssues());
+ }
+}
diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml
index 051497b69..9d7fdd288 100644
--- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml
+++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml
@@ -66,6 +66,14 @@ Choose a type: Choisir un type
1 hour 30: 1 heure 30
1 hour 45: 1 heure 45
2 hours: 2 heures
+2 hours 15: 2 heures 15
+2 hours 30: 2 heures 30
+2 hours 45: 2 heures 45
+3 hours: 3 heures
+3 hours 30: 3 heures 30
+4 hours: 4 heures
+4 hours 30: 4 heures 30
+5 hours: 5 heures
Concerned groups: Parties concernées
Persons in accompanying course: Usagers du parcours
Third persons: Tiers non-pro.
@@ -232,3 +240,5 @@ This is the minimal activity data: Activité n°
docgen:
Activity basic: Echange
A basic context for activity: Contexte pour les échanges
+ Accompanying period with a list of activities: Parcours d'accompagnement avec liste des échanges
+ Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les échanges pour un parcours. Les échanges ne sont pas filtrés.
diff --git a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/Configuration.php b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/Configuration.php
index a077ce902..76bcebca5 100644
--- a/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/Configuration.php
+++ b/src/Bundle/ChillAsideActivityBundle/src/DependencyInjection/Configuration.php
@@ -31,6 +31,10 @@ class Configuration implements ConfigurationInterface
->isRequired()
->defaultValue(
[
+ ['label' => '1 minutes', 'seconds' => 60],
+ ['label' => '2 minutes', 'seconds' => 120],
+ ['label' => '3 minutes', 'seconds' => 180],
+ ['label' => '4 minutes', 'seconds' => 240],
['label' => '5 minutes', 'seconds' => 300],
['label' => '10 minutes', 'seconds' => 600],
['label' => '15 minutes', 'seconds' => 900],
diff --git a/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php b/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php
index 8e46feb7b..243cd4749 100644
--- a/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php
+++ b/src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php
@@ -25,13 +25,13 @@ use function count;
*/
class SummaryBudget implements SummaryBudgetInterface
{
- private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, type FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
+ private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
- private const QUERY_CHARGE_BY_PERSON = 'select SUM(amount) AS sum, type FROM chill_budget.charge WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
+ private const QUERY_CHARGE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.charge WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
- private const QUERY_RESOURCE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, type FROM chill_budget.resource WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
+ private const QUERY_RESOURCE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.resource WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
- private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, type FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
+ private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private array $chargeLabels;
@@ -110,7 +110,8 @@ class SummaryBudget implements SummaryBudgetInterface
$rsm = new ResultSetMapping();
$rsm
->addScalarResult('sum', 'sum')
- ->addScalarResult('type', 'type');
+ ->addScalarResult('type', 'type')
+ ->addScalarResult('comment', 'comment');
return $rsm;
}
@@ -121,7 +122,7 @@ class SummaryBudget implements SummaryBudgetInterface
$labels = $this->chargeLabels;
return array_combine($keys, array_map(function ($i) use ($labels) {
- return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i])];
+ return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i]), 'comment' => ''];
}, $keys));
}
@@ -131,7 +132,7 @@ class SummaryBudget implements SummaryBudgetInterface
$labels = $this->resourcesLabels;
return array_combine($keys, array_map(function ($i) use ($labels) {
- return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i])];
+ return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i]), 'comment' => ''];
}, $keys));
}
@@ -158,6 +159,7 @@ class SummaryBudget implements SummaryBudgetInterface
$result[$row['type']] = [
'sum' => (float) $row['sum'],
'label' => $this->translatableStringHelper->localize($label[$row['type']]),
+ 'comment' => (string) $row['comment'],
];
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/Context/ContextManager.php b/src/Bundle/ChillDocGeneratorBundle/Context/ContextManager.php
index 8dfecb730..b8f4d0eed 100644
--- a/src/Bundle/ChillDocGeneratorBundle/Context/ContextManager.php
+++ b/src/Bundle/ChillDocGeneratorBundle/Context/ContextManager.php
@@ -14,7 +14,7 @@ namespace Chill\DocGeneratorBundle\Context;
use Chill\DocGeneratorBundle\Context\Exception\ContextNotFoundException;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
-class ContextManager
+final class ContextManager implements ContextManagerInterface
{
/**
* @var DocGeneratorContextInterface[]|iterable
@@ -26,9 +26,6 @@ class ContextManager
$this->contexts = $contexts;
}
- /**
- * @throw ContextNotFoundException when the context is not found
- */
public function getContextByDocGeneratorTemplate(DocGeneratorTemplate $docGeneratorTemplate): DocGeneratorContextInterface
{
foreach ($this->contexts as $key => $context) {
diff --git a/src/Bundle/ChillDocGeneratorBundle/Context/ContextManagerInterface.php b/src/Bundle/ChillDocGeneratorBundle/Context/ContextManagerInterface.php
new file mode 100644
index 000000000..3468d787d
--- /dev/null
+++ b/src/Bundle/ChillDocGeneratorBundle/Context/ContextManagerInterface.php
@@ -0,0 +1,30 @@
+contextManager = $contextManager;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->driver = $driver;
$this->logger = $logger;
$this->paginatorFactory = $paginatorFactory;
- $this->tempUrlGenerator = $tempUrlGenerator;
- $this->kernel = $kernel;
$this->client = $client;
+ $this->storedObjectManager = $storedObjectManager;
+ $this->entityManager = $entityManager;
}
/**
@@ -178,8 +176,10 @@ final class DocGeneratorTemplateController extends AbstractController
return $this->redirectToRoute(
'chill_docgenerator_test_generate_from_template',
- ['template' => $template, 'entityClassName' => $entityClassName, 'entityId' => $entityId,
- 'returnPath' => $request->query->get('returnPath', '/'), ]
+ [
+ 'template' => $template, 'entityClassName' => $entityClassName, 'entityId' => $entityId,
+ 'returnPath' => $request->query->get('returnPath', '/'),
+ ]
);
}
@@ -193,16 +193,26 @@ final class DocGeneratorTemplateController extends AbstractController
try {
$context = $this->contextManager->getContextByDocGeneratorTemplate($template);
} catch (ContextNotFoundException $e) {
- throw new NotFoundHttpException($e->getMessage(), $e);
+ throw new NotFoundHttpException(
+ 'Context not found.',
+ $e
+ );
}
- $entity = $this->getDoctrine()->getRepository($context->getEntityClass())->find($entityId);
+ $entity = $this
+ ->entityManager
+ ->getRepository($context->getEntityClass())
+ ->find($entityId);
if (null === $entity) {
- throw new NotFoundHttpException("Entity with classname {$entityClassName} and id {$entityId} is not found");
+ throw new NotFoundHttpException(
+ sprintf('Entity with classname %s and id %s is not found', $entityClassName, $entityId)
+ );
}
- $contextGenerationData = [];
+ $contextGenerationData = [
+ 'test_file' => null,
+ ];
if (
$context instanceof DocGeneratorContextWithPublicFormInterface
@@ -240,128 +250,114 @@ final class DocGeneratorTemplateController extends AbstractController
$contextGenerationData = $form->getData();
} elseif (!$form->isSubmitted() || ($form->isSubmitted() && !$form->isValid())) {
$templatePath = '@ChillDocGenerator/Generator/basic_form.html.twig';
- $templateOptions = ['entity' => $entity, 'form' => $form->createView(),
- 'template' => $template, 'context' => $context, ];
+ $templateOptions = [
+ 'entity' => $entity, 'form' => $form->createView(),
+ 'template' => $template, 'context' => $context,
+ ];
return $this->render($templatePath, $templateOptions);
}
}
- if ($isTest && null !== $contextGenerationData['test_file']) {
- /** @var File $file */
- $file = $contextGenerationData['test_file'];
- $templateResource = fopen($file->getPathname(), 'rb');
+ $document = $template->getFile();
+
+ if ($isTest && ($contextGenerationData['test_file'] instanceof File)) {
+ $dataDecrypted = file_get_contents($contextGenerationData['test_file']->getPathname());
} else {
- $getUrlGen = $this->tempUrlGenerator->generate(
- 'GET',
- $template->getFile()->getFilename()
- );
-
- $data = $this->client->request('GET', $getUrlGen->url);
-
- $iv = $template->getFile()->getIv(); // iv as an Array
- $ivGoodFormat = pack('C*', ...$iv); // iv as a String (ok for openssl_decrypt)
-
- $method = 'AES-256-CBC';
-
- $key = $template->getFile()->getKeyInfos()['k'];
- $keyGoodFormat = Base64Url::decode($key);
-
- $dataDecrypted = openssl_decrypt($data->getContent(), $method, $keyGoodFormat, 1, $ivGoodFormat);
-
- if (false === $dataDecrypted) {
- throw new Exception('Error during Decrypt ', 1);
+ try {
+ $dataDecrypted = $this->storedObjectManager->read($document);
+ } catch (Throwable $exception) {
+ throw $exception;
}
-
- if (false === $templateResource = fopen('php://memory', 'r+b')) {
- $this->logger->error('Could not write data to memory');
-
- throw new HttpException(500);
- }
- fwrite($templateResource, $dataDecrypted);
- rewind($templateResource);
}
- $datas = $context->getData($template, $entity, $contextGenerationData);
if ($isTest && isset($form) && $form['show_data']->getData()) {
// very ugly hack...
- dd($datas);
+ dd($context->getData($template, $entity, $contextGenerationData));
}
try {
- $generatedResource = $this->driver->generateFromResource($templateResource, $template->getFile()->getType(), $datas, $template->getFile()->getFilename());
+ $generatedResource = $this
+ ->driver
+ ->generateFromString(
+ $dataDecrypted,
+ $template->getFile()->getType(),
+ $context->getData($template, $entity, $contextGenerationData),
+ $template->getFile()->getFilename()
+ );
} catch (TemplateException $e) {
- $msg = implode("\n", $e->getErrors());
-
- return new Response($msg, 400, [
- 'Content-Type' => 'text/plain',
- ]);
+ return new Response(
+ implode("\n", $e->getErrors()),
+ 400,
+ [
+ 'Content-Type' => 'text/plain',
+ ]
+ );
}
- fclose($templateResource);
-
if ($isTest) {
- return new StreamedResponse(
- static function () use ($generatedResource) {
- fpassthru($generatedResource);
- fclose($generatedResource);
- },
+ return new Response(
+ $generatedResource,
Response::HTTP_OK,
[
'Content-Transfer-Encoding', 'binary',
'Content-Type' => 'application/vnd.oasis.opendocument.text',
- 'Content-Disposition' => sprintf('attachment; filename="%s.odt"', 'generated'),
- 'Content-Length' => fstat($generatedResource)['size'],
+ 'Content-Disposition' => 'attachment; filename="generated.odt"',
+ 'Content-Length' => strlen($generatedResource),
],
);
}
- $genDocName = 'doc_' . sprintf('%010d', mt_rand()) . 'odt';
-
- $getUrlGen = $this->tempUrlGenerator->generate(
- 'PUT',
- $genDocName
- );
-
- $client = new Client();
+ /** @var StoredObject $storedObject */
+ $storedObject = (new ObjectNormalizer())
+ ->denormalize(
+ [
+ 'type' => $template->getFile()->getType(),
+ 'filename' => sprintf('%s_odt', uniqid('doc_', true)),
+ ],
+ StoredObject::class
+ );
try {
- $putResponse = $client->request('PUT', $getUrlGen->url, [
- 'body' => $generatedResource,
- ]);
+ $this->storedObjectManager->write($storedObject, $generatedResource);
+ } catch (Throwable $exception) {
+ throw $exception;
+ }
- if ($putResponse->getStatusCode() === 201) {
- $em = $this->getDoctrine()->getManager();
- $storedObject = new StoredObject();
- $storedObject
- ->setType($template->getFile()->getType())
- ->setFilename($genDocName);
+ $this->entityManager->persist($storedObject);
- $em->persist($storedObject);
-
- try {
- $context->storeGenerated($template, $storedObject, $entity, $contextGenerationData);
- } catch (Exception $e) {
- $this->logger->error('Could not store the associated document to entity', [
+ try {
+ $context
+ ->storeGenerated(
+ $template,
+ $storedObject,
+ $entity,
+ $contextGenerationData
+ );
+ } catch (Exception $e) {
+ $this
+ ->logger
+ ->error(
+ 'Unable to store the associated document to entity',
+ [
'entityClassName' => $entityClassName,
'entityId' => $entityId,
'contextKey' => $context->getName(),
- ]);
+ ]
+ );
- throw $e;
- }
-
- $em->flush();
-
- return $this->redirectToRoute('chill_wopi_file_edit', [
- 'fileId' => $storedObject->getUuid(),
- 'returnPath' => $request->query->get('returnPath', '/'),
- ]);
- }
- } catch (TransferException $e) {
throw $e;
}
- throw new Exception('Unable to generate document.');
+ $this->entityManager->flush();
+
+ return $this
+ ->redirectToRoute(
+ 'chill_wopi_file_edit',
+ [
+ 'fileId' => $storedObject->getUuid(),
+ 'returnPath' => $request->query->get('returnPath', '/'),
+ ]
+ );
}
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/DriverInterface.php b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/DriverInterface.php
index 2572484e1..d787523c9 100644
--- a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/DriverInterface.php
+++ b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/DriverInterface.php
@@ -13,10 +13,5 @@ namespace Chill\DocGeneratorBundle\GeneratorDriver;
interface DriverInterface
{
- /**
- * @param resource $template
- *
- * @return resource
- */
- public function generateFromResource($template, string $resourceType, array $data, ?string $templateName = null);
+ public function generateFromString(string $template, string $resourceType, array $data, ?string $templateName = null): string;
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php
index 7a3e4ac69..4cadcef1f 100644
--- a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php
+++ b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php
@@ -16,45 +16,47 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\FormDataPart;
+use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
-use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
+use Throwable;
-class RelatorioDriver implements DriverInterface
+final class RelatorioDriver implements DriverInterface
{
- private LoggerInterface $logger;
+ private HttpClientInterface $client;
- private HttpClientInterface $relatorioClient;
+ private LoggerInterface $logger;
private string $url;
public function __construct(
- HttpClientInterface $relatorioClient,
+ HttpClientInterface $client,
ParameterBagInterface $parameterBag,
LoggerInterface $logger
) {
- $this->relatorioClient = $relatorioClient;
+ $this->client = $client;
$this->logger = $logger;
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
}
- public function generateFromResource($template, string $resourceType, array $data, ?string $templateName = null)
+ public function generateFromString(string $template, string $resourceType, array $data, ?string $templateName = null): string
{
- $formFields = [
- 'variables' => json_encode($data),
- 'template' => new DataPart($template, $templateName ?? uniqid('template_'), $resourceType),
- ];
- $form = new FormDataPart($formFields);
+ $form = new FormDataPart(
+ [
+ 'variables' => json_encode($data),
+ 'template' => new DataPart($template, $templateName ?? uniqid('template_'), $resourceType),
+ ]
+ );
try {
- $response = $this->relatorioClient->request('POST', $this->url, [
+ $response = $this->client->request('POST', $this->url, [
'headers' => $form->getPreparedHeaders()->toArray(),
'body' => $form->bodyToIterable(),
]);
- return $response->toStream();
- } catch (HttpExceptionInterface $e) {
+ return $response->getContent();
+ } catch (ClientExceptionInterface $e) {
$content = $e->getResponse()->getContent(false);
if (400 === $e->getResponse()->getStatusCode()) {
@@ -87,6 +89,18 @@ class RelatorioDriver implements DriverInterface
]);
throw $e;
+ } catch (Throwable $exception) {
+ $this
+ ->logger
+ ->error(
+ 'relatorio: Unable to get content from response.',
+ [
+ 'msg' => $exception->getMessage(),
+ 'e' => $exception->getTraceAsString(),
+ ]
+ );
+
+ throw $exception;
}
}
}
diff --git a/src/Bundle/ChillDocGeneratorBundle/composer.json b/src/Bundle/ChillDocGeneratorBundle/composer.json
index 4ae119636..42939bd2b 100644
--- a/src/Bundle/ChillDocGeneratorBundle/composer.json
+++ b/src/Bundle/ChillDocGeneratorBundle/composer.json
@@ -18,6 +18,7 @@
}
],
"require": {
+ "spomky-labs/base64url": "^2"
},
"require-dev": {
},
diff --git a/src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php b/src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php
index ae8db17f6..41991b1bb 100644
--- a/src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php
+++ b/src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php
@@ -155,7 +155,7 @@ class DocumentAccompanyingCourseController extends AbstractController
$documents = $this->courseRepository
->findBy(
['course' => $course],
- ['date' => 'DESC'],
+ ['date' => 'DESC', 'id' => 'DESC'],
$pagination->getItemsPerPage(),
$pagination->getCurrentPageFirstItemNumber()
);
diff --git a/src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php b/src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
index 42e49ad3c..062ac3e4a 100644
--- a/src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
+++ b/src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
@@ -178,7 +178,7 @@ class DocumentPersonController extends AbstractController
$documents = $this->personDocumentACLAwareRepository->findByPerson(
$person,
- [],
+ ['date' => 'DESC', 'id' => 'DESC'],
$pagination->getItemsPerPage(),
$pagination->getCurrentPageFirstItemNumber()
);
diff --git a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
index 78387e7fe..56380ec74 100644
--- a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
+++ b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php
@@ -109,12 +109,12 @@ class StoredObject implements AsyncFileInterface, Document
return $this->id;
}
- public function getIv()
+ public function getIv(): array
{
return $this->iv;
}
- public function getKeyInfos()
+ public function getKeyInfos(): array
{
return $this->keyInfos;
}
@@ -149,37 +149,37 @@ class StoredObject implements AsyncFileInterface, Document
return $this;
}
- public function setDatas(array $datas)
+ public function setDatas(?array $datas)
{
- $this->datas = $datas;
+ $this->datas = (array) $datas;
return $this;
}
- public function setFilename($filename)
+ public function setFilename(?string $filename)
{
- $this->filename = $filename;
+ $this->filename = (string) $filename;
return $this;
}
- public function setIv($iv)
+ public function setIv(?array $iv)
{
- $this->iv = $iv;
+ $this->iv = (array) $iv;
return $this;
}
- public function setKeyInfos($keyInfos)
+ public function setKeyInfos(?array $keyInfos)
{
- $this->keyInfos = $keyInfos;
+ $this->keyInfos = (array) $keyInfos;
return $this;
}
- public function setType($type)
+ public function setType(?string $type)
{
- $this->type = $type;
+ $this->type = (string) $type;
return $this;
}
diff --git a/src/Bundle/ChillDocStoreBundle/Exception/StoredObjectManagerException.php b/src/Bundle/ChillDocStoreBundle/Exception/StoredObjectManagerException.php
new file mode 100644
index 000000000..b5d0bf679
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Exception/StoredObjectManagerException.php
@@ -0,0 +1,40 @@
+addACL($qb, $person);
- foreach ($orderBy as [$field, $order]) {
+ foreach ($orderBy as $field => $order) {
$qb->addOrderBy($field, $order);
}
diff --git a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/new.html.twig b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/new.html.twig
index 94cfb8828..01be1a5d7 100644
--- a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/new.html.twig
+++ b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/new.html.twig
@@ -30,7 +30,7 @@
{% if c.full_content is defined and c.full_content == true %}
- {{ c.notification.message|chill_markdown_to_html }}
+ {% if c.notification.message is not empty %}
+ {{ c.notification.message|chill_markdown_to_html }}
+ {% else %}
+
- {% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) %}
- {% if notif_counter.total > 0 %}
-
- {{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) }}
+
+ {% if show_pinned_comment|default(false) and period.pinnedComment is not empty%}
+
+ {% if itemMeta is not defined %}
+ {% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) %}
+ {% if notif_counter.total > 0 %}
+
+ {% set participating = false %}
+ {% for part in acp.currentParticipations %}
+ {% if part.person.id != person.id %}
+ {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
+ targetEntity: { name: 'person', id: part.person.id },
+ action: 'show',
+ displayBadge: true,
+ buttonText: part.person|chill_entity_render_string,
+ isDead: part.person.deathdate is not null
+ } %}
+ {% else %}
+ {% set participating = true %}
+ {% endif %}
+ {% endfor %}
+ {% if participating %}
+ {{ 'person.and_himself'|trans({'gender': person.gender}) }}
+ {% endif %}
+
+
+ {% endif %}
+
+ {% if acp.requestoranonymous == false %}
+ {% if (acp.requestorPerson is not null and acp.requestorPerson.id != person.id) or acp.requestorThirdParty is not null %}
+
+
+
+ {% if acp.requestorPerson is not null %}
+ {{ 'Requestor'|trans({'gender': acp.requestorPerson.gender}) }}
+ {% else %}
+ {{ 'Requestor'|trans({'gender': 'other'})}}
+ {% endif %}
+
+
+
+ {% if acp.requestorThirdParty is not null %}
+ {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
+ targetEntity: { name: 'thirdparty', id: acp.requestorThirdParty.id },
+ action: 'show',
+ displayBadge: true,
+ buttonText: acp.requestorThirdParty|chill_entity_render_string
+ } %}
+ {% else %}
+ {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
+ targetEntity: { name: 'person', id: acp.requestorPerson.id },
+ action: 'show',
+ displayBadge: true,
+ buttonText: acp.requestorPerson|chill_entity_render_string,
+ isDead: acp.requestorPerson.deathdate is not null
+ } %}
+ {% endif %}
+
- {% set participating = false %}
- {% for part in acp.currentParticipations %}
- {% if part.person.id != person.id %}
- {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
- targetEntity: { name: 'person', id: part.person.id },
- action: 'show',
- displayBadge: true,
- buttonText: part.person|chill_entity_render_string,
- isDead: part.person.deathdate is not null
- } %}
- {% else %}
- {% set participating = true %}
- {% endif %}
- {% endfor %}
- {% if participating %}
- {{ 'person.and_himself'|trans({'gender': person.gender}) }}
- {% endif %}
-
-
- {% endif %}
-
- {% if acp.requestoranonymous == false %}
- {% if (acp.requestorPerson is not null and acp.requestorPerson.id != person.id) or acp.requestorThirdParty is not null %}
-
-
-
- {% if acp.requestorPerson is not null %}
- {{ 'Requestor'|trans({'gender': acp.requestorPerson.gender}) }}
- {% else %}
- {{ 'Requestor'|trans({'gender': 'other'})}}
- {% endif %}
-
-
-
- {% if acp.requestorThirdParty is not null %}
- {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
- targetEntity: { name: 'thirdparty', id: acp.requestorThirdParty.id },
- action: 'show',
- displayBadge: true,
- buttonText: acp.requestorThirdParty|chill_entity_render_string
- } %}
- {% else %}
- {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
- targetEntity: { name: 'person', id: acp.requestorPerson.id },
- action: 'show',
- displayBadge: true,
- buttonText: acp.requestorPerson|chill_entity_render_string,
- isDead: acp.requestorPerson.deathdate is not null
- } %}
- {% endif %}
-
- {% endif %}
- {% if evaluation.warningInterval and evaluation.warningInterval.d > 0 %}
-
- {% set days = (evaluation.warningInterval.d + evaluation.warningInterval.m * 30) %}
- {{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}
- {{ 'accompanying_course_work.%days% days before max_date'|trans({'%days%': days }) }}
-
- {% endif %}
-
- {% if evaluation.createdBy is not null %}
- créé par
- {{ evaluation.createdBy.username }}
- {% endif %}
- {% if evaluation.createdAt is not null %}
- {{ 'le'|trans }}
- {{ evaluation.createdAt|format_date('short') }}
- {% endif %}
-
-
- {% if evaluation.comment %}
-
- {{ evaluation.comment }}
-
- {% endif %}
+ {% if evaluation.accompanyingPeriodWork.endDate %}
+
+ {% endif %}
+ {% if evaluation.warningInterval and evaluation.warningInterval.d > 0 %}
+
+ {% set days = (evaluation.warningInterval.d + evaluation.warningInterval.m * 30) %}
+ {{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}
+ {{ 'accompanying_course_work.%days% days before max_date'|trans({'%days%': days }) }}
+
+ {% endif %}
+
+ {% if evaluation.createdBy is not null %}
+ créé par
+ {{ evaluation.createdBy.username }}
+ {% endif %}
+ {% if evaluation.createdAt is not null %}
+ {{ 'le'|trans }}
+ {{ evaluation.createdAt|format_date('short') }}
+ {% endif %}
+
-{% if display_action is defined and display_action == true %}
- {% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', evaluation.accompanyingPeriodWork) %}
-
- {% if chill_document_is_editable(doc.storedObject) %}
+ {% if display_action is defined and display_action == true %}
+ {% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', evaluation.accompanyingPeriodWork) %}
+