diff --git a/.changes/header.tpl.md b/.changes/header.tpl.md new file mode 100644 index 000000000..df8faa7b2 --- /dev/null +++ b/.changes/header.tpl.md @@ -0,0 +1,6 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), +and is generated by [Changie](https://github.com/miniscruff/changie). diff --git a/.changes/unreleased/.gitkeep b/.changes/unreleased/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.changes/v2.0.0.md b/.changes/v2.0.0.md new file mode 100644 index 000000000..8d70ecba6 --- /dev/null +++ b/.changes/v2.0.0.md @@ -0,0 +1,677 @@ +## 2.0.0 + +* this is a release to relaunch our proceess of release with semantic versioning + +## Test releases + +### 2.0.0-beta3 + +* [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person +* [activity][export] Feature: improve label for aliases in "Filter by activity type" +* [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository +* [person][export] Fixed: some inconsistency with date filter on accompanying courses +* [person][export] Fixed: use left join for related entities in accompanying course aggregators +* [workflow] Feature: allow user to copy and send manually the access link for the workflow +* [workflow] Feature: show the email addresses that received an access link for the workflow +### 2.0.0-beta2 + +* [workflow]: Fixed: the notification is sent when the user is added to the first step. +* [budget] Feature: allow to desactivate some charges and resources, adding an `active` key in the configuration +* [person] Feature: on Evaluation, allow to configure an URL from the admin + +### 2022-06 + +* [workflow]: added pagination to workflow list page +* [homepage_widget]: null error on tasks widget fixed +* [person-thirdparty]: fix quick-add of names that consist of multiple parts (eg. De Vlieger) within onthefly modal person/thirdparty +* [search]: Order of birthdate fields changed in advanced search to avoid confusion. +* [workflow]: Constraint added to workflow (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/675) +* [social_action]: only show active objectives (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/625) +* [household]: Reposition and cut button for enfant hors menage have been deleted (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/620) +* [admin]: Add crud for composition type in admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/611) +* [social_action]: only show active objectives (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/625) + +## Test releases + +### 2022-05-30 + +* fix creating a new AccompanyingPeriodWorkEvaluationDocument when replacing the document (the workflow was lost) + +### 2022-05-27 + +* [storedobject] add title field on StoredObject entity + use it in activity documents (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/604) +* [main] add a "read more..." on comment embeddable when overflown (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/604) +* [person] add closing motive to closed acc course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/603) +* [person] household filiation: fetch person info when unfolding person (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/586) +* [admin] repair edit of social action in the admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/601) +* [admin]: add select2 to Goal form type entity fields (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/702) +* [main] allow hide permissions group list menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/577) +* [main] allow hide change user password menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/577) +* [main] filter user jobs by active jobs (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/577) +* [main] add civility to User (entity, migration and form type) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/577) +* [admin] refactorisation of the admin section: reorganisation of the menu, translations, form types, new entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/592) +* [admin] add admin section for languages and countries (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/596) +* [activity] activity admin: translations + remove label field for comment on admin activity type (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/587) +* [main] admin user_job: improvements (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/588) +* [address] can add extra address info even if noAddress (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/576) + + +### 2022-05-06 + +* [person] add civility when creating a person (with the on-the-fly component or in the php form) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/557) +* [person] add address when creating a person (with the on-the-fly component or in the php form) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/557) +* [person] add household creation API point (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/557) + +### 2021-04-29 + +* [person] prevent circular references in PersonDocGenNormalizer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/527) +* [person] add maritalStatusComment to PersonDocGenNormalizer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/582) +* Load relationships without gender in french fixtures +* Add command to remove old draft accompanying periods +* [parcours]: If users assings him/herself as referrer and job is not null. Update parcours job (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/578) + +### 2021-04-28 + +* [address] fix bug when editing address: update location and addressreferenceId + better update of the map in edition (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/593) +* [main] avoid address reference search on undefined post code (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/561) +* [person] prevent duplicate relationship in filiation/household graph (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/560) +* [Documents] Validate storedObject and allow for null data (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/565) +* [parcours]: Comments can be unpinned + edit/delete for all users that are allowed to edit parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/566) + +### 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 +* [evaluation_document] changing date to datetime in order to display the time at which document was created (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/569) + + +### 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 + + +### continuous release in February and March + +* Creation of PickCivilityType, and implementation in PersonType and ThirdpartyType +* [person] Accompanying course evaluation documents: disable the WOPI edit link if mimetype not supported and if no keyInfos +(https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/585) +* [activity] display error messages above the form in creating a new location (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/481) +* [activity] show required field in activity edit/new by an asterix in the vuejs fields (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/494) +* [ACL] fix allow to see the course, event if the scope'course does not contains the scope's user +* [search] enforce limit of results for fetching rsults by search api https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/576 +* [activity] Fix delete button for document (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/554) +* [activity] Add return path the document generation (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/553) +* [person] add person ressource to person docgen normaliser (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/517) +* [person] AccompanyingCourseWorkEdit: fix deleting evaluation documents (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/546) +* [person] AccompanyingCourseWorkEdit: download existing documents (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/512) +* [person] AccompanyingCourseWorkEdit: replace document by a new one (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/511) +* [person] AccompanyingPeriodWork: add referrers to work, add doctrine event listener to add logged user to referrers collection and display a referrers list in work list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/502) +* [person] AccompanyingPeriodWorkEvaluation: fix circular reference when serialising (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/495) +* [person] order accompanying period by opening date in search persons, person and household period lists (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/493) +* [parcours] autosave of the pinned comment for draft accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/477) +* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472) +* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472) +* [person] Add url in accompanying period work evaluations entity and form (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/476) +* [person] Add document generation in admin and in person/{id}/document (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/464) +* [activity] do not override location if already exist (when validating new activity) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/470) +* [parcours] Toggle emergency/intensity only by referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442) +* [docstore] Add an API entrypoint for StoredObject (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466) +* [person] Add the possibility of uploading existing documents to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466) +* [person] Add title to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466) +* [person] Order social issues by the field "ordering" (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/388) +* [Person/Household list] when listing other simultaneous members of an household, exclude the members on person, not on members (avoid to show two membersship with the same person) +* [draft periods] add a delete button (if acl granted) on each draft period listed on draft period page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/463) +* [Person] Display suffixText in RenderPerson, PersonText.vue, RenderPersonBox.vue (was made for displaying "enfant confie") (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/441) +* [budget]: budget enabled for persons and households (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/469) +* [person] residential address: show residential address or info in PersonRenderBox, refactor Residential Address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/439) +* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345) +* [documents] Improve flex-table item-col placement when long buttons and long metadata +* [thirdparty] Fix display of multiple contact badges so they wrap onto next line (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/482) +* [confidential] Fix position of toggle button so it does not cover text nor fall outside of box (no issue) +* [parcours] Fix edit of both thirdparty and contact name (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/474) +* [template] do not list inactive templates (for doc generator) +* [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) +* [thirdparty] Add parameter condition to display centers or not (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/500) +* [phonenumber] Remove placeholder in phonenumber field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/496) +* [person_resource] separate create page created to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/504) +* [contact] add contact button color changed plus the pipe at the side removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/506) +* [thirdparty] For contacts show current civility/profession in edit form + fix saving of edited information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/491) +* [household] create-edit household composition placed in separate page to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/505) +* [blur] Improved positioning of toggle icon (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/486) +* [thirdparty] add firstname field to thirdparty 'child' or 'contact' types (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/508) +* [household] create-edit household composition placed in separate page to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/505) +* [blur] Improved positioning of toggle icon (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/486) +* [parcours] List of parcours for a specific user so they can be reassigned in case of absence (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/509) +* [thirdparty] Thirdparty view page, english text translated (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/534) +* [social_action] Translation changed in evaluation section (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/512) +* [filiation] Possible to add person (or create onthefly) to add to filiation graph + add relation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/519) +* [household] Within parcours listing page of household add create button (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/560) +* [person_resource] bugfix when adding thirdparty or freetext resource + prevent personOwner themselves to be added. (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/526) +* [aside_activity] style correction + sticky-form create button (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/529) +* [budget] order within the menu adjusted (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/592) +* [onthefly] fix create person. Bug was noticed in filiation (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/591) +* [parcours] Create document buttons made sticky (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/532) +* [person] Trailing guillemet removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/530) +* [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) +* [documents] 'gabarit' changed to 'modèle' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/405) +* [person_resources] Menu name and order changed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/460) +* workflow: fix sending notifications +* [thirdparty] Extend the thirdparty search to thirdparty children (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/448) +* [person]: AddPersons: allow creation of person or thirdparty only (no users) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422) +* [person]: AddPersons: allow creation of person or thirdparty depending on allowed types (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422) +* [person]: AddPersons: add suggestion of name when creating new person or thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422) +* [main] Address: fix small bug: when modifying an address without street (isNoAddress), also check errors if street is an empty string as back-end change null value to empty string for street (and streetNumber) +* [main] Address: stronger client-side validation of addresses (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/449) +* [person] accompanying course: filter suggested entities by open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/415) +[activity] can click through the cross icon for removing person in concerned group (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476) +[activity] correct associated persons by considering only open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476) +* [person_resources]: Renderboxes used to display person/thirdparty info (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/465) +* [Household]: Add end date in HouseholdMember form for 'enfant hors menage' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/434) +* [homepage_widget]: If no sender then display as 'notification automatique' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/435) +* [parcours]: Order social activities and only display most recent three in parcours resumé (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/481) +* [3party]: 3party: redirect to parent when contact (child) is opened in view page +* [parcours / addresses]: launch an event when a person change address (either through changing household or because the household is associated to a new address). If the person is localising a course, the course location go back to a temporarily address. +* [thirdparty]: address/phonenumber/email/fonction displayed in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/401) +* [thirdparty_contact]: in search results the 'qualité' is displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/465) +* [bug]: fix confidential toggle of address in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/460) + + + +### test release 2022-02-14 + +* AddPersons: remove ul-li html tags from AddPersons (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/419) +* [doc-generator] do not set required fields for mainPerson, person1, person2 (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement#456) +* [doc-generation] add age and obele in the mainPerson, person1 and person2 list + add obele in person renderString if addAge (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/370) +* [person] accompanying course work: fix on-the-fly update of thirdParty +* fix normalisation of accompanying course requestor api (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/378) +* [person] add a returnPath when clicking on some Person or ThirdParty badge (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/427) +* [person] accompanying course work: fix on-the-fly update of thirdParty +* [on-the-fly] close modal only after validation +* [person] correct thirdparty PATCH url + add email and altnames in AddPerson and serializer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/433) +* change order for accompanying course work list +* [parcours]: Mes parcours brouillon added to user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/440) +* [Documents]: List view adapted to display more information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/414) +* [person]: style fix in parcours listing per person. (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/432) +* [parcours]: Only the referrer can toggle the intensity of the parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442) +* [household]: display address of current household (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/415) +* ajoute un ordre dans les localisation (api) +* [pick entity]: fix translations in modal (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/419) +* [homepage_widget]: fix translation on emergency badge (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/440) +* [person]: create person and household added to button dropdown (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/454) +* display full address in address.text in normalization. Adapt AddressRenderBox +* [address]: Correction residential address 'depuis le' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/459) +* [Documents]: List view adapted to display more information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/414) +* [Thirdparty_contact]: address blurred if confidential in view page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/450) +* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345) + + +### test release 2021-02-01 + +* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche +* renomme date de début en date d'ouverture dans le formulaire parcours +* [homepage widget] improve content tables, improve counter pluralization with style on number +* [notification lists] add comments counter information +* [workflows] fix popover header with previous transition +* [parcours]: validation + message for closing parcours adjusted. +* [household]: household composition double edit button replaced by a delete action (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/426) +[fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413 +[homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc. +* [person]: Comment on marital status is possible even if marital status is not defined (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/421) +* [parcours]: In the list of person results the requestor is not displayed if defined as anonymous (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/424) +* [bugfix]: modal closes and newly created person/thirdparty is selected when multiple persons/thirdparties are created through the modal (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/429) +* [person_resource]: Onthefly button added to view person/thirdparty and badge differentiation for a contact-thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/428) +* [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal +* [AddAddress] disable multiselect search, and rely only on most pertinent Cities and Street computed backend +* [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413 +* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc. +* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345) +* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc. + + +### test release 2021-01-31 + +* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409) +* [person] accompanying course: close modal when edit participation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420) +* [person] accompanying course: treat validation error when editing on-the-fly entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420) +* [activity] show activity attendee (présence) in the activity list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/412) +* [activity] admin: change validation rule for social action visible field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/413) +* [parcours]: component added to change the opening date of a parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/411) +* [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410) +* [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408) +* [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389) +* [household member editor] allow to push to existing household + + +### test release 2021-01-28 + +* [person] improve filiations vis graph: disable physics, use chill colors for persons-households-course, increase label of relations, remove labels on household arrows and other improvements (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/286, https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/362) +* [activity] Order activity by date and by id (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/364) +* [main] increase length of 4 Address fields (change to TEXT, no size limits) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/277) +* [main] Add confidential option for address, in edit and view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/165) +* [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) +* [person] Add residential address entity, form and list for each person +* [aside_activity]: dynamicUserPickerType used (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/399) +* dispatching list + + +### test release 2021-01-26 + +* [parcours] comments truncated if too long + link added (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/406) +* [person]: possibility to add person resources (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/382) +* [person ressources]: module added + + +### 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 +* [main] location form type: fix unmapped address field (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/246) +* [activity] fix wrong import of js assets for adding and viewing documents in activity (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/83 & https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/176) +* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380) +* [forms] dynamic picker types for user/person/thirdparty types created (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/386) + +### test release 2022-01-17 + +* [main] Add editableByUser field to locationType entity, adapt the admin template and add this condition in the location-type endpoint (see https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/297) +* [main] Add mainLocation field to User entity and add it in user form type +* rewrite page which allow to select activity +* [main] Add mainLocation field to User entity and add it in user form type +* [course list in person context] show full username/label for ref +* [accompanying period work] remove the possibility to generate document from an accompanying period work +* vuejs: add validation on required fields for AddPerson, Address and Location components +* vuejs: treat 422 validation errors in locations and AddPerson components +* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380) + +## Test releases +* vuejs: add validation on required fields for AddPerson, Address and Location components +* vuejs: treat 422 validation errors in locations and AddPerson components +* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380) + +### test release 2022-01-12 + +* fix thirdparty normalizer on telephone field: https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/322 + +### test release 2022-01-11 + +* vuejs: translate in French all multiselect widgets +* [address] define address lines according postal standards for France and Belgium (default) and change AddressRender, chill_entity_render_box and AddressRenderBox.vue +* [household] change translations (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109) +* [household] add address i18n in household component (champs-libres/departement-de-la-vendee/accent-suivi-developpement#158) +* [household] add on the fly i18n in household component +* [household] redirect to the household page when a household is created from a person (champs-libres/departement-de-la-vendee/accent-suivi-developpement#175) +* [household] household member editor: display alert if some members have already an household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#172) +* [household] household member editor: do not add in new members if the member is included in the members of household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#123) +* [household] household member editor: remove markNoAddress button (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109) +* [person]: ordering fields in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61) +* [person]: Add email and alt names in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61) +* [accompanyingCourse] Add a delete action and delete buttons to delete a accompanying course when step = DRAFT (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/64) +* [accompanyingCourse] Add a administrative location in the accompanying course, set the user current location as default, allow to select a location in a select field and do not allow to confirm the accompanying course if location is empty. +* [accompanyingCourse] Add the administrative location in the available variables for document generation +* AddAddress: optimize loading: wait for the user finish typing; +* UserPicker: fix bug with deprecated role +* docgen: add base context + tests +* docgen: add age for person +* [household menu] fix filiation order https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/265 +* [AddAddress]: optimize loading: wait for the user finish typing; +* [UserPicker]: fix bug with deprecated role +* [docgen]: add base context + tests +* [docgen]: add age for person +* [task]: fix dropdown menu style + fix bug in singleTaskController (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/338) +* Household: fix bug when moving person on the same day (see https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/281) +* Household: show date validFrom and validTo when moving +* address reference: add index for refid +* [accompanyingCourse_work] fix styles conflicts + fix bug with remove goal (remove goals one at a time) +* [accompanyingCourse] improve masonry on resume page, add origin +* [notification] new notification interface, can be associated to AccompanyingCourse/Period, Activities. + * List notifications, show, and comment in User section + * Notify button and contextual notification box on associated objects pages +* [accompanyingCourse] add a comment for each resource associated. A modal allow to save comment. Comment is displayed in on-the-fly show modal of the accompanyingCourse context (edit page + resume page). + +### test release 2021-12-14 + +* [asideactivity] creation of aside activity category fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/262) +* [vendee/person] fix typo "situation professionelle" => "situation professionnelle" +* [main] add availableForUsers condition from locationType in the location API endpoint (champs-libres/departement-de-la-vendee/accent-suivi-developpement#248) +* [main] add the current location of the user as API point + add it in the activity location list (champs-libres/departement-de-la-vendee/accent-suivi-developpement#247) +* [activity] improve show/new/edit templates, fix SEE and SEE_DETAILS acl +* [badges] create specific badge for TMS, and make person/thirdparty badges clickable with on-the-fly modal in : + * concerned groups items (activity, calendar) + * accompanyingCourseWork lists + * accompanyingCourse lists +* [acompanyingCourse] add initial comment on Resume page +* [person] create button full width (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/330) + +### test release 2021-12-11 + +* [main] add order field to civility +* [main] change address format in case the country is France, in Address render box and address normalizer +* [person] add validator for accompanying period with a test on social issues (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/76) +* [activity] fix visibility for location +* [origin] fix origin: use correctly the translatable strings + * /!\ everyone must update the origin table. As there is only one row, execute `update chill_person_accompanying_period_origin set label = jsonb_build_object('fr', 'appel téléphonique');` +* [person] redirect bug fixed. +* [action] add an unrelated issue within action creation. +* [origin] fix origin: use correctly the translatable strings + * /!\ everyone must update the origin table. As there is only one row, execute `update chill_person_accompanying_period_origin set label = jsonb_build_object('fr', 'appel téléphonique');` +* [main] change order of civilities in civility fixtures (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] set min attr in the minimum of children field (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] add marital status date in person view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] show number of children + allow set number of children to null (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] show acceptSMS option (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] add death information in person render box in twig and vue render boxes (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [asideactivity] creation of aside activity category fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/262) +* [vendee/person] fix typo "situation professionelle" => "situation professionnelle" +* [accompanyingcourse_work] Changes in layout/behavior of edit form (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/321) +* [badge-entity] design coherency between pills badge-person and 3 kinds of badge-thirdparty +* [AddPersons] suggestions row are clickable, not only checkbox + +### test release 2021-12-06 + +* [main] address: use search API end points for getting postal code and reference address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316) +* [main] address: in edit mode, select the encoded values in multiselect for address reference and city (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316) +* [person search] fix bug when using birthdate after and birthdate before +* [person search] increase pertinence when lastname begins with search pattern +* [activity/actions] Améliore la cohérence du design entre + * la page résumé d'un parcours (liste d'actions récentes et liste d'activités récentes) + * la page liste des actions + * la page liste des activités (contexte personne / contexte parcours) +* [household] field to edit wheter person is titulaire of household or not removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/322) +* [activity] create work if a work with same social action is not associated to the activity +* [visgraph] improve and fix bugs on vis-network relationship graph +* [bugfix] posting of birth- and deathdate through api fixed. +* [suggestions] improve suggestions lists + +### Test release 2021-11-19 - bis + +* [household] do not allow to create two addresses on the same date +* [activity] handle case when there is no social action associated to social issue +* [activity] layout for issues / actions +* [activity][bugfix] in edit mode, the form will now load the social action list + + +### Test release 2021-11-29 + +* [person] suggest entities (person | thirdparty) when creating/editing the accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/119) +* [activity] add custom validation on the Activity class, based on what is required from the ActivityType (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/188) +* [main] translate multiselect messages when selecting/creating address +* [main] set the coordinates of the city when creating a new address OR choosing "pas d'adresse complète" +* Use the user.label in accompanying course banner, instead of username; +* fix: show validation message when closing accompanying course; +* [thirdparty] link from modal to thirdparty detail page fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/228) +* [assets] new asset to style suggestions lists (with add/remove item link) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/258) +* [accompanyingCourseWorkEdit] improves hyphenation and line breaks for long badges +* [acompanyingCourse] improve Resume page + * complete all needed informations, + * actions and activities are clickables, + * better placement with js masonry blocks on top of content area, + * https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/101 + * https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/295 +* [activity/calendar] on show page, concerned groups of persons table adapt itself to isVisibles options +* [activity] remove the "plus" button in activity list +* [activity] check ACL on activity list in person context +* [list for accompanying course in person] filter list using ACL +* [validation] toasts are displayed for errors when modifying accompanying course (generalization required). +* [period] only the user can enable confidentiality +* add an endpoint for checking permissions. See https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/232 +* [activity] for a new activity: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties +* [calendar] for a new rdv: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties +* [period] Validation added when period is confidential and confirmed -> user cannot be null. + + +## Test releases + +### Test release 2021-11-22 + +* [activity] delete admin_user_show in twig template because this route is not defined and should be defined +* [activity] suggest requestor, user and ressources for adding persons|user|3rdparty +* [calendar] suggest persons, professionals and invites for adding persons|3rdparty|user +* [activity] take into account the restrictions on person|thirdparties|users visibilities defined in ActivityType +* [main] Add currentLocation to the User entity + add a page for selecting this location + add in the user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133) +* [activity] add user current location as default location for a new activity (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133) +* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167) +* remove "search by phone configuration option": search by phone is now executed by default +* remplacer le classement par ordre alphabétique par un classement par ordre de pertinence, qui tient compte: + * de la présence d'une string avec le nom de la ville; + * de la similarité; + * du fait que la recherche commence par une partie du mot recherché +* ajouter la recherche par numéro de téléphone directement dans la barre de recherche et dans le formulaire recherche avancée; +* ajouter la recherche par date de naissance directement dans la barre de recherche; +* ajouter la recherche par ville dans la recherche avancée +* ajouter un lien vers le ménage dans les résultats de recherche +* ajouter l'id du parcours dans les résultats de recherche +* ajouter le demandeur dans les résultats de recherche +* ajout d'un bouton "recherche avancée" sur la page d'accueil +* [person] create an accompanying course: add client-side validation if no origin (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/210) +* [person] fix bounds for computing current person address: the new address appears immediatly +* [docgen] create a normalizer and serializer for normalization on doc format +* [person normalization] the key center is now "centers" and is an array. Empty array if no center +* [accompanyingCourse] Ability to close accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/296) +* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167) +* [list result] show all courses, except ones with period closed +* [accompanyingCourse] improve banner with small carousel to display slide social-issues or slide associated persons (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/69) + +### Test release 2021-11-15 + +* [main] fix adding multiple AddresseDeRelais (combine PickAddressType with ChillCollection) +* [person]: do not suggest the current household of the person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/51) +* [person]: display other phone numbers in view + add message in case no others phone numbers (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/184) +* unnecessary whitespace removed from person banner after person-id + double parentheses removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/290) +* [person]: delete accompanying period work, including related objects (cascade) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/36) +* [address]: Display of incomplete address adjusted. +* [household]: improve relationship graph + * add form to create/edit/delete relationship link, + * improve graph refresh mechanism + * add feature to export canvas as image (png) +* [person suggest] In widget "add person", improve the pertinence of persons when one of the names starts with the pattern; +* [person] do not ask for center any more on person creation +* [3party] do not ask for center any more on 3party creation + +## Test releases + +### Test release 2021-11-08 + +* [person]: Display the name of a user when searching after a User (TMS) +* [person]: Add civility to the person +* [person]: Various improvements on the edit person form +* [person]: Set available_languages and available_countries as parameters for use in the edit person form +* [activity] Bugfix: documents can now be added to an activity. +* [tasks] improve tasks with filter order +* [tasks] refactor singleControllerTasks: limit the number of conditions from the context +* [validations] validation of accompanying period added: no duplicate participations or resources (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/60). +* [renderbox] If gender of person is not defined, no icon is displayed instead of neuter-icon (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/129). +* [confidential information] module added to blur confidential information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/248). +* refactor `AuthorizationHelper` and `UserACLAwareRepository` to fix constructor, and separate logic for parent role helper into `ParentRoleHelper` +* [main]: filter location and locationType in backend: exclude NULL names, only active and availableToUsers +* [activity]: perform client-side validation & show/hide fields in the "new location" modal +* [person]: normalize person with CenterResolverDispatcher and handle case where center is null or multiple in PersonRenderBox +* [docstore] voter for PersonDocument and AccompanyingCourseDocument on the 2.0 way (using VoterHelperFactory) +* [docstore] add authorization check inside controller and menu +* [activity]: fix inheritance for role `ACTIVITY FULL` and add missing acl in menu +* [person] show current address in search results +* [person] show alt names in search results +* [admin]: links to activity admin section added again. +* [household]: endDate field deleted from household edit form. +* [household]: View accompanying periods of current and old household members. +* [tasks]: different layout for task list / my tasks, and fix link to tasks in alert or in warning +* [admin]: links to activity admin section added again. +* [household]: household addresses ordered by ValidFrom date and by id to show the last created address on top. +* [socialWorkAction]: display of social issue and parent issues + banner context added. +* [DBAL dependencies] Upgrade to DBAL 3.1 + +### Test release 2021-10-27 + +* [person]: delete double actions buttons on search person page +* [person]: accompanying course work: remove creation date display the list of work + handle case when end date is null +* [main]: Add new pages with a menu for managing location and location type in the admin +* [main]: Add some fixtures for location type +* [calendar]: Pass the location when transforming a calendar item (rdv) into an activity +* [calendar]: Add a user menu for "my calendar" + +### Test release 2021-10-18 + +* [3party]: french translation of contact and company +* [3party]: show parent in list +* [3party]: change color for badge "child" +* [3party]: fix address creation +* [household members editor] finalisation of editor +* [AccompanyingCourse banner]: replace translation referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/70) +* [Location]: add location system in activity and RV (calendar). User can choose in location list or create a new location. +* [household]: add relationship page with dynamic data visualisation graph + +## Test releases + +### Test release 2021-10-11 + +* Address: zoom on postal code geometry + fix origin of manually entered postal code + +* in the Address vue component, order the postal code and street address by alphabetic and numeric order + +* add 3 new fields to PostalCode and adapt postal code command and fixtures + +* [Aside activity] Fixes for aside activity + + * categories with child + * fast creation buttons + * add ordering for types + +* [AccompanyingCourse Resume page] dashboard for AccompanyingCourseWork and for Activities; +* Improve badges behaviour with small screens; + +* [ThirdParty]: + + * third party list + * create a kind contact/institution when create a new thirdparty, and set contact embedded as kind=child; + * filter thirdparties in list + +* [FilterOrder]: add development kit for generating filter and ordering in list +* [Capitalization of names] person names are capitalized on creation, on prePersist event +* [On-The-Fly] modale works for showing, editing and creating person or thirdparty ; +* [AccompanyingCourse Resume page] associated persons list, can see household when hover, and with show on-the-fly modale when clicking person ; + +### test release 2021-10-04 + +* [Household editor][UI] Update how household suggestion and addresses are picked; + + See https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/80 +* [AddAddress] Handle address suggestion; +* [CenterType][Create a person] when overriding the ACL rules, allow to show a PickCenterType + when no centers are reachable by the default ACL. +* [Household] Show comment event if no address are associated with the household; +* [Person results] Add requestor into search results: + + * a badge "requestor" is shown into search results; + * periods where the person is only requestor (without participating) are also shown; + + Issues: + + * https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/13 + * https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/199 +* [Person form] "accept sms" not required: + + https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/37 + https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/221 + +* [Household editor] suggest only temporarily addresses; + See https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/82 +* On-The-Fly modale works for showing, editing and creating person and thirdparty ; +* AccompanyingCourse Resume page: list associated persons by household, see household when hover, and show on-the-fly modale when clicking on person ; +* [AddAddress] Handle address suggestion; +* [AddAddress][Entity address]: add a link between address and address reference; +* [Household editor] suggest household by comparing the temporary addresses from courses; + + See https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/81 +* On-The-Fly modale works for showing, editing and creating person and thirdparty + + +## Test released + + + +## Stable releases + +No stable releases for v2+ + diff --git a/.changes/v2.1.0.md b/.changes/v2.1.0.md new file mode 100644 index 000000000..ade83aee0 --- /dev/null +++ b/.changes/v2.1.0.md @@ -0,0 +1,17 @@ +## v2.1.0 - 2023-06-12 + +### Feature + +* [docgen] allow to pick a third party when generating a document in context Activity, AccompanyingPeriod + +### Fixed + +* ([#111](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/111)) List of "my accompanying periods": separate the active and closed periods in two different lists, and show the inactive_long and inactive_short periods + +### Security + +* ([#105](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/105)) Rights are checked for display of 'accompanying period' tab in household menu. Rights are also checked for creation of 'accompanying period' from within household context + +### DX + +* Add methods to RegroupmentRepository and fullfill Center / Regroupment Doctrine mapping diff --git a/.changes/v2.2.0.md b/.changes/v2.2.0.md new file mode 100644 index 000000000..5371cad35 --- /dev/null +++ b/.changes/v2.2.0.md @@ -0,0 +1,12 @@ +## v2.2.0 - 2023-06-18 +### Feature +* When navigating from a workflow regarding to an evaluation's document to an accompanying course, scroll directly to the document, and blink to highlight this document +* Add notification to accompanying period work and work's evaluation's documents +* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113))[Export] Filter accompanying period by step at date: allow to pick multiple steps +* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113))[export] add a filter on accompanying period: filter by step between two dates +### Fixed +* use the correct annotation for the association between PersonCurrentCenter and Person +* ([#58](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/58))Fix birthdate timezone in PersonRenderBox +* ([#55](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/55))Fix the notification counter +### DX +* DQL function OVERLAPSI: simplify expression in postgresql diff --git a/.changie.yaml b/.changie.yaml new file mode 100644 index 000000000..83fd9770a --- /dev/null +++ b/.changie.yaml @@ -0,0 +1,34 @@ +changesDir: .changes +unreleasedDir: unreleased +headerPath: header.tpl.md +changelogPath: CHANGELOG.md +versionExt: md +versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}' +kindFormat: '### {{.Kind}}' +changeFormat: >- + * {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{- end }}{{.Body}} +custom: + - key: Issue + label: Issue number (on chill-bundles repository) (optional) + optional: true + type: int + minInt: 1 +body: + # allow multiline messages + block: true +kinds: + - label: Feature + auto: minor + - label: Deprecated + auto: minor + - label: Fixed + auto: patch + - label: Security + auto: patch + - label: DX + auto: patch +newlines: + afterChangelogHeader: 1 + beforeChangelogVersion: 1 + endOfVersion: 1 +envPrefix: CHANGIE_ diff --git a/CHANGELOG.md b/CHANGELOG.md index b2f951479..2c5ed9b3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,50 @@ # Changelog - All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), +and is generated by [Changie](https://github.com/miniscruff/changie). -* [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for stable releases; -* date versioning for test releases -## Unreleased +## v2.2.0 - 2023-06-18 +### Feature +* When navigating from a workflow regarding to an evaluation's document to an accompanying course, scroll directly to the document, and blink to highlight this document +* Add notification to accompanying period work and work's evaluation's documents +* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113))[Export] Filter accompanying period by step at date: allow to pick multiple steps +* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113))[export] add a filter on accompanying period: filter by step between two dates +### Fixed +* use the correct annotation for the association between PersonCurrentCenter and Person +* ([#58](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/58))Fix birthdate timezone in PersonRenderBox +* ([#55](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/55))Fix the notification counter +### DX +* DQL function OVERLAPSI: simplify expression in postgresql + +## v2.1.0 - 2023-06-12 + +### Feature + +* [docgen] allow to pick a third party when generating a document in context Activity, AccompanyingPeriod + +### Fixed + +* ([#111](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/111)) List of "my accompanying periods": separate the active and closed periods in two different lists, and show the inactive_long and inactive_short periods + +### Security + +* ([#105](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/105)) Rights are checked for display of 'accompanying period' tab in household menu. Rights are also checked for creation of 'accompanying period' from within household context + +### DX + +* Add methods to RegroupmentRepository and fullfill Center / Regroupment Doctrine mapping + +## 2.0.0 + +* this is a release to relaunch our proceess of release with semantic versioning + +## Test releases + +### 2.0.0-beta3 - * [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person * [activity][export] Feature: improve label for aliases in "Filter by activity type" * [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository @@ -18,9 +52,6 @@ and this project adheres to * [person][export] Fixed: use left join for related entities in accompanying course aggregators * [workflow] Feature: allow user to copy and send manually the access link for the workflow * [workflow] Feature: show the email addresses that received an access link for the workflow - -## Test releases - ### 2.0.0-beta2 * [workflow]: Fixed: the notification is sent when the user is added to the first step. diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 97678af50..444c663fc 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -650,8 +650,8 @@ final class ActivityController extends AbstractController throw $this->createNotFoundException('Accompanying Period not found'); } - // TODO Add permission - // $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); + // TODO Add permission + // $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); } else { throw $this->createNotFoundException('Person or Accompanying Period not found'); } diff --git a/src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php index 03a1b09ab..a3cebec38 100644 --- a/src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php +++ b/src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php @@ -26,12 +26,12 @@ final class PersonMenuBuilder implements LocalMenuBuilderInterface /** * @var AuthorizationCheckerInterface */ - protected $authorizationChecker; + private $authorizationChecker; /** * @var TranslatorInterface */ - protected $translator; + private $translator; public function __construct( AuthorizationCheckerInterface $authorizationChecker, diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php index 624859eda..a20ca365b 100644 --- a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php +++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php @@ -24,6 +24,9 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface; +use Chill\ThirdPartyBundle\Entity\ThirdParty; +use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender; +use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; @@ -55,6 +58,10 @@ class ActivityContext implements private TranslatorInterface $translator; + private ThirdPartyRender $thirdPartyRender; + + private ThirdPartyRepository $thirdPartyRepository; + public function __construct( DocumentCategoryRepository $documentCategoryRepository, NormalizerInterface $normalizer, @@ -63,7 +70,9 @@ class ActivityContext implements PersonRenderInterface $personRender, PersonRepository $personRepository, TranslatorInterface $translator, - BaseContextData $baseContextData + BaseContextData $baseContextData, + ThirdPartyRender $thirdPartyRender, + ThirdPartyRepository $thirdPartyRepository ) { $this->documentCategoryRepository = $documentCategoryRepository; $this->normalizer = $normalizer; @@ -73,6 +82,8 @@ class ActivityContext implements $this->personRepository = $personRepository; $this->translator = $translator; $this->baseContextData = $baseContextData; + $this->thirdPartyRender = $thirdPartyRender; + $this->thirdPartyRepository = $thirdPartyRepository; } public function adminFormReverseTransform(array $data): array @@ -89,6 +100,8 @@ class ActivityContext implements 'person1Label' => $data['person1Label'] ?? $this->translator->trans('docgen.person 1'), 'person2' => $data['person2'] ?? false, 'person2Label' => $data['person2Label'] ?? $this->translator->trans('docgen.person 2'), + 'thirdParty' => $data['thirdParty'] ?? false, + 'thirdPartyLabel' => $data['thirdPartyLabel'] ?? $this->translator->trans('thirdParty'), ]; } @@ -118,6 +131,14 @@ class ActivityContext implements ->add('person2Label', TextType::class, [ 'label' => 'person 2 label', 'required' => true, + ]) + ->add('thirdParty', CheckboxType::class, [ + 'required' => false, + 'label' => 'docgen.Ask for thirdParty', + ]) + ->add('thirdPartyLabel', TextType::class, [ + 'label' => 'docgen.thirdParty label', + 'required' => true, ]); } @@ -143,6 +164,20 @@ class ActivityContext implements ]); } } + + $thirdParties = $entity->getThirdParties(); + if ($options['thirdParty'] ?? false) { + $builder->add('thirdParty', EntityType::class, [ + 'class' => ThirdParty::class, + 'choices' => $thirdParties, + 'choice_label' => fn (ThirdParty $p) => $this->thirdPartyRender->renderString($p, []), + 'multiple' => false, + 'required' => false, + 'expanded' => true, + 'label' => $options['thirdPartyLabel'], + 'placeholder' => $this->translator->trans('Any third party selected'), + ]); + } } public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array @@ -157,6 +192,12 @@ class ActivityContext implements } } + if (null !== ($id = ($data['thirdParty'] ?? null))) { + $denormalized['thirdParty'] = $this->thirdPartyRepository->find($id); + } else { + $denormalized['thirdParty'] = null; + } + return $denormalized; } @@ -165,9 +206,11 @@ class ActivityContext implements $normalized = []; foreach (['mainPerson', 'person1', 'person2'] as $k) { - $normalized[$k] = null === $data[$k] ? null : $data[$k]->getId(); + $normalized[$k] = ($data[$k] ?? null)?->getId(); } + $normalized['thirdParty'] = ($data['thirdParty'] ?? null)?->getId(); + return $normalized; } @@ -196,6 +239,13 @@ class ActivityContext implements } } + if ($options['thirdParty']) { + $data['thirdParty'] = $this->normalizer->normalize($contextGenerationData['thirdParty'], 'docgen', [ + 'docgen:expects' => ThirdParty::class, + 'groups' => 'docgen:read' + ]); + } + return $data; } @@ -235,7 +285,7 @@ class ActivityContext implements { $options = $template->getOptions(); - return $options['mainPerson'] || $options['person1'] || $options['person2']; + return $options['mainPerson'] || $options['person1'] || $options['person2'] || $options ['thirdParty']; } public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void diff --git a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php index 3da451f2e..045d09beb 100644 --- a/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php +++ b/src/Bundle/ChillActivityBundle/Service/DocGenerator/ListActivitiesByAccompanyingPeriodContext.php @@ -140,26 +140,36 @@ class ListActivitiesByAccompanyingPeriodContext implements return $normalized; } + /** + * @return list + */ private function filterActivitiesByUser(array $activities, User $user): array { - return array_filter( - $activities, - function ($activity) use ($user) { - $activityUsernames = array_map(static fn ($user) => $user['username'], $activity['users'] ?? []); - return in_array($user->getUsername(), $activityUsernames, true); - } + return array_values( + array_filter( + $activities, + function ($activity) use ($user) { + $activityUsernames = array_map(static fn ($user) => $user['username'], $activity['users'] ?? []); + return in_array($user->getUsername(), $activityUsernames, true); + } + ) ); } + /** + * @return list + */ private function filterWorksByUser(array $works, User $user): array { - return array_filter( - $works, - function ($work) use ($user) { - $workUsernames = array_map(static fn ($user) => $user['username'], $work['referrers'] ?? []); + return array_values( + array_filter( + $works, + function ($work) use ($user) { + $workUsernames = array_map(static fn ($user) => $user['username'], $work['referrers'] ?? []); - return in_array($user->getUsername(), $workUsernames, true); - } + return in_array($user->getUsername(), $workUsernames, true); + } + ) ); } diff --git a/src/Bundle/ChillBudgetBundle/Calculator/CalculatorResult.php b/src/Bundle/ChillBudgetBundle/Calculator/CalculatorResult.php index 380e641a6..2a67486f0 100644 --- a/src/Bundle/ChillBudgetBundle/Calculator/CalculatorResult.php +++ b/src/Bundle/ChillBudgetBundle/Calculator/CalculatorResult.php @@ -21,7 +21,7 @@ class CalculatorResult public $label; - public $result; + public float $result; public $type; } diff --git a/src/Bundle/ChillBudgetBundle/Resources/views/Budget/_macros.html.twig b/src/Bundle/ChillBudgetBundle/Resources/views/Budget/_macros.html.twig index 70e96d96c..dfa286af4 100644 --- a/src/Bundle/ChillBudgetBundle/Resources/views/Budget/_macros.html.twig +++ b/src/Bundle/ChillBudgetBundle/Resources/views/Budget/_macros.html.twig @@ -16,8 +16,14 @@ {% if f.isResource %} {{ f.resource.name|localize_translatable_string }} + {% if f.resource.getKind is same as 'other' %} + : {{ f.getComment }} + {% endif %} {% else %} {{ f.charge.name|localize_translatable_string }} + {% if f.charge.getKind is same as 'other' %} + : {{ f.getComment }} + {% endif %} {% endif %} {{ f.amount|format_currency('EUR') }} diff --git a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/CollectionDocGenNormalizer.php b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/CollectionDocGenNormalizer.php index 196018a03..b4e99bb98 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/CollectionDocGenNormalizer.php +++ b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/CollectionDocGenNormalizer.php @@ -13,6 +13,7 @@ namespace Chill\DocGeneratorBundle\Serializer\Normalizer; use ArrayObject; use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ReadableCollection; use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; @@ -51,7 +52,9 @@ class CollectionDocGenNormalizer implements ContextAwareNormalizerInterface, Nor return false; } - return $data instanceof Collection - || (null === $data && Collection::class === ($context['docgen:expects'] ?? null)); + return $data instanceof ReadableCollection + || (null === $data && Collection::class === ($context['docgen:expects'] ?? null)) + || (null === $data && ReadableCollection::class === ($context['docgen:expects'] ?? null)) + ; } } diff --git a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php index 3807ee9ee..bbf12b0fd 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php +++ b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php @@ -13,6 +13,7 @@ namespace Chill\DocGeneratorBundle\Serializer\Normalizer; use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; +use Doctrine\Common\Collections\ReadableCollection; use ReflectionClass; use RuntimeException; use Symfony\Component\PropertyAccess\PropertyAccess; @@ -271,6 +272,14 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte if ($isTranslatable) { $data[$key] = $this->translatableStringHelper ->localize($value); + } elseif ($value instanceof ReadableCollection) { + // when normalizing collection, we should not preserve keys (to ensure that the result is a list) + // this is why we make call to the normalizer again to use the CollectionDocGenNormalizer + $data[$key] = + $this->normalizer->normalize($value, $format, array_merge( + $objectContext, + $attribute->getNormalizationContextForGroups($expectedGroups) + )); } elseif (is_iterable($value)) { $arr = []; diff --git a/src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Normalizer/CollectionDocGenNormalizerTest.php b/src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Normalizer/CollectionDocGenNormalizerTest.php new file mode 100644 index 000000000..7bfddadca --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Normalizer/CollectionDocGenNormalizerTest.php @@ -0,0 +1,52 @@ +normalizer = self::$container->get(NormalizerInterface::class); + } + + public function testNormalizeFilteredArray(): void + { + $coll = new ArrayCollection([ + (object) ['v' => 'foo'], + (object) ['v' => 'bar'], + (object) ['v' => 'baz'], + ]); + + //filter to get non continuous indexes + $criteria = new Criteria(); + $criteria->where(Criteria::expr()->neq('v', 'bar')); + + $filtered = $coll->matching($criteria); + $normalized = $this->normalizer->normalize($filtered, 'docgen', []); + + self::assertIsArray($normalized); + self::assertArrayHasKey(0, $normalized); + self::assertArrayHasKey(1, $normalized); + } +} diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue index aa99a223f..be482cf7e 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue @@ -44,3 +44,9 @@ async function download_and_open(event: Event): Promise { } + + diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DownloadButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DownloadButton.vue index f05d1e91c..0248ef30b 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DownloadButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DownloadButton.vue @@ -1,51 +1,96 @@ + + diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue index d68f60f86..ea244192e 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue @@ -40,5 +40,8 @@ async function beforeLeave(event: Event): Promise { diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts index 5b61a108d..c0b0fa940 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts @@ -149,16 +149,21 @@ async function download_and_decrypt_doc(urlGenerator: string, keyData: JsonWebKe } if (iv.length === 0) { + console.log('returning document immediatly'); return rawResponse.blob(); } + console.log('start decrypting doc'); + const rawBuffer = await rawResponse.arrayBuffer(); try { const key = await window.crypto.subtle .importKey('jwk', keyData, { name: algo }, false, ['decrypt']); + console.log('key created'); const decrypted = await window.crypto.subtle .decrypt({ name: algo, iv: iv }, key, rawBuffer); + console.log('doc decrypted'); return Promise.resolve(new Blob([decrypted])); } catch (e) { diff --git a/src/Bundle/ChillDocStoreBundle/Resources/views/Button/button_group.html.twig b/src/Bundle/ChillDocStoreBundle/Resources/views/Button/button_group.html.twig index f83cafd51..2babe1ad9 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/views/Button/button_group.html.twig +++ b/src/Bundle/ChillDocStoreBundle/Resources/views/Button/button_group.html.twig @@ -1,5 +1,5 @@ {%- import "@ChillDocStore/Macro/macro.html.twig" as m -%} -
{% transchoice total with { '%pattern%' : pattern } %}%total% events match the search %pattern%{% endtranschoice %}

+ {% if events|length > 0 %}

{{ 'Results %start%-%end% of %total%'|trans({ '%start%' : start, '%end%': start + events|length, '%total%' : total } ) }}

- - +
@@ -25,7 +29,7 @@
  • {# {% if is_granted('CHILL_EVENT_SEE_DETAILS', event) %} #} - + {{ 'See'|trans }} {# {% endif %} #} diff --git a/src/Bundle/ChillEventBundle/Resources/views/Event/listByPerson.html.twig b/src/Bundle/ChillEventBundle/Resources/views/Event/listByPerson.html.twig index ef2dae85c..ec9ebf76a 100644 --- a/src/Bundle/ChillEventBundle/Resources/views/Event/listByPerson.html.twig +++ b/src/Bundle/ChillEventBundle/Resources/views/Event/listByPerson.html.twig @@ -24,7 +24,7 @@ {% block content %}

    {{ 'Events participation' |trans }}

    -
{{ 'Name'|trans }}
+
diff --git a/src/Bundle/ChillEventBundle/Resources/views/Event/newPickCenter.html.twig b/src/Bundle/ChillEventBundle/Resources/views/Event/newPickCenter.html.twig index a10d754e6..71c68002a 100644 --- a/src/Bundle/ChillEventBundle/Resources/views/Event/newPickCenter.html.twig +++ b/src/Bundle/ChillEventBundle/Resources/views/Event/newPickCenter.html.twig @@ -18,10 +18,10 @@
  • - {{ form_widget(form.submit, { 'attr' : { 'class' : 'btn btn-chill-green' } }) }} + {{ form_widget(form.submit, { 'attr' : { 'class' : 'btn btn-submit' } }) }}
  • - + {{ form_end(form) }} {% endblock %} diff --git a/src/Bundle/ChillEventBundle/Resources/views/Event/show.html.twig b/src/Bundle/ChillEventBundle/Resources/views/Event/show.html.twig index 4ce92a200..31eaf0df6 100644 --- a/src/Bundle/ChillEventBundle/Resources/views/Event/show.html.twig +++ b/src/Bundle/ChillEventBundle/Resources/views/Event/show.html.twig @@ -1,14 +1,14 @@ {% extends '@ChillEvent/layout.html.twig' %} {% block title 'Event : %label%'|trans({ '%label%' : event.name } ) %} - -{% import 'ChillPersonBundle:Person:macro.html.twig' as person_macro %} + +{% import 'ChillPersonBundle:Person:macro.html.twig' as person_macro %} {% block event_content -%}

    {{ 'Details of an event'|trans }}

    -
    {{ 'Date'|trans }}
    +
    @@ -33,7 +33,7 @@
    {{ 'Name'|trans }}
    - +
      {% set returnPath = app.request.get('return_path') %} @@ -64,13 +64,13 @@
    - +

    {{ 'Participations'|trans }}

    {% set count = event.participations|length %}

    {% transchoice count %}%count% participations to this event{% endtranschoice %}

    - + {% if count > 0 %} - +
    @@ -108,10 +108,10 @@ {% endfor %}
    {{ 'Person'|trans }}
    - - + + {% endif %} - +
      {% if count > 0 %}
    • {{ 'Edit all the participations'|trans }}
    • diff --git a/src/Bundle/ChillEventBundle/Search/EventSearch.php b/src/Bundle/ChillEventBundle/Search/EventSearch.php index a7ac22592..6bb3d435d 100644 --- a/src/Bundle/ChillEventBundle/Search/EventSearch.php +++ b/src/Bundle/ChillEventBundle/Search/EventSearch.php @@ -12,13 +12,14 @@ declare(strict_types=1); namespace Chill\EventBundle\Search; use Chill\EventBundle\Entity\Event; +use Chill\EventBundle\Repository\EventRepository; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Search\AbstractSearch; use Chill\MainBundle\Search\SearchInterface; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; -use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Templating\EngineInterface as TemplatingEngine; use function count; @@ -40,15 +41,17 @@ class EventSearch extends AbstractSearch { public const NAME = 'event_regular'; + private $security; + /** - * @var EntityRepository + * @var EventRepository */ private $er; /** * @var AuthorizationHelper */ - private $helper; + private $authorizationHelper; /** * @var PaginatorFactory @@ -60,21 +63,16 @@ class EventSearch extends AbstractSearch */ private $templating; - /** - * @var \Chill\MainBundle\Entity\User - */ - private $user; - public function __construct( - TokenStorageInterface $tokenStorage, - EntityRepository $eventRepository, + Security $security, + EventRepository $eventRepository, AuthorizationHelper $authorizationHelper, TemplatingEngine $templating, PaginatorFactory $paginatorFactory ) { - $this->user = $tokenStorage->getToken()->getUser(); + $this->security = $security; $this->er = $eventRepository; - $this->helper = $authorizationHelper; + $this->authorizationHelper = $authorizationHelper; $this->templating = $templating; $this->paginationFactory = $paginatorFactory; } @@ -101,7 +99,7 @@ class EventSearch extends AbstractSearch if ('html' === $format) { return $this->templating->render( - 'ChillEventBundle:Event:list.html.twig', + '@ChillEvent/Event/list.html.twig', [ 'events' => $this->search($terms, $start, $limit, $options), 'pattern' => $this->recomposePattern($terms, $this->getAvailableTerms(), $terms['_domain']), @@ -140,8 +138,10 @@ class EventSearch extends AbstractSearch protected function composeQuery(QueryBuilder &$qb, $terms) { // add security clauses - $reachableCenters = $this->helper - ->getReachableCenters($this->user, 'CHILL_EVENT_SEE'); + $reachableCenters = $this->authorizationHelper->getReachableCenters( + $this->security->getUser(), + 'CHILL_EVENT_SEE' + ); if (count($reachableCenters) === 0) { // add a clause to block all events @@ -152,8 +152,9 @@ class EventSearch extends AbstractSearch $orWhere = $qb->expr()->orX(); foreach ($reachableCenters as $center) { - $circles = $this->helper->getReachableScopes( - $this->user, + $n = $n+1; + $circles = $this->authorizationHelper->getReachableScopes( + $this->security->getUser(), 'CHILL_EVENT_SEE', $center ); diff --git a/src/Bundle/ChillEventBundle/config/services/repositories.yaml b/src/Bundle/ChillEventBundle/config/services/repositories.yaml index 4d627f625..ff20d094e 100644 --- a/src/Bundle/ChillEventBundle/config/services/repositories.yaml +++ b/src/Bundle/ChillEventBundle/config/services/repositories.yaml @@ -17,7 +17,7 @@ services: factory: ['@doctrine.orm.entity_manager', getRepository] arguments: - 'Chill\EventBundle\Entity\Role' - + chill_event.repository.status: class: Doctrine\ORM\EntityRepository factory: ['@doctrine.orm.entity_manager', getRepository] diff --git a/src/Bundle/ChillEventBundle/config/services/search.yaml b/src/Bundle/ChillEventBundle/config/services/search.yaml index 2df3db26f..5c55fc388 100644 --- a/src/Bundle/ChillEventBundle/config/services/search.yaml +++ b/src/Bundle/ChillEventBundle/config/services/search.yaml @@ -1,12 +1,11 @@ services: - chill_event.search_events: - class: Chill\EventBundle\Search\EventSearch + Chill\EventBundle\Search\EventSearch: arguments: - - "@security.token_storage" - - "@chill_event.repository.event" - - "@chill.main.security.authorization.helper" - - "@templating" - - "@chill_main.paginator_factory" + $security: '@Symfony\Component\Security\Core\Security' + $eventRepository: "@chill_event.repository.event" + $authorizationHelper: "@chill.main.security.authorization.helper" + $templating: "@templating" + $paginatorFactory: "@chill_main.paginator_factory" tags: - { name: chill.search, alias: 'event_regular' } - + diff --git a/src/Bundle/ChillEventBundle/translations/messages.fr.yml b/src/Bundle/ChillEventBundle/translations/messages.fr.yml index 8810c040a..6b6ac220f 100644 --- a/src/Bundle/ChillEventBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillEventBundle/translations/messages.fr.yml @@ -30,7 +30,7 @@ The event was created: L'événement a été créé #crud participation Edit all the participations: Modifier toutes les participations -Edit the participation: Modifier la participation +Edit the participation: Modifier la participation à l'événement Participation Edit: Modifier une participation Add a participation: Ajouter un participant Participation creation: Ajouter une participation diff --git a/src/Bundle/ChillMainBundle/Command/SynchronizeEntityInfoViewsCommand.php b/src/Bundle/ChillMainBundle/Command/SynchronizeEntityInfoViewsCommand.php index c3dc8a87c..05fefe20f 100644 --- a/src/Bundle/ChillMainBundle/Command/SynchronizeEntityInfoViewsCommand.php +++ b/src/Bundle/ChillMainBundle/Command/SynchronizeEntityInfoViewsCommand.php @@ -36,5 +36,4 @@ class SynchronizeEntityInfoViewsCommand extends Command return 0; } - } diff --git a/src/Bundle/ChillMainBundle/Controller/PermissionsGroupController.php b/src/Bundle/ChillMainBundle/Controller/PermissionsGroupController.php index 5d32a8dbb..97e80916c 100644 --- a/src/Bundle/ChillMainBundle/Controller/PermissionsGroupController.php +++ b/src/Bundle/ChillMainBundle/Controller/PermissionsGroupController.php @@ -16,14 +16,18 @@ use Chill\MainBundle\Entity\RoleScope; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Form\PermissionsGroupType; use Chill\MainBundle\Form\Type\ComposedRoleScopeType; +use Chill\MainBundle\Repository\PermissionsGroupRepository; +use Chill\MainBundle\Repository\RoleScopeRepository; use Chill\MainBundle\Security\RoleProvider; use Chill\MainBundle\Templating\TranslatableStringHelper; +use Doctrine\ORM\EntityManagerInterface; use RuntimeException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\Role\Role; -use Symfony\Component\Security\Core\Role\RoleHierarchy; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -32,62 +36,28 @@ use function array_key_exists; /** * Class PermissionsGroupController. */ -class PermissionsGroupController extends AbstractController +final class PermissionsGroupController extends AbstractController { - /** - * @var RoleHierarchy - */ - private $roleHierarchy; - - /** - * @var RoleProvider - */ - private $roleProvider; - - /** - * @var TranslatableStringHelper - */ - private $translatableStringHelper; - - /** - * @var TranslatorInterface - */ - private $translator; - - /** - * @var ValidatorInterface - */ - private $validator; - /** * PermissionsGroupController constructor. */ public function __construct( - TranslatableStringHelper $translatableStringHelper, - RoleProvider $roleProvider, - RoleHierarchy $roleHierarchy, - TranslatorInterface $translator, - ValidatorInterface $validator + private readonly TranslatableStringHelper $translatableStringHelper, + private readonly RoleProvider $roleProvider, + private readonly RoleHierarchyInterface $roleHierarchy, + private readonly TranslatorInterface $translator, + private readonly ValidatorInterface $validator, + private readonly EntityManagerInterface $em, + private readonly PermissionsGroupRepository $permissionsGroupRepository, + private readonly RoleScopeRepository $roleScopeRepository, ) { - $this->translatableStringHelper = $translatableStringHelper; - $this->roleProvider = $roleProvider; - $this->roleHierarchy = $roleHierarchy; - $this->translator = $translator; - $this->validator = $validator; } /** - * @param int $id - * - * @throws type - * - * @return Respon */ - public function addLinkRoleScopeAction(Request $request, $id) + public function addLinkRoleScopeAction(Request $request, int $id): Response { - $em = $this->getDoctrine()->getManager(); - - $permissionsGroup = $em->getRepository(\Chill\MainBundle\Entity\PermissionsGroup::class)->find($id); + $permissionsGroup = $this->permissionsGroupRepository->find($id); if (!$permissionsGroup) { throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); @@ -106,7 +76,7 @@ class PermissionsGroupController extends AbstractController $violations = $this->validator->validate($permissionsGroup); if ($violations->count() === 0) { - $em->flush(); + $this->em->flush(); $this->addFlash( 'notice', @@ -166,16 +136,15 @@ class PermissionsGroupController extends AbstractController /** * Creates a new PermissionsGroup entity. */ - public function createAction(Request $request) + public function createAction(Request $request): Response { $permissionsGroup = new PermissionsGroup(); $form = $this->createCreateForm($permissionsGroup); $form->handleRequest($request); if ($form->isValid()) { - $em = $this->getDoctrine()->getManager(); - $em->persist($permissionsGroup); - $em->flush(); + $this->em->persist($permissionsGroup); + $this->em->flush(); return $this->redirect($this->generateUrl( 'admin_permissionsgroup_edit', @@ -191,18 +160,11 @@ class PermissionsGroupController extends AbstractController /** * remove an association between permissionsGroup and roleScope. - * - * @param int $pgid permissionsGroup id - * @param int $rsid roleScope id - * - * @return redirection to edit form */ - public function deleteLinkRoleScopeAction($pgid, $rsid) + public function deleteLinkRoleScopeAction(int $pgid, int $rsid): Response { - $em = $this->getDoctrine()->getManager(); - - $permissionsGroup = $em->getRepository(\Chill\MainBundle\Entity\PermissionsGroup::class)->find($pgid); - $roleScope = $em->getRepository(\Chill\MainBundle\Entity\RoleScope::class)->find($rsid); + $permissionsGroup = $this->permissionsGroupRepository->find($pgid); + $roleScope = $this->roleScopeRepository->find($rsid); if (!$permissionsGroup) { throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); @@ -214,7 +176,7 @@ class PermissionsGroupController extends AbstractController try { $permissionsGroup->removeRoleScope($roleScope); - } catch (RuntimeException $ex) { + } catch (RuntimeException) { $this->addFlash( 'notice', $this->translator->trans("The role '%role%' and circle " @@ -231,7 +193,7 @@ class PermissionsGroupController extends AbstractController )); } - $em->flush(); + $this->em->flush(); if ($roleScope->getScope() !== null) { $this->addFlash( @@ -260,14 +222,10 @@ class PermissionsGroupController extends AbstractController /** * Displays a form to edit an existing PermissionsGroup entity. - * - * @param mixed $id */ - public function editAction($id) + public function editAction(int $id): Response { - $em = $this->getDoctrine()->getManager(); - - $permissionsGroup = $em->getRepository(\Chill\MainBundle\Entity\PermissionsGroup::class)->find($id); + $permissionsGroup = $this->permissionsGroupRepository->find($id); if (!$permissionsGroup) { throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); @@ -311,11 +269,9 @@ class PermissionsGroupController extends AbstractController /** * Lists all PermissionsGroup entities. */ - public function indexAction() + public function indexAction(): Response { - $em = $this->getDoctrine()->getManager(); - - $entities = $em->getRepository(\Chill\MainBundle\Entity\PermissionsGroup::class)->findAll(); + $entities = $this->permissionsGroupRepository->findAllOrderedAlphabetically(); return $this->render('@ChillMain/PermissionsGroup/index.html.twig', [ 'entities' => $entities, @@ -325,7 +281,7 @@ class PermissionsGroupController extends AbstractController /** * Displays a form to create a new PermissionsGroup entity. */ - public function newAction() + public function newAction(): Response { $permissionsGroup = new PermissionsGroup(); $form = $this->createCreateForm($permissionsGroup); @@ -338,14 +294,10 @@ class PermissionsGroupController extends AbstractController /** * Finds and displays a PermissionsGroup entity. - * - * @param mixed $id */ - public function showAction($id) + public function showAction(int $id): Response { - $em = $this->getDoctrine()->getManager(); - - $permissionsGroup = $em->getRepository(\Chill\MainBundle\Entity\PermissionsGroup::class)->find($id); + $permissionsGroup = $this->permissionsGroupRepository->find($id); if (!$permissionsGroup) { throw $this->createNotFoundException('Unable to find PermissionsGroup entity.'); @@ -393,15 +345,10 @@ class PermissionsGroupController extends AbstractController /** * Edits an existing PermissionsGroup entity. - * - * @param mixed $id */ - public function updateAction(Request $request, $id) + public function updateAction(Request $request, int $id): Response { - $em = $this->getDoctrine()->getManager(); - - $permissionsGroup = $em - ->getRepository(\Chill\MainBundle\Entity\PermissionsGroup::class) + $permissionsGroup = $this->permissionsGroupRepository ->find($id); if (!$permissionsGroup) { @@ -413,7 +360,7 @@ class PermissionsGroupController extends AbstractController $editForm->handleRequest($request); if ($editForm->isValid()) { - $em->flush(); + $this->em->flush(); return $this->redirect($this->generateUrl('admin_permissionsgroup_edit', ['id' => $id])); } @@ -452,18 +399,11 @@ class PermissionsGroupController extends AbstractController /** * get a role scope by his parameters. The role scope is persisted if it - * doesn't exists in database. - * - * @param Scope $scope - * @param string $role - * - * @return RoleScope + * doesn't exist in database. */ - protected function getPersistentRoleScopeBy($role, ?Scope $scope = null) + protected function getPersistentRoleScopeBy(string $role, ?Scope $scope = null): RoleScope { - $em = $this->getDoctrine()->getManager(); - - $roleScope = $em->getRepository(\Chill\MainBundle\Entity\RoleScope::class) + $roleScope = $this->roleScopeRepository ->findOneBy(['role' => $role, 'scope' => $scope]); if (null === $roleScope) { @@ -471,7 +411,7 @@ class PermissionsGroupController extends AbstractController ->setRole($role) ->setScope($scope); - $em->persist($roleScope); + $this->em->persist($roleScope); } return $roleScope; @@ -479,10 +419,8 @@ class PermissionsGroupController extends AbstractController /** * creates a form to add a role scope to permissionsgroup. - * - * @return \Symfony\Component\Form\Form The form */ - private function createAddRoleScopeForm(PermissionsGroup $permissionsGroup) + private function createAddRoleScopeForm(PermissionsGroup $permissionsGroup): FormInterface { return $this->createFormBuilder() ->setAction($this->generateUrl( @@ -499,10 +437,8 @@ class PermissionsGroupController extends AbstractController * Creates a form to create a PermissionsGroup entity. * * @param PermissionsGroup $permissionsGroup The entity - * - * @return \Symfony\Component\Form\Form The form */ - private function createCreateForm(PermissionsGroup $permissionsGroup) + private function createCreateForm(PermissionsGroup $permissionsGroup): FormInterface { $form = $this->createForm(PermissionsGroupType::class, $permissionsGroup, [ 'action' => $this->generateUrl('admin_permissionsgroup_create'), @@ -518,13 +454,11 @@ class PermissionsGroupController extends AbstractController * Creates a form to delete a link to roleScope. * * @param mixed $permissionsGroup The entity id - * - * @return \Symfony\Component\Form\Form The form */ private function createDeleteRoleScopeForm( PermissionsGroup $permissionsGroup, RoleScope $roleScope - ) { + ): FormInterface { return $this->createFormBuilder() ->setAction($this->generateUrl( 'admin_permissionsgroup_delete_role_scope', @@ -537,12 +471,8 @@ class PermissionsGroupController extends AbstractController /** * Creates a form to edit a PermissionsGroup entity. - * - * @param PermissionsGroup $permissionsGroup The entity - * - * @return \Symfony\Component\Form\Form The form */ - private function createEditForm(PermissionsGroup $permissionsGroup) + private function createEditForm(PermissionsGroup $permissionsGroup): FormInterface { $form = $this->createForm(PermissionsGroupType::class, $permissionsGroup, [ 'action' => $this->generateUrl('admin_permissionsgroup_update', ['id' => $permissionsGroup->getId()]), @@ -556,10 +486,8 @@ class PermissionsGroupController extends AbstractController /** * expand roleScopes to be easily shown in template. - * - * @return array */ - private function getExpandedRoles(array $roleScopes) + private function getExpandedRoles(array $roleScopes): array { $expandedRoles = []; @@ -567,10 +495,10 @@ class PermissionsGroupController extends AbstractController if (!array_key_exists($roleScope->getRole(), $expandedRoles)) { $expandedRoles[$roleScope->getRole()] = array_map( - static fn (Role $role) => $role->getRole(), + static fn ($role) => $role, $this->roleHierarchy - ->getReachableRoles( - [new Role($roleScope->getRole())] + ->getReachableRoleNames( + [$roleScope->getRole()] ) ); } diff --git a/src/Bundle/ChillMainBundle/Controller/UserExportController.php b/src/Bundle/ChillMainBundle/Controller/UserExportController.php new file mode 100644 index 000000000..530ac19b7 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/UserExportController.php @@ -0,0 +1,144 @@ +security->isGranted('ROLE_ADMIN')) { + throw new AccessDeniedHttpException('Only ROLE_ADMIN can export this list'); + } + + $users = $this->userRepository->findAllAsArray($request->getLocale()); + + $csv = Writer::createFromPath('php://temp', 'r+'); + $csv->insertOne( + array_map( + fn (string $e) => $this->translator->trans('admin.users.export.' . $e), + [ + 'id', + 'username', + 'email', + 'enabled', + 'civility_id', + 'civility_abbreviation', + 'civility_name', + 'label', + 'mainCenter_id' , + 'mainCenter_name', + 'mainScope_id', + 'mainScope_name', + 'userJob_id', + 'userJob_name', + 'currentLocation_id', + 'currentLocation_name', + 'mainLocation_id', + 'mainLocation_name', + 'absenceStart' + ] + ) + ); + $csv->addFormatter(fn (array $row) => null !== ($row['absenceStart'] ?? null) ? array_merge($row, ['absenceStart' => $row['absenceStart']->format('Y-m-d')]) : $row); + $csv->insertAll($users); + + return new StreamedResponse( + function () use ($csv) { + foreach ($csv->chunk(1024) as $chunk) { + echo $chunk; + flush(); + } + }, + Response::HTTP_OK, + [ + 'Content-Encoding' => 'none', + 'Content-Type' => 'text/csv; charset=UTF-8', + 'Content-Disposition' => 'attachment; users.csv', + ] + ); + } + + /** + * @return StreamedResponse + * @throws \League\Csv\CannotInsertRecord + * @throws \League\Csv\Exception + * @throws \League\Csv\UnavailableStream + * + * @Route("/{_locale}/admin/main/users/export/permissions.{_format}", requirements={"_format": "csv"}, name="chill_main_users_export_permissions") + */ + public function userPermissionsList(string $_format = 'csv'): StreamedResponse + { + if (!$this->security->isGranted('ROLE_ADMIN')) { + throw new AccessDeniedHttpException('Only ROLE_ADMIN can export this list'); + } + + $userPermissions = $this->userRepository->findAllUserACLAsArray(); + + $csv = Writer::createFromPath('php://temp', 'r+'); + $csv->insertOne( + array_map( + fn (string $e) => $this->translator->trans('admin.users.export.' . $e), + [ + 'id', + 'username', + 'email', + 'label', + 'enabled', + 'center_id', + 'center_name', + 'permissionsGroup_id', + 'permissionsGroup_name', + ] + ) + ); + $csv->insertAll($userPermissions); + + return new StreamedResponse( + function () use ($csv) { + foreach ($csv->chunk(1024) as $chunk) { + echo $chunk; + flush(); + } + }, + Response::HTTP_OK, + [ + 'Content-Encoding' => 'none', + 'Content-Type' => 'text/csv; charset=UTF-8', + 'Content-Disposition' => 'attachment; users.csv', + ] + ); + } +} diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php index 36647612b..cfd28160c 100644 --- a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php +++ b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php @@ -359,9 +359,9 @@ class WorkflowController extends AbstractController } // TODO symfony 5: add those "future" on context ($workflow->apply($entityWorkflow, $transition, $context) - $entityWorkflow->futureCcUsers = $transitionForm['future_cc_users']->getData(); - $entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData(); - $entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData(); + $entityWorkflow->futureCcUsers = $transitionForm['future_cc_users']->getData() ?? []; + $entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData() ?? []; + $entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData() ?? []; $workflow->apply($entityWorkflow, $transition); diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/OverlapsI.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/OverlapsI.php index 29642dc15..d39c1451c 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/OverlapsI.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/OverlapsI.php @@ -90,16 +90,14 @@ class OverlapsI extends FunctionNode if ($part instanceof PathExpression) { return sprintf( - "CASE WHEN %s IS NOT NULL THEN %s ELSE '%s'::date END", - $part->dispatch($sqlWalker), + "COALESCE(%s, '%s'::date)", $part->dispatch($sqlWalker), $p ); } return sprintf( - "CASE WHEN %s::date IS NOT NULL THEN %s::date ELSE '%s'::date END", - $part->dispatch($sqlWalker), + "COALESCE(%s::date, '%s'::date)", $part->dispatch($sqlWalker), $p ); diff --git a/src/Bundle/ChillMainBundle/Entity/Center.php b/src/Bundle/ChillMainBundle/Entity/Center.php index 5ca051ec0..0d5402409 100644 --- a/src/Bundle/ChillMainBundle/Entity/Center.php +++ b/src/Bundle/ChillMainBundle/Entity/Center.php @@ -48,12 +48,19 @@ class Center implements HasCenterInterface */ private string $name = ''; + /** + * @var Collection + * @ORM\ManyToMany(targetEntity=Regroupment::class, mappedBy="centers") + */ + private Collection $regroupments; + /** * Center constructor. */ public function __construct() { - $this->groupCenters = new \Doctrine\Common\Collections\ArrayCollection(); + $this->groupCenters = new ArrayCollection(); + $this->regroupments = new ArrayCollection(); } /** @@ -106,6 +113,14 @@ class Center implements HasCenterInterface return $this->name; } + /** + * @return Collection + */ + public function getRegroupments(): Collection + { + return $this->regroupments; + } + /** * @param $name * diff --git a/src/Bundle/ChillMainBundle/Entity/Regroupment.php b/src/Bundle/ChillMainBundle/Entity/Regroupment.php index 96953abf5..c5d9525cf 100644 --- a/src/Bundle/ChillMainBundle/Entity/Regroupment.php +++ b/src/Bundle/ChillMainBundle/Entity/Regroupment.php @@ -22,11 +22,12 @@ use Doctrine\ORM\Mapping as ORM; class Regroupment { /** - * @var Center * @ORM\ManyToMany( - * targetEntity=Center::class + * targetEntity=Center::class, + * inversedBy="regroupments" * ) * @ORM\Id + * @var Collection
      */ private Collection $centers; @@ -52,6 +53,26 @@ class Regroupment $this->centers = new ArrayCollection(); } + public function addCenter(Center $center): self + { + if (!$this->centers->contains($center)) { + $this->centers->add($center); + $center->getRegroupments()->add($this); + } + + return $this; + } + + public function removeCenter(Center $center): self + { + if ($this->centers->contains($center)) { + $this->centers->removeElement($center); + $center->getRegroupments()->removeElement($this); + } + + return $this; + } + public function getCenters(): Collection { return $this->centers; diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index a8ef88fbe..35b73786b 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -506,11 +506,11 @@ class User implements UserInterface * * @return User */ - public function setUsername($name) + public function setUsername(?string $name) { - $this->username = $name; + $this->username = (string) $name; - if (empty($this->getLabel())) { + if ("" === trim($this->getLabel())) { $this->setLabel($name); } diff --git a/src/Bundle/ChillMainBundle/Form/RegroupmentType.php b/src/Bundle/ChillMainBundle/Form/RegroupmentType.php index bc8e6684f..22814598a 100644 --- a/src/Bundle/ChillMainBundle/Form/RegroupmentType.php +++ b/src/Bundle/ChillMainBundle/Form/RegroupmentType.php @@ -31,7 +31,7 @@ class RegroupmentType extends AbstractType ->add('centers', EntityType::class, [ 'class' => Center::class, 'multiple' => true, - 'attr' => ['class' => 'select2'], + 'expanded' => true, ]) ->add('isActive', CheckboxType::class, [ 'label' => 'Actif ?', diff --git a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php index 529bd9789..7dff8d672 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php +++ b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php @@ -43,6 +43,10 @@ class EntityToJsonTransformer implements DataTransformerInterface public function reverseTransform($value) { + if ("" === $value) { + return null; + } + $denormalized = json_decode($value, true, 512, JSON_THROW_ON_ERROR); if ($this->multiple) { @@ -56,10 +60,6 @@ class EntityToJsonTransformer implements DataTransformerInterface ); } - if ('' === $value) { - return null; - } - return $this->denormalizeOne($denormalized); } diff --git a/src/Bundle/ChillMainBundle/Notification/NotificationPresence.php b/src/Bundle/ChillMainBundle/Notification/NotificationPresence.php index c09b845d7..a6e02c4ea 100644 --- a/src/Bundle/ChillMainBundle/Notification/NotificationPresence.php +++ b/src/Bundle/ChillMainBundle/Notification/NotificationPresence.php @@ -34,9 +34,13 @@ class NotificationPresence $this->notificationRepository = $notificationRepository; } - public function countNotificationsForClassAndEntity(string $relatedEntityClass, int $relatedEntityId): array + /** + * @param list $more + * @return array{unread: int, sent: int, total: int} + */ + public function countNotificationsForClassAndEntity(string $relatedEntityClass, int $relatedEntityId, array $more = [], array $options = []): array { - if (array_key_exists($relatedEntityClass, $this->cache) && array_key_exists($relatedEntityId, $this->cache[$relatedEntityClass])) { + if ([] === $more && array_key_exists($relatedEntityClass, $this->cache) && array_key_exists($relatedEntityId, $this->cache[$relatedEntityClass])) { return $this->cache[$relatedEntityClass][$relatedEntityId]; } @@ -46,21 +50,25 @@ class NotificationPresence $counter = $this->notificationRepository->countNotificationByRelatedEntityAndUserAssociated( $relatedEntityClass, $relatedEntityId, - $user + $user, + $more ); - $this->cache[$relatedEntityClass][$relatedEntityId] = $counter; + if ([] === $more) { + $this->cache[$relatedEntityClass][$relatedEntityId] = $counter; + } return $counter; } - return ['unread' => 0, 'read' => 0]; + return ['unread' => 0, 'sent' => 0, 'total' => 0]; } /** + * @param list $more * @return array|Notification[] */ - public function getNotificationsForClassAndEntity(string $relatedEntityClass, int $relatedEntityId): array + public function getNotificationsForClassAndEntity(string $relatedEntityClass, int $relatedEntityId, array $more = []): array { $user = $this->security->getUser(); @@ -68,7 +76,8 @@ class NotificationPresence return $this->notificationRepository->findNotificationByRelatedEntityAndUserAssociated( $relatedEntityClass, $relatedEntityId, - $user + $user, + $more ); } diff --git a/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php b/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php index 0720c6da6..a8751374e 100644 --- a/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php +++ b/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php @@ -34,24 +34,30 @@ class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface $this->urlGenerator = $urlGenerator; } - public function counterNotificationFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string + public function counterNotificationFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $more = [], array $options = []): string { return $environment->render( '@ChillMain/Notification/extension_counter_notifications_for.html.twig', [ - 'counter' => $this->notificationPresence->countNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId), + 'counter' => $this->notificationPresence->countNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId, $more), ] ); } - public function countNotificationsFor(string $relatedEntityClass, int $relatedEntityId, array $options = []): array + /** + * @param list $more + */ + public function countNotificationsFor(string $relatedEntityClass, int $relatedEntityId, array $more = [], array $options = []): array { - return $this->notificationPresence->countNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId); + return $this->notificationPresence->countNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId, $more); } - public function listNotificationsFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string + /** + * @param list $more + */ + public function listNotificationsFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $more = [], array $options = []): string { - $notifications = $this->notificationPresence->getNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId); + $notifications = $this->notificationPresence->getNotificationsForClassAndEntity($relatedEntityClass, $relatedEntityId, $more); if ([] === $notifications) { return ''; diff --git a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php index 72f9ea5d1..fe42578d9 100644 --- a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php @@ -29,6 +29,15 @@ final class NotificationRepository implements ObjectRepository private EntityRepository $repository; + private const BASE_COUNTER_SQL = <<<'SQL' + SELECT + SUM((EXISTS (SELECT 1 AS c FROM chill_main_notification_addresses_unread cmnau WHERE user_id = :userid and cmnau.notification_id = cmn.id))::int) AS unread, + SUM((cmn.sender_id = :userid)::int) AS sent, + SUM((EXISTS (SELECT 1 AS c FROM chill_main_notification_addresses_user cmnau_all WHERE user_id = :userid and cmnau_all.notification_id = cmn.id))::int) + SUM((cmn.sender_id = :userid)::int) AS total + FROM chill_main_notification cmn + SQL; + + public function __construct(EntityManagerInterface $entityManager) { $this->em = $entityManager; @@ -51,29 +60,45 @@ final class NotificationRepository implements ObjectRepository ->getSingleScalarResult(); } - public function countNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user): array + /** + * @param list $more + * @return array{unread: int, sent: int, total: int} + */ + public function countNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user, array $more = []): array { - if (null === $this->notificationByRelatedEntityAndUserAssociatedStatement) { - $sql = - 'SELECT - SUM((EXISTS (SELECT 1 AS c FROM chill_main_notification_addresses_unread cmnau WHERE user_id = :userid and cmnau.notification_id = cmn.id))::int) AS unread, - SUM((cmn.sender_id = :userid)::int) AS sent, - COUNT(cmn.*) AS total - FROM chill_main_notification cmn - WHERE relatedentityclass = :relatedEntityClass AND relatedentityid = :relatedEntityId AND sender_id IS NOT NULL'; + $sqlParams = ['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId, 'userid' => $user->getId()]; - $this->notificationByRelatedEntityAndUserAssociatedStatement = - $this->em->getConnection()->prepare($sql); + if ([] === $more) { + if (null === $this->notificationByRelatedEntityAndUserAssociatedStatement) { + $sql = self::BASE_COUNTER_SQL . ' WHERE relatedentityclass = :relatedEntityClass AND relatedentityid = :relatedEntityId AND sender_id IS NOT NULL'; + + $this->notificationByRelatedEntityAndUserAssociatedStatement = + $this->em->getConnection()->prepare($sql); + } + + $results = $this->notificationByRelatedEntityAndUserAssociatedStatement + ->executeQuery($sqlParams); + + $result = $results->fetchAssociative(); + + $results->free(); + } else { + $wheres = []; + foreach ([ + ['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId], + ...$more + ] as $k => ['relatedEntityClass' => $relClass, 'relatedEntityId' => $relId]) { + $wheres[] = "(relatedEntityClass = :relatedEntityClass_{$k} AND relatedEntityId = :relatedEntityId_{$k})"; + $sqlParams["relatedEntityClass_{$k}"] = $relClass; + $sqlParams["relatedEntityId_{$k}"] = $relId; + } + + $sql = self::BASE_COUNTER_SQL . ' WHERE sender_id IS NOT NULL AND (' . implode(' OR ', $wheres) . ')'; + + $result = $this->em->getConnection()->fetchAssociative($sql, $sqlParams); } - $results = $this->notificationByRelatedEntityAndUserAssociatedStatement - ->executeQuery(['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId, 'userid' => $user->getId()]); - - $result = $results->fetchAssociative(); - - $results->free(); - - return $result; + return array_map(fn (?int $number) => $number ?? 0, $result); } public function countUnreadByUser(User $user): int @@ -167,8 +192,8 @@ final class NotificationRepository implements ObjectRepository } /** - * @param mixed|null $limit - * @param mixed|null $offset + * @param int|null $limit + * @param int|null $offset * * @return Notification[] */ @@ -178,13 +203,15 @@ final class NotificationRepository implements ObjectRepository } /** + * @param list $more * @return array|Notification[] */ - public function findNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user): array + public function findNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user, array $more): array { return - $this->buildQueryNotificationByRelatedEntityAndUserAssociated($relatedEntityClass, $relatedEntityId, $user) + $this->buildQueryNotificationByRelatedEntityAndUserAssociated($relatedEntityClass, $relatedEntityId, $user, $more) ->select('n') + ->addOrderBy('n.date', 'DESC') ->getQuery() ->getResult(); } @@ -222,13 +249,36 @@ final class NotificationRepository implements ObjectRepository return Notification::class; } - private function buildQueryNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user): QueryBuilder + /** + * @param list $more + */ + private function buildQueryNotificationByRelatedEntityAndUserAssociated(string $relatedEntityClass, int $relatedEntityId, User $user, array $more = []): QueryBuilder { $qb = $this->repository->createQueryBuilder('n'); + // add condition for related entity (in main arguments, and in more) + $or = $qb->expr()->orX($qb->expr()->andX( + $qb->expr()->eq('n.relatedEntityClass', ':relatedEntityClass'), + $qb->expr()->eq('n.relatedEntityId', ':relatedEntityId') + )); $qb - ->where($qb->expr()->eq('n.relatedEntityClass', ':relatedEntityClass')) - ->andWhere($qb->expr()->eq('n.relatedEntityId', ':relatedEntityId')) + ->setParameter('relatedEntityClass', $relatedEntityClass) + ->setParameter('relatedEntityId', $relatedEntityId); + + foreach ($more as $k => ['relatedEntityClass' => $relatedClass, 'relatedEntityId' => $relatedId]) { + $or->add( + $qb->expr()->andX( + $qb->expr()->eq('n.relatedEntityClass', ':relatedEntityClass_'.$k), + $qb->expr()->eq('n.relatedEntityId', ':relatedEntityId_'.$k) + ) + ); + $qb + ->setParameter('relatedEntityClass_'.$k, $relatedClass) + ->setParameter('relatedEntityId_'.$k, $relatedId); + } + + $qb + ->andWhere($or) ->andWhere($qb->expr()->isNotNull('n.sender')) ->andWhere( $qb->expr()->orX( @@ -236,8 +286,6 @@ final class NotificationRepository implements ObjectRepository $qb->expr()->eq('n.sender', ':user') ) ) - ->setParameter('relatedEntityClass', $relatedEntityClass) - ->setParameter('relatedEntityId', $relatedEntityId) ->setParameter('user', $user); return $qb; diff --git a/src/Bundle/ChillMainBundle/Repository/PermissionsGroupRepository.php b/src/Bundle/ChillMainBundle/Repository/PermissionsGroupRepository.php index 64863bf8c..0f57d0bac 100644 --- a/src/Bundle/ChillMainBundle/Repository/PermissionsGroupRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/PermissionsGroupRepository.php @@ -38,6 +38,19 @@ final class PermissionsGroupRepository implements ObjectRepository return $this->repository->findAll(); } + /** + * @return list + */ + public function findAllOrderedAlphabetically(): array + { + $qb = $this->repository->createQueryBuilder('pg'); + + return $qb->select(['pg', 'pg.name AS HIDDEN sort_name']) + ->orderBy('sort_name') + ->getQuery() + ->getResult(); + } + /** * @param mixed|null $limit * @param mixed|null $offset diff --git a/src/Bundle/ChillMainBundle/Repository/RegroupmentRepository.php b/src/Bundle/ChillMainBundle/Repository/RegroupmentRepository.php index a28f2f341..c115d5d98 100644 --- a/src/Bundle/ChillMainBundle/Repository/RegroupmentRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/RegroupmentRepository.php @@ -14,6 +14,8 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\Regroupment; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\NonUniqueResultException; +use Doctrine\ORM\NoResultException; use Doctrine\Persistence\ObjectRepository; final class RegroupmentRepository implements ObjectRepository @@ -59,6 +61,30 @@ final class RegroupmentRepository implements ObjectRepository return $this->repository->findOneBy($criteria, $orderBy); } + /** + * @throws NonUniqueResultException + * @throws NoResultException + */ + public function findOneByName(string $name): ?Regroupment + { + return $this->repository->createQueryBuilder('r') + ->where('LOWER(r.name) = LOWER(:searched)') + ->setParameter('searched', $name) + ->getQuery() + ->getSingleResult(); + } + + /** + * @return array + */ + public function findRegroupmentAssociatedToNoCenter(): array + { + return $this->repository->createQueryBuilder('r') + ->where('SIZE(r.centers) = 0') + ->getQuery() + ->getResult(); + } + public function getClassName() { return Regroupment::class; diff --git a/src/Bundle/ChillMainBundle/Repository/UserRepository.php b/src/Bundle/ChillMainBundle/Repository/UserRepository.php index a26ae04ad..4d0cc9698 100644 --- a/src/Bundle/ChillMainBundle/Repository/UserRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/UserRepository.php @@ -13,6 +13,7 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\GroupCenter; use Chill\MainBundle\Entity\User; +use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\NoResultException; @@ -76,6 +77,81 @@ final class UserRepository implements UserRepositoryInterface return $this->repository->findAll(); } + /** + * @param string $lang + */ + public function findAllAsArray(string $lang): iterable + { + $dql = sprintf(<<<'DQL' + SELECT + u.id AS id, + u.username AS username, + u.email, + u.enabled, + IDENTITY(u.civility) AS civility_id, + JSON_EXTRACT(civility.abbreviation, :lang) AS civility_abbreviation, + JSON_EXTRACT(civility.name, :lang) AS civility_name, + u.label, + mainCenter.id AS mainCenter_id, + mainCenter.name AS mainCenter_name, + IDENTITY(u.mainScope) AS mainScope_id, + JSON_EXTRACT(mainScope.name, :lang) AS mainScope_name, + IDENTITY(u.userJob) AS userJob_id, + JSON_EXTRACT(userJob.label, :lang) AS userJob_name, + currentLocation.id AS currentLocation_id, + currentLocation.name AS currentLocation_name, + mainLocation.id AS mainLocation_id, + mainLocation.name AS mainLocation_name, + u.absenceStart + FROM Chill\MainBundle\Entity\User u + LEFT JOIN u.civility civility + LEFT JOIN u.currentLocation currentLocation + LEFT JOIN u.mainLocation mainLocation + LEFT JOIN u.mainCenter mainCenter + LEFT JOIN u.mainScope mainScope + LEFT JOIN u.userJob userJob + ORDER BY u.label + DQL); + + $query = $this->entityManager->createQuery($dql) + ->setHydrationMode(AbstractQuery::HYDRATE_ARRAY) + ->setParameter('lang', $lang) + ; + + foreach ($query->toIterable() as $u) { + yield $u; + } + } + + public function findAllUserACLAsArray(): iterable + { + $sql = <<<'SQL' + SELECT + u.id, + u.username, + u.email, + u.label, + u.enabled, + c.id AS center_id, + c.name AS center_name, + pg.id AS permissionsGroup_id, + pg.name AS permissionsGroup_name + FROM users u + LEFT JOIN user_groupcenter ON u.id = user_groupcenter.user_id + LEFT JOIN group_centers ON user_groupcenter.groupcenter_id = group_centers.id + LEFT JOIN centers c on group_centers.center_id = c.id + LEFT JOIN permission_groups pg on group_centers.permissionsgroup_id = pg.id + ORDER BY u.username, c.name, pg.name + SQL; + + $query = $this->entityManager->getConnection()->executeQuery($sql); + + foreach ($query->iterateAssociative() as $u) { + yield $u; + } + } + + /** * @param mixed|null $limit * @param mixed|null $offset diff --git a/src/Bundle/ChillMainBundle/Repository/UserRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/UserRepositoryInterface.php index c869e1b82..b405e2d23 100644 --- a/src/Bundle/ChillMainBundle/Repository/UserRepositoryInterface.php +++ b/src/Bundle/ChillMainBundle/Repository/UserRepositoryInterface.php @@ -14,6 +14,9 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\User; use Doctrine\Persistence\ObjectRepository; +/** + * @template ObjectRepository + */ interface UserRepositoryInterface extends ObjectRepository { public function countBy(array $criteria): int; @@ -24,20 +27,25 @@ interface UserRepositoryInterface extends ObjectRepository public function countByUsernameOrEmail(string $pattern): int; - public function find($id, $lockMode = null, $lockVersion = null): ?User; - /** - * @return User[] - */ - public function findAll(): array; - - /** - * @param mixed|null $limit - * @param mixed|null $offset + * Find a list of all users. * - * @return User[] + * The main purpose for this method is to provide a lightweight list of all users in the database. + * + * @param string $lang The lang to display all the translatable string (no fallback if not present) + * @return iterable */ - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array; + public function findAllAsArray(string $lang): iterable; + + /** + * Find a list of permissions associated to each users. + * + * The main purpose for this method is to provide a lightweight list of all permissions group and center + * associated to each user. + * + * @return iterable + */ + public function findAllUserACLAsArray(): iterable; /** * @return array|User[] @@ -53,8 +61,6 @@ interface UserRepositoryInterface extends ObjectRepository public function findByUsernameOrEmail(string $pattern, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array; - public function findOneBy(array $criteria, ?array $orderBy = null): ?User; - public function findOneByUsernameOrEmail(string $pattern): ?User; /** @@ -68,6 +74,4 @@ interface UserRepositoryInterface extends ObjectRepository * @param mixed $flag */ public function findUsersHavingFlags($flag, array $amongstUsers = []): array; - - public function getClassName(): string; } diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig index 9e37dc2cd..2e2dc0ec6 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig @@ -39,8 +39,13 @@ {{ 'This will eventually restrict your possibilities in filtering the data.'|trans }}

      {{ 'Center'|trans }}

      + {{ form_widget(form.centers.center) }} +
      + +
      + {% if form.centers.regroupment is defined %}

      {{ 'Pick aggregated centers'|trans }}

      {{ form_widget(form.centers.regroupment) }} @@ -53,3 +58,15 @@
    {% endblock content %} + +{% block js %} + + + +{% endblock js %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/extension_counter_notifications_for.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/extension_counter_notifications_for.html.twig index d92e4230e..0e64e9f6a 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Notification/extension_counter_notifications_for.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/extension_counter_notifications_for.html.twig @@ -9,4 +9,4 @@ {{ 'notification.counter unread notifications'|trans({'unread': counter.unread }) }} {% endif %} - \ No newline at end of file + diff --git a/src/Bundle/ChillMainBundle/Resources/views/User/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/User/index.html.twig index e4e7d70d8..c5cd73d18 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/User/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/User/index.html.twig @@ -98,6 +98,18 @@
  • {{'Back to the admin'|trans}}
  • + +
  • + +
  • {{ 'Create'|trans }}
  • diff --git a/src/Bundle/ChillMainBundle/config/services/controller.yaml b/src/Bundle/ChillMainBundle/config/services/controller.yaml index 16c545a81..428147961 100644 --- a/src/Bundle/ChillMainBundle/config/services/controller.yaml +++ b/src/Bundle/ChillMainBundle/config/services/controller.yaml @@ -37,3 +37,6 @@ services: Chill\MainBundle\Controller\RegroupmentController: autowire: true autoconfigure: true + + Chill\MainBundle\Controller\UserExportController: + tags: ['controller.service_arguments'] diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 14de6dbf0..78de523c4 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -285,6 +285,8 @@ The export will contains only data from the picked centers.: L'export ne contien This will eventually restrict your possibilities in filtering the data.: Les possibilités de filtrages seront adaptées aux droits de consultation pour les centres choisis. Go to export options: Vers la préparation de l'export Pick aggregated centers: Regroupement de centres +uncheck all centers: Désélectionner tous les centres +check all centers: Sélectionner tous les centres # export creation step 'export' : choose aggregators, filtering and formatter Formatter: Mise en forme Choose the formatter: Choisissez le format d'export voulu. @@ -610,3 +612,32 @@ absence: You are listed as absent, as of: Votre absence est indiquée à partir du No absence listed: Aucune absence indiquée. Is absent: Absent? + +admin: + users: + export_list_csv: Liste des utilisateurs (format CSV) + export_permissions_csv: Association utilisateurs - groupes de permissions - centre (format CSV) + export: + id: Identifiant + username: Nom d'utilisateur + email: Courriel + enabled: Activé + civility_id: Identifiant civilité + civility_abbreviation: Abbréviation civilité + civility_name: Civilité + label: Label + mainCenter_id: Identifiant centre principal + mainCenter_name: Centre principal + mainScope_id: Identifiant service principal + mainScope_name: Service principal + userJob_id: Identifiant métier + userJob_name: Métier + currentLocation_id: Identifiant localisation actuelle + currentLocation_name: Localisation actuelle + mainLocation_id: Identifiant localisation principale + mainLocation_name: Localisation principale + absenceStart: Absent à partir du + center_id: Identifiant du centre + center_name: Centre + permissionsGroup_id: Identifiant du groupe de permissions + permissionsGroup_name: Groupe de permissions diff --git a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Events/UserRefEventSubscriber.php b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Events/UserRefEventSubscriber.php index 8d7249928..50e7dcabb 100644 --- a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Events/UserRefEventSubscriber.php +++ b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Events/UserRefEventSubscriber.php @@ -51,7 +51,10 @@ class UserRefEventSubscriber implements EventSubscriberInterface public function onStateEntered(EnteredEvent $enteredEvent): void { - if ($enteredEvent->getMarking()->has(AccompanyingPeriod::STEP_CONFIRMED)) { + if ( + $enteredEvent->getMarking()->has(AccompanyingPeriod::STEP_CONFIRMED) + and $enteredEvent->getTransition()->getName() === 'confirm' + ) { $this->onPeriodConfirmed($enteredEvent->getSubject()); } } diff --git a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeCronjob.php b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeCronjob.php index bec31d45d..f637e70b9 100644 --- a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeCronjob.php +++ b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeCronjob.php @@ -27,7 +27,7 @@ readonly class AccompanyingPeriodStepChangeCronjob implements CronJobInterface { $now = $this->clock->now(); - if ($now->sub(new \DateInterval('P1D')) < $cronJobExecution->getLastStart()) { + if (null !== $cronJobExecution && $now->sub(new \DateInterval('P1D')) < $cronJobExecution->getLastStart()) { return false; } diff --git a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeMessageHandler.php b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeMessageHandler.php index 881b3999a..4a9873c6d 100644 --- a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeMessageHandler.php +++ b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeMessageHandler.php @@ -34,5 +34,4 @@ class AccompanyingPeriodStepChangeMessageHandler implements MessageHandlerInterf ($this->changer)($period, $message->getTransition()); } - } diff --git a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeRequestor.php b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeRequestor.php index 3d4979608..f1c3563fc 100644 --- a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeRequestor.php +++ b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/Lifecycle/AccompanyingPeriodStepChangeRequestor.php @@ -84,5 +84,4 @@ class AccompanyingPeriodStepChangeRequestor $this->messageBus->dispatch(new AccompanyingPeriodStepChangeRequestMessage($accompanyingPeriodId, 'mark_active')); } } - } diff --git a/src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php b/src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php index 13a62094d..4e2f7468e 100644 --- a/src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php +++ b/src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php @@ -13,7 +13,11 @@ namespace Chill\PersonBundle\Actions\Remove; use Chill\PersonBundle\Actions\ActionEvent; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Household\HouseholdMember; +use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Entity\Relationships\Relationship; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -42,7 +46,7 @@ class PersonMove protected $eventDispatcher; public function __construct( - EntityManagerInterface $em, + EntityManagerInterface $em, EventDispatcherInterface $eventDispatcher ) { $this->em = $em; @@ -84,8 +88,11 @@ class PersonMove } foreach ($metadata->getAssociationMappings() as $field => $mapping) { - if (Person::class === $mapping['targetEntity']) { - if (in_array($metadata->getName(), $toDelete, true)) { + if (in_array($mapping['sourceEntity'], $this->getIgnoredEntities(), true)) { + continue; + } + if (Person::class === $mapping['targetEntity'] and true === $mapping['isOwningSide']) { + if (in_array($mapping['sourceEntity'], $toDelete, true)) { $sql = $this->createDeleteSQL($metadata, $from, $field); $event = new ActionEvent( $from->getId(), @@ -120,7 +127,7 @@ class PersonMove return $sqls; } - protected function createDeleteSQL(ClassMetadata $metadata, Person $from, $field): string + private function createDeleteSQL(ClassMetadata $metadata, Person $from, $field): string { $mapping = $metadata->getAssociationMapping($field); @@ -137,26 +144,41 @@ class PersonMove ); } - protected function createMoveSQL(ClassMetadata $metadata, Person $from, Person $to, $field): string + private function createMoveSQL(ClassMetadata $metadata, Person $from, Person $to, $field): string { $mapping = $metadata->getAssociationMapping($field); // Set part of the query, aka in "UPDATE table SET " $sets = []; - - foreach ($mapping['joinColumns'] as $columns) { - $sets[] = sprintf('%s = %d', $columns['name'], $to->getId()); - } - $conditions = []; + $tableName = ''; - foreach ($mapping['joinColumns'] as $columns) { - $conditions[] = sprintf('%s = %d', $columns['name'], $from->getId()); + if (array_key_exists('joinTable', $mapping)) { + $tableName = (null !== ($mapping['joinTable']['schema'] ?? null) ? $mapping['joinTable']['schema'] . '.' : '') + . $mapping['joinTable']['name']; + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $columns) { + $sets[] = sprintf('%s = %d', $columns['name'], $to->getId()); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $columns) { + $conditions[] = sprintf('%s = %d', $columns['name'], $from->getId()); + } + } elseif (array_key_exists('joinColumns', $mapping)) { + $tableName = $this->getTableName($metadata); + foreach ($mapping['joinColumns'] as $columns) { + $sets[] = sprintf('%s = %d', $columns['name'], $to->getId()); + } + + + foreach ($mapping['joinColumns'] as $columns) { + $conditions[] = sprintf('%s = %d', $columns['name'], $from->getId()); + } } return sprintf( 'UPDATE %s SET %s WHERE %s', - $this->getTableName($metadata), + $tableName, implode(' ', $sets), implode(' AND ', $conditions) ); @@ -166,10 +188,23 @@ class PersonMove * return an array of classes where entities should be deleted * instead of moved. */ - protected function getDeleteEntities(): array + private function getDeleteEntities(): array { return [ - AccompanyingPeriod::class, + Person\PersonCenterHistory::class, + HouseholdMember::class, + AccompanyingPeriodParticipation::class, + AccompanyingPeriod\AccompanyingPeriodWork::class, + Relationship::class + ]; + } + + private function getIgnoredEntities(): array + { + return [ + Person\PersonCurrentAddress::class, + PersonHouseholdAddress::class, + Person\PersonCenterCurrent::class, ]; } diff --git a/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php b/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php index 91615935c..8085732ba 100644 --- a/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php +++ b/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php @@ -42,10 +42,18 @@ final class ImportSocialWorkMetadata extends Command protected function configure() { + $description = 'Imports a structured table containing social issues, social actions, objectives, results and evaluations.'; + $help = 'File to csv format, no headers, semi-colon as delimiter, datas sorted by alphabetical order, column after column.'. PHP_EOL + . 'Columns are: social issues parent, social issues child, social actions parent, social actions child, goals, results, evaluations.'. PHP_EOL + . PHP_EOL + . 'See social_work_metadata.csv as example.'. PHP_EOL; + $this ->setName('chill:person:import-socialwork') ->addOption('filepath', 'f', InputOption::VALUE_REQUIRED, 'The file to import.') - ->addOption('language', 'l', InputOption::VALUE_OPTIONAL, 'The default language'); + ->addOption('language', 'l', InputOption::VALUE_OPTIONAL, 'The default language') + ->setDescription($description) + ->setHelp($help); } protected function execute(InputInterface $input, OutputInterface $output) diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php b/src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php index 928112d2e..7ade542e0 100644 --- a/src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php +++ b/src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php @@ -247,7 +247,7 @@ class PersonDuplicateController extends Controller ); $duplicatePersons = $this->similarPersonMatcher-> - matchPerson($person, $personNotDuplicateRepository, 0.5, SimilarPersonMatcher::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL); + matchPerson($person, 0.5, SimilarPersonMatcher::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL, false); $notDuplicatePersons = $personNotDuplicateRepository->findNotDuplicatePerson($person); @@ -264,14 +264,14 @@ class PersonDuplicateController extends Controller $nb_activity = $em->getRepository(Activity::class)->findBy(['person' => $id]); $nb_document = $em->getRepository(PersonDocument::class)->findBy(['person' => $id]); - $nb_event = $em->getRepository(Participation::class)->findBy(['person' => $id]); + // $nb_event = $em->getRepository(Participation::class)->findBy(['person' => $id]); $nb_task = $em->getRepository(SingleTask::class)->countByParameters(['person' => $id]); $person = $em->getRepository(Person::class)->findOneBy(['id' => $id]); return [ 'nb_activity' => count($nb_activity), 'nb_document' => count($nb_document), - 'nb_event' => count($nb_event), + // 'nb_event' => count($nb_event), 'nb_task' => $nb_task, 'nb_addresses' => count($person->getAddresses()), ]; diff --git a/src/Bundle/ChillPersonBundle/Controller/UserAccompanyingPeriodController.php b/src/Bundle/ChillPersonBundle/Controller/UserAccompanyingPeriodController.php index 658fdb7be..1b67014b3 100644 --- a/src/Bundle/ChillPersonBundle/Controller/UserAccompanyingPeriodController.php +++ b/src/Bundle/ChillPersonBundle/Controller/UserAccompanyingPeriodController.php @@ -12,9 +12,11 @@ declare(strict_types=1); namespace Chill\PersonBundle\Controller; use Chill\MainBundle\Pagination\PaginatorFactory; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class UserAccompanyingPeriodController extends AbstractController @@ -32,12 +34,24 @@ class UserAccompanyingPeriodController extends AbstractController /** * @Route("/{_locale}/person/accompanying-periods/my", name="chill_person_accompanying_period_user") */ - public function listAction(Request $request) + public function listAction(Request $request): Response { - $total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser(), 'step' => ['CONFIRMED', 'CLOSED']]); + $active = $request->query->getBoolean('active', true); + $steps = match ($active) { + true => [ + AccompanyingPeriod::STEP_CONFIRMED, + AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG, + AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT, + ], + false => [ + AccompanyingPeriod::STEP_CLOSED, + ] + }; + + $total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser(), 'step' => $steps]); $pagination = $this->paginatorFactory->create($total); $accompanyingPeriods = $this->accompanyingPeriodRepository->findBy( - ['user' => $this->getUser(), 'step' => ['CONFIRMED', 'CLOSED']], + ['user' => $this->getUser(), 'step' => $steps], ['openingDate' => 'DESC'], $pagination->getItemsPerPage(), $pagination->getCurrentPageFirstItemNumber() @@ -46,13 +60,14 @@ class UserAccompanyingPeriodController extends AbstractController return $this->render('@ChillPerson/AccompanyingPeriod/user_periods_list.html.twig', [ 'accompanyingPeriods' => $accompanyingPeriods, 'pagination' => $pagination, + 'active' => $active, ]); } /** * @Route("/{_locale}/person/accompanying-periods/my/drafts", name="chill_person_accompanying_period_draft_user") */ - public function listDraftsAction(Request $request) + public function listDraftsAction(): Response { $total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser(), 'step' => 'DRAFT']); $pagination = $this->paginatorFactory->create($total); diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php index 5361012b3..e3b2883be 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php @@ -59,6 +59,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues * ) * @Serializer\Groups({"read", "docgen:read"}) * @ORM\OrderBy({"startDate": "DESC", "id": "DESC"}) + * @var Collection * * @internal /!\ the serialization for write evaluations is handled in `AccompanyingPeriodWorkDenormalizer` */ @@ -278,6 +279,9 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues return $this->accompanyingPeriod; } + /** + * @return Collection + */ public function getAccompanyingPeriodWorkEvaluations(): Collection { return $this->accompanyingPeriodWorkEvaluations; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php index 8780f7d17..d35ffc900 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluation.php @@ -79,6 +79,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU * ) * @ORM\OrderBy({"createdAt": "DESC", "id": "DESC"}) * @Serializer\Groups({"read"}) + * @var Collection */ private Collection $documents; @@ -204,7 +205,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU } /** - * @return Collection + * @return Collection */ public function getDocuments() { diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php index 8a99c7eb4..74b8ed5f8 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php @@ -213,6 +213,10 @@ class Household return null; } + /** + * @Serializer\Groups({"docgen:read"}) + * @Serializer\SerializedName("current_composition") + */ public function getCurrentComposition(?DateTimeImmutable $at = null): ?HouseholdComposition { $at ??= new DateTimeImmutable('today'); diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdComposition.php b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdComposition.php index f11d1402b..65178c3ed 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdComposition.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdComposition.php @@ -44,6 +44,7 @@ class HouseholdComposition implements TrackCreationInterface, TrackUpdateInterfa /** * @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) * @Assert\GreaterThanOrEqual(propertyPath="startDate", groups={"Default", "household_composition"}) + * @Serializer\Groups({"docgen:read"}) */ private ?DateTimeImmutable $endDate = null; @@ -56,6 +57,7 @@ class HouseholdComposition implements TrackCreationInterface, TrackUpdateInterfa /** * @ORM\ManyToOne(targetEntity=HouseholdCompositionType::class) * @ORM\JoinColumn(nullable=false) + * @Serializer\Groups({"docgen:read"}) */ private ?HouseholdCompositionType $householdCompositionType = null; @@ -71,12 +73,14 @@ class HouseholdComposition implements TrackCreationInterface, TrackUpdateInterfa * @ORM\Column(type="integer", nullable=true, options={"default": null}) * @Assert\NotNull * @Assert\GreaterThanOrEqual(0, groups={"Default", "household_composition"}) + * @Serializer\Groups({"docgen:read"}) */ private ?int $numberOfChildren = null; /** * @ORM\Column(type="date_immutable", nullable=false) * @Assert\NotNull(groups={"Default", "household_composition"}) + * @Serializer\Groups({"docgen:read"}) */ private ?DateTimeImmutable $startDate = null; diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php index 6d67fbbbb..6de73de0d 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php @@ -45,7 +45,7 @@ class PersonCenterCurrent private ?int $id = null; /** - * @ORM\ManyToOne(targetEntity=Person::class, inversedBy="centerCurrent") + * @ORM\OneToOne(targetEntity=Person::class, inversedBy="centerCurrent") */ private Person $person; diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php index 4ac96aac1..d001cbef7 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php @@ -98,7 +98,7 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface 'acp_geog_units' ); - $qb->andWhere($qb->expr()->eq('acp_geog_units.layer', ':acp_geog_unit_layer')); + $qb->andWhere($qb->expr()->in('acp_geog_units.layer', ':acp_geog_unit_layer')); $qb->setParameter('acp_geog_unit_layer', $data['level']); @@ -129,6 +129,8 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface 'class' => GeographicalUnitLayer::class, 'choices' => $this->geographicalUnitLayerRepository->findAllHavingUnits(), 'choice_label' => fn (GeographicalUnitLayer $item) => $this->translatableStringHelper->localize($item->getName()), + 'multiple' => true, + 'expanded' => true, ]); } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php index ff72994ba..5d73c1fdd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php @@ -101,6 +101,8 @@ class GeographicalUnitAggregator implements AggregatorInterface 'class' => GeographicalUnitLayer::class, 'choices' => $this->geographicalUnitLayerRepository->findAllHavingUnits(), 'choice_label' => fn (GeographicalUnitLayer $item) => $this->translatableStringHelper->localize($item->getName()), + 'multiple' => true, + 'expanded' => true, ]); } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListHouseholdInPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListHouseholdInPeriod.php index 24d929c00..8894d145d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListHouseholdInPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListHouseholdInPeriod.php @@ -107,7 +107,6 @@ class ListHouseholdInPeriod implements ListInterface, GroupedExportInterface return $this->aggregateStringHelper->getLabelMulti($key, $values, 'export.list.household.' . $key); case 'compositionType': - //dump($values); return $this->translatableStringHelper->getLabel($key, $values, 'export.list.household.' . $key); default: diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilterBetweenDates.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilterBetweenDates.php new file mode 100644 index 000000000..9033c64b3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilterBetweenDates.php @@ -0,0 +1,125 @@ + AccompanyingPeriod::STEP_DRAFT, + 'course.confirmed' => AccompanyingPeriod::STEP_CONFIRMED, + 'course.closed' => AccompanyingPeriod::STEP_CLOSED, + 'course.inactive_short' => AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT, + 'course.inactive_long' => AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG, + ]; + + private RollingDateConverterInterface $rollingDateConverter; + + private TranslatorInterface $translator; + + public function __construct( + RollingDateConverterInterface $rollingDateConverter, + TranslatorInterface $translator + ) { + $this->rollingDateConverter = $rollingDateConverter; + $this->translator = $translator; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $alias = 'acp_filter_by_step_between_dat_alias'; + $steps = 'acp_filter_by_step_between_dat_steps'; + $from = 'acp_filter_by_step_between_dat_from'; + $to = 'acp_filter_by_step_between_dat_to'; + + $qb + ->andWhere( + $qb->expr()->exists( + "SELECT 1 FROM " . AccompanyingPeriod\AccompanyingPeriodStepHistory::class . " {$alias} " . + "WHERE {$alias}.step IN (:{$steps}) AND OVERLAPSI ({$alias}.startDate, {$alias}.endDate),(:{$from}, :{$to}) = TRUE " . + "AND {$alias}.period = acp" + ) + ) + ->setParameter($from, $this->rollingDateConverter->convert($data['date_from'])) + ->setParameter($to, $this->rollingDateConverter->convert($data['date_to'])) + ->setParameter($steps, $data['accepted_steps_multi']); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('accepted_steps_multi', ChoiceType::class, [ + 'label' => 'export.filter.course.by_step.steps', + 'choices' => self::STEPS, + 'multiple' => true, + 'expanded' => true, + ]) + ->add('date_from', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_step.date_from', + ]) + ->add('date_to', PickRollingDateType::class, [ + 'label' => 'export.filter.course.by_step.date_to', + ]); + } + + public function getFormDefaultData(): array + { + return [ + 'accepted_steps_multi' => self::DEFAULT_CHOICE, + 'date_from' => new RollingDate(RollingDate::T_YEAR_CURRENT_START), + 'date_to' => new RollingDate(RollingDate::T_TODAY), + ]; + } + + public function describeAction($data, $format = 'string') + { + $steps = array_map(fn (string $step) => $this->translator->trans(array_flip(self::STEPS)[$step]), $data['accepted_steps_multi']); + + return ['export.filter.course.by_step.Filtered by steps: only %step% and between %date_from% and %date_to%', [ + '%step%' => implode(', ', $steps), + '%date_from%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'), + '%date_to%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'), + ]]; + } + + public function getTitle() + { + return 'export.filter.course.by_step.Filter by step between dates'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilterOnDate.php similarity index 78% rename from src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php rename to src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilterOnDate.php index 8ee798c07..357ed6f30 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilterOnDate.php @@ -23,11 +23,15 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; use function in_array; -class StepFilter implements FilterInterface +class StepFilterOnDate implements FilterInterface { private const A = 'acp_filter_bystep_stephistories'; - private const DEFAULT_CHOICE = AccompanyingPeriod::STEP_CONFIRMED; + private const DEFAULT_CHOICE = [ + AccompanyingPeriod::STEP_CONFIRMED, + AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT, + AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG, + ]; private const P = 'acp_step_filter_date'; @@ -79,7 +83,7 @@ class StepFilter implements FilterInterface $qb->expr()->in(self::A . '.step', ':acp_filter_by_step_steps') ) ->setParameter(self::P, $this->rollingDateConverter->convert($data['calc_date'])) - ->setParameter('acp_filter_by_step_steps', $data['accepted_steps']); + ->setParameter('acp_filter_by_step_steps', $data['accepted_steps_multi']); } public function applyOn() @@ -90,30 +94,36 @@ class StepFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { $builder - ->add('accepted_steps', ChoiceType::class, [ + ->add('accepted_steps_multi', ChoiceType::class, [ + 'label' => 'export.filter.course.by_step.steps', 'choices' => self::STEPS, - 'multiple' => false, + 'multiple' => true, 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, ]) ->add('calc_date', PickRollingDateType::class, [ 'label' => 'export.filter.course.by_step.date_calc', - 'data' => new RollingDate(RollingDate::T_TODAY), ]); } + public function getFormDefaultData(): array + { + return [ + 'accepted_steps_multi' => self::DEFAULT_CHOICE, + 'calc_date' => new RollingDate(RollingDate::T_TODAY), + ]; + } + public function describeAction($data, $format = 'string') { - $step = array_flip(self::STEPS)[$data['accepted_steps']]; + $steps = array_map(fn (string $step) => $this->translator->trans(array_flip(self::STEPS)[$step]), $data['accepted_steps_multi']); return ['Filtered by steps: only %step%', [ - '%step%' => $this->translator->trans($step), + '%step%' => implode(', ', $steps), ]]; } public function getTitle() { - return 'Filter by step'; + return 'export.filter.course.by_step.Filter by step'; } } diff --git a/src/Bundle/ChillPersonBundle/Menu/HouseholdMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/HouseholdMenuBuilder.php index 074c27027..13291bdd1 100644 --- a/src/Bundle/ChillPersonBundle/Menu/HouseholdMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/HouseholdMenuBuilder.php @@ -12,7 +12,9 @@ declare(strict_types=1); namespace Chill\PersonBundle\Menu; use Chill\MainBundle\Routing\LocalMenuBuilderInterface; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Knp\Menu\MenuItem; +use Symfony\Component\Security\Core\Security; use Symfony\Contracts\Translation\TranslatorInterface; class HouseholdMenuBuilder implements LocalMenuBuilderInterface @@ -22,9 +24,12 @@ class HouseholdMenuBuilder implements LocalMenuBuilderInterface */ protected $translator; - public function __construct(TranslatorInterface $translator) + private Security $security; + + public function __construct(TranslatorInterface $translator, Security $security) { $this->translator = $translator; + $this->security = $security; } public function buildMenu($menuId, MenuItem $menu, array $parameters): void @@ -53,12 +58,14 @@ class HouseholdMenuBuilder implements LocalMenuBuilderInterface ], ]) ->setExtras(['order' => 17]); - $menu->addChild($this->translator->trans('household.Accompanying period'), [ - 'route' => 'chill_person_household_accompanying_period', - 'routeParameters' => [ - 'household_id' => $household->getId(), - ], ]) - ->setExtras(['order' => 20]); + if ($this->security->isGranted(AccompanyingPeriodVoter::SEE, $parameters['household'])) { + $menu->addChild($this->translator->trans('household.Accompanying period'), [ + 'route' => 'chill_person_household_accompanying_period', + 'routeParameters' => [ + 'household_id' => $household->getId(), + ],]) + ->setExtras(['order' => 20]); + } $menu->addChild($this->translator->trans('household.Addresses'), [ 'route' => 'chill_person_household_addresses', diff --git a/src/Bundle/ChillPersonBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/PersonMenuBuilder.php index 39ac557fb..b7934c2b2 100644 --- a/src/Bundle/ChillPersonBundle/Menu/PersonMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/PersonMenuBuilder.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Repository\ResidentialAddressRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; +use Chill\PersonBundle\Security\Authorization\PersonVoter; use Knp\Menu\MenuItem; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Security\Core\Security; @@ -106,17 +107,19 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface ->setExtras([ 'order' => 99999, ]); - /* - $menu->addChild($this->translator->trans('Person duplicate'), [ - 'route' => 'chill_person_duplicate_view', - 'routeParameters' => [ - 'person_id' => $parameters['person']->getId(), - ], - ]) - ->setExtras([ - 'order' => 99999, - ]); - */ + + if ($this->security->isGranted(PersonVoter::DUPLICATE, $parameters['person'])) { + $menu->addChild($this->translator->trans('Person duplicate'), [ + 'route' => 'chill_person_duplicate_view', + 'routeParameters' => [ + 'person_id' => $parameters['person']->getId(), + ], + ]) + ->setExtras([ + 'order' => 99999, + ]); + } + if ( 'visible' === $this->showAccompanyingPeriod && $this->security->isGranted(AccompanyingPeriodVoter::SEE, $parameters['person']) diff --git a/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationHandler.php b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationHandler.php index 486209453..2492e9a41 100644 --- a/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationHandler.php +++ b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationHandler.php @@ -27,7 +27,7 @@ final class AccompanyingPeriodNotificationHandler implements NotificationHandler public function getTemplate(Notification $notification, array $options = []): string { - return 'ChillPersonBundle:AccompanyingPeriod:showInNotification.html.twig'; + return '@ChillPerson/AccompanyingPeriod/showInNotification.html.twig'; } public function getTemplateData(Notification $notification, array $options = []): array diff --git a/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodWorkEvaluationDocumentNotificationHandler.php b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodWorkEvaluationDocumentNotificationHandler.php new file mode 100644 index 000000000..4dcb03489 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodWorkEvaluationDocumentNotificationHandler.php @@ -0,0 +1,47 @@ +accompanyingPeriodWorkEvaluationDocumentRepository = $accompanyingPeriodWorkEvaluationDocumentRepository; + } + + public function getTemplate(Notification $notification, array $options = []): string + { + return '@ChillPerson/AccompanyingCourseWork/showEvaluationDocumentInNotification.html.twig'; + } + + public function getTemplateData(Notification $notification, array $options = []): array + { + return [ + 'notification' => $notification, + 'document' => $doc = $this->accompanyingPeriodWorkEvaluationDocumentRepository->find($notification->getRelatedEntityId()), + 'evaluation' => $doc?->getAccompanyingPeriodWorkEvaluation(), + ]; + } + + public function supports(Notification $notification, array $options = []): bool + { + return $notification->getRelatedEntityClass() === AccompanyingPeriodWorkEvaluationDocument::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodWorkNotificationHandler.php b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodWorkNotificationHandler.php new file mode 100644 index 000000000..b111c131f --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodWorkNotificationHandler.php @@ -0,0 +1,46 @@ +accompanyingPeriodWorkRepository = $accompanyingPeriodWorkRepository; + } + + public function getTemplate(Notification $notification, array $options = []): string + { + return '@ChillPerson/AccompanyingCourseWork/showInNotification.html.twig'; + } + + public function getTemplateData(Notification $notification, array $options = []): array + { + return [ + 'notification' => $notification, + 'work' => $this->accompanyingPeriodWorkRepository->find($notification->getRelatedEntityId()), + ]; + } + + public function supports(Notification $notification, array $options = []): bool + { + return $notification->getRelatedEntityClass() === AccompanyingPeriodWork::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodInfoRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodInfoRepository.php index 57925b131..0296d00f1 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodInfoRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodInfoRepository.php @@ -89,5 +89,4 @@ readonly class AccompanyingPeriodInfoRepository implements AccompanyingPeriodInf { return AccompanyingPeriodInfo::class; } - } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue index 755e4455c..ad386992e 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue @@ -122,7 +122,8 @@ + v-bind:evaluation="e" + v-bind:docAnchorId="this.docAnchorId"> @@ -296,7 +297,21 @@ @go-to-generate-workflow="goToGenerateWorkflow" > - + +
  • + + +
  • +
  • +
  • +
  • + + +
  • @@ -214,14 +228,17 @@ const i18n = { template_title: "Nom du template", browse: "Ajouter un document", replace: "Remplacer", - download: "Télécharger le fichier existant" + download: "Télécharger le fichier existant", + notification_notify_referrer: "Notifier le référent", + notification_notify_any: "Notifier d'autres utilisateurs", + notification_send: "Envoyer une notification", } } }; export default { name: "FormEvaluation", - props: ['evaluation'], + props: ['evaluation', 'docAnchorId'], components: { ckeditor: CKEditor.component, PickTemplate, @@ -260,8 +277,14 @@ export default { }, computed: { ...mapState([ - 'isPosting' + 'isPosting', + 'work', + 'me', ]), + AmIRefferer() { + return (!(this.$store.state.work.accompanyingPeriod.user && this.$store.state.me + && (this.$store.state.work.accompanyingPeriod.user.id !== this.$store.state.me.id))); + }, getTemplatesAvailables() { return this.$store.getters.getTemplatesAvailablesForEvaluation(this.evaluation.evaluation); }, @@ -390,6 +413,18 @@ export default { return this.$store.dispatch('submit', callback) .catch(e => { console.log(e); throw e; }); }, + goToGenerateDocumentNotification(document, tos){ + const callback = (data) => { + let evaluation = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key); + if (tos === true) { + window.location.assign(`/fr/notification/create?entityClass=Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument&entityId=${document.id}&tos[0]=${this.$store.state.work.accompanyingPeriod.user.id}&returnPath=/fr/person/accompanying-period/work/${evaluation.id}/edit`) + } else { + window.location.assign(`/fr/notification/create?entityClass=Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument&entityId=${document.id}&returnPath=/fr/person/accompanying-period/work/${evaluation.id}/edit`) + } + }; + return this.$store.dispatch('submit', callback) + .catch(e => {console.log(e); throw e}); + } }, } @@ -402,4 +437,19 @@ export default { ul.document-upload { justify-content: flex-start; } + + .bg-blink{ + color: #050000; + padding: 10px; + display: inline-block; + border-radius: 5px; + animation: blinkingBackground 2.2s infinite; + animation-iteration-count: 2; + } + + @keyframes blinkingBackground{ + 0% { background-color: #ed776d;} + 50% { background-color: #ffffff;} + 100% { background-color: #ed776d;} + } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js index 47e4b2d3f..9a46b741a 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js @@ -35,6 +35,7 @@ const store = createStore({ referrers: window.accompanyingCourseWork.referrers, isPosting: false, errors: [], + me: null }, getters: { socialAction(state) { @@ -130,6 +131,9 @@ const store = createStore({ } }, mutations: { + setWhoAmiI(state, me) { + state.me = me; + }, setEvaluationsPicked(state, evaluations) { state.evaluationsPicked = evaluations.map((e, index) => { var k = Object.assign(e, { @@ -385,6 +389,19 @@ const store = createStore({ }, }, actions: { + getWhoAmI({ commit }) { + let url = `/api/1.0/main/whoami.json`; + window.fetch(url) + .then(response => { + if (response.ok) { + return response.json(); + } + throw { m: 'Error while retriving results for goal', s: response.status, b: response.body }; + }) + .then(data => { + commit('setWhoAmiI', data); + }); + }, updateThirdParty({ commit }, payload) { commit('updateThirdParty', payload); }, @@ -514,6 +531,7 @@ store.commit('setEvaluationsPicked', window.accompanyingCourseWork.accompanyingP store.dispatch('getReachablesResultsForAction'); store.dispatch('getReachablesGoalsForAction'); store.dispatch('getReachablesEvaluationsForAction'); +store.dispatch('getWhoAmI'); store.state.evaluationsPicked.forEach(evaluation => { store.dispatch('fetchTemplatesAvailablesForEvaluation', evaluation.evaluation) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue index 740eb0039..2576efd35 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue @@ -207,7 +207,7 @@