mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge remote-tracking branch 'origin/master' into testing
This commit is contained in:
commit
f7be53f790
6
.changes/header.tpl.md
Normal file
6
.changes/header.tpl.md
Normal file
@ -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).
|
0
.changes/unreleased/.gitkeep
Normal file
0
.changes/unreleased/.gitkeep
Normal file
677
.changes/v2.0.0.md
Normal file
677
.changes/v2.0.0.md
Normal file
@ -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
|
||||
|
||||
<!--
|
||||
|
||||
Coming soon...
|
||||
|
||||
DO NOT ADD unreleased items here. Add them under "Unreleased" title
|
||||
|
||||
### Test release yyyy-mm-dd
|
||||
|
||||
-->
|
||||
|
||||
## Stable releases
|
||||
|
||||
No stable releases for v2+
|
||||
|
17
.changes/v2.1.0.md
Normal file
17
.changes/v2.1.0.md
Normal file
@ -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
|
12
.changes/v2.2.0.md
Normal file
12
.changes/v2.2.0.md
Normal file
@ -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
|
34
.changie.yaml
Normal file
34
.changie.yaml
Normal file
@ -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_
|
49
CHANGELOG.md
49
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
|
||||
|
||||
<!-- write down unreleased development here -->
|
||||
* [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.
|
||||
|
@ -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');
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ class CalculatorResult
|
||||
|
||||
public $label;
|
||||
|
||||
public $result;
|
||||
public float $result;
|
||||
|
||||
public $type;
|
||||
}
|
||||
|
@ -16,8 +16,14 @@
|
||||
<td class="el-type">
|
||||
{% 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 %}
|
||||
</td>
|
||||
<td>{{ f.amount|format_currency('EUR') }}</td>
|
||||
|
@ -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))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -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 = [];
|
||||
|
||||
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\DocGeneratorBundle\tests\Serializer\Normalizer;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class CollectionDocGenNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->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);
|
||||
}
|
||||
}
|
@ -44,3 +44,9 @@ async function download_and_open(event: Event): Promise<void> {
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="sass">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,51 +1,96 @@
|
||||
<template>
|
||||
<a :class="props.classes" @click="download_and_open($event)">
|
||||
<i class="fa fa-download"></i>
|
||||
Télécharger
|
||||
</a>
|
||||
<a v-if="!state.is_ready" :class="props.classes" @click="download_and_open($event)">
|
||||
<i class="fa fa-download"></i>
|
||||
Télécharger
|
||||
</a>
|
||||
<a v-else :class="props.classes" target="_blank" :type="props.storedObject.type" :download="buildDocumentName()" :href="state.href_url" ref="open_button">
|
||||
<i class="fa fa-external-link"></i>
|
||||
Ouvrir
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {reactive} from "vue";
|
||||
import {reactive, ref, nextTick, onMounted} from "vue";
|
||||
import {build_download_info_link, download_and_decrypt_doc} from "./helpers";
|
||||
import mime from "mime";
|
||||
import {StoredObject} from "../../types";
|
||||
|
||||
interface DownloadButtonConfig {
|
||||
storedObject: StoredObject,
|
||||
classes: {[k: string]: boolean},
|
||||
filename?: string,
|
||||
storedObject: StoredObject,
|
||||
classes: { [k: string]: boolean },
|
||||
filename?: string,
|
||||
}
|
||||
|
||||
interface DownloadButtonState {
|
||||
content: null|string
|
||||
is_ready: boolean,
|
||||
is_running: boolean,
|
||||
href_url: string,
|
||||
}
|
||||
|
||||
const props = defineProps<DownloadButtonConfig>();
|
||||
const state: DownloadButtonState = reactive({content: null});
|
||||
const state: DownloadButtonState = reactive({is_ready: false, is_running: false, href_url: "#"});
|
||||
|
||||
const open_button = ref<HTMLAnchorElement | null>(null);
|
||||
|
||||
function buildDocumentName(): string {
|
||||
const document_name = props.filename || 'document';
|
||||
const ext = mime.getExtension(props.storedObject.type);
|
||||
|
||||
if (null !== ext) {
|
||||
return document_name + '.' + ext;
|
||||
}
|
||||
|
||||
return document_name;
|
||||
}
|
||||
|
||||
async function download_and_open(event: Event): Promise<void> {
|
||||
const button = event.target as HTMLAnchorElement;
|
||||
const button = event.target as HTMLAnchorElement;
|
||||
|
||||
if (null === state.content) {
|
||||
event.preventDefault();
|
||||
if (state.is_running) {
|
||||
console.log('state is running, aborting');
|
||||
return;
|
||||
}
|
||||
|
||||
state.is_running = true;
|
||||
|
||||
if (state.is_ready) {
|
||||
console.log('state is ready. This should not happens');
|
||||
return;
|
||||
}
|
||||
|
||||
const urlInfo = build_download_info_link(props.storedObject.filename);
|
||||
let raw;
|
||||
|
||||
const raw = await download_and_decrypt_doc(urlInfo, props.storedObject.keyInfos, new Uint8Array(props.storedObject.iv));
|
||||
state.content = window.URL.createObjectURL(raw);
|
||||
|
||||
button.href = window.URL.createObjectURL(raw);
|
||||
button.type = props.storedObject.type;
|
||||
|
||||
button.download = props.filename || 'document';
|
||||
|
||||
const ext = mime.getExtension(props.storedObject.type);
|
||||
if (null !== ext) {
|
||||
button.download = button.download + '.' + ext;
|
||||
try {
|
||||
raw = await download_and_decrypt_doc(urlInfo, props.storedObject.keyInfos, new Uint8Array(props.storedObject.iv));
|
||||
} catch (e) {
|
||||
console.error("error while downloading and decrypting document");
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
button.click();
|
||||
console.log('document downloading (and decrypting) successfully');
|
||||
|
||||
console.log('creating the url')
|
||||
state.href_url = window.URL.createObjectURL(raw);
|
||||
console.log('url created', state.href_url);
|
||||
state.is_running = false;
|
||||
state.is_ready = true;
|
||||
console.log('new button marked as ready');
|
||||
console.log('will click on button');
|
||||
|
||||
console.log('openbutton is now', open_button.value);
|
||||
|
||||
await nextTick();
|
||||
console.log('next tick actions');
|
||||
console.log('openbutton after next tick', open_button.value);
|
||||
open_button.value?.click();
|
||||
console.log('open button should have been clicked');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="sass">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
@ -40,5 +40,8 @@ async function beforeLeave(event: Event): Promise<true> {
|
||||
</script>
|
||||
|
||||
<style scoped lang="sass">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{%- import "@ChillDocStore/Macro/macro.html.twig" as m -%}
|
||||
<div
|
||||
<div class="d-inline-flex"
|
||||
data-download-buttons
|
||||
data-stored-object="{{ document_json|json_encode|escape('html_attr') }}"
|
||||
data-can-edit="{{ can_edit ? '1' : '0' }}"
|
||||
|
@ -18,6 +18,7 @@ No document found: Aucun document trouvé
|
||||
The document is successfully registered: Le document est enregistré
|
||||
The document is successfully updated: Le document est mis à jour
|
||||
Any description: Aucune description
|
||||
See the document: Voir le document
|
||||
|
||||
document:
|
||||
Any title: Aucun titre
|
||||
|
@ -2,11 +2,15 @@
|
||||
|
||||
<p>{% transchoice total with { '%pattern%' : pattern } %}%total% events match the search %pattern%{% endtranschoice %}</p>
|
||||
|
||||
<style>
|
||||
table.events td:last-child {
|
||||
width: 15em;
|
||||
}
|
||||
</style>
|
||||
{% if events|length > 0 %}
|
||||
|
||||
<p>{{ 'Results %start%-%end% of %total%'|trans({ '%start%' : start, '%end%': start + events|length, '%total%' : total } ) }}</p>
|
||||
|
||||
<table class="table events">
|
||||
<table class="table table-bordered border-dark align-middle events">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="chill-red">{{ 'Name'|trans }}</th>
|
||||
@ -25,7 +29,7 @@
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
{# {% if is_granted('CHILL_EVENT_SEE_DETAILS', event) %} #}
|
||||
<a href="{{ path('chill_event__event_show', { 'event_id' : event.id } ) }}" class="btn btn-dark">
|
||||
<a href="{{ path('chill_event__event_show', { 'event_id' : event.id } ) }}" class="btn btn-view">
|
||||
{{ 'See'|trans }}
|
||||
</a>
|
||||
{# {% endif %} #}
|
||||
|
@ -24,7 +24,7 @@
|
||||
{% block content %}
|
||||
<h2>{{ 'Events participation' |trans }}</h2>
|
||||
|
||||
<table class="table table-striped table-bordered mt-3 events">
|
||||
<table class="table table-striped table-bordered border-dark align-middle mt-3 events">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="chill-green">{{ 'Date'|trans }}</th>
|
||||
|
@ -18,10 +18,10 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ form_widget(form.submit, { 'attr' : { 'class' : 'btn btn-chill-green' } }) }}
|
||||
{{ form_widget(form.submit, { 'attr' : { 'class' : 'btn btn-submit' } }) }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -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 -%}
|
||||
<div class="col-10">
|
||||
<h1>{{ 'Details of an event'|trans }}</h1>
|
||||
|
||||
<table class="table record_properties">
|
||||
<table class="table table-bordered border-dark align-middle">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{{ 'Name'|trans }}</th>
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<ul class="record_actions">
|
||||
|
||||
{% set returnPath = app.request.get('return_path') %}
|
||||
@ -64,13 +64,13 @@
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>{{ 'Participations'|trans }}</h2>
|
||||
{% set count = event.participations|length %}
|
||||
<p>{% transchoice count %}%count% participations to this event{% endtranschoice %}</p>
|
||||
|
||||
|
||||
{% if count > 0 %}
|
||||
<table class="table">
|
||||
<table class="table table-bordered border-dark align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'Person'|trans }}</th>
|
||||
@ -108,10 +108,10 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
<ul class="record_actions">
|
||||
{% if count > 0 %}
|
||||
<li><a href="{{ path('chill_event_participation_edit_multiple', { 'event_id' : event.id } ) }}" class="btn btn-edit">{{ 'Edit all the participations'|trans }}</a></li>
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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]
|
||||
|
@ -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' }
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -36,5 +36,4 @@ class SynchronizeEntityInfoViewsCommand extends Command
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
144
src/Bundle/ChillMainBundle/Controller/UserExportController.php
Normal file
144
src/Bundle/ChillMainBundle/Controller/UserExportController.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||
use League\Csv\Writer;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final readonly class UserExportController
|
||||
{
|
||||
public function __construct(
|
||||
private UserRepositoryInterface $userRepository,
|
||||
private Security $security,
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \League\Csv\CannotInsertRecord
|
||||
* @throws \League\Csv\Exception
|
||||
* @throws \League\Csv\UnavailableStream
|
||||
*
|
||||
* @Route("/{_locale}/admin/main/users/export/list.{_format}", requirements={"_format": "csv"}, name="chill_main_users_export_list")
|
||||
*/
|
||||
public function userList(Request $request, string $_format = 'csv'): StreamedResponse
|
||||
{
|
||||
if (!$this->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',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -48,12 +48,19 @@ class Center implements HasCenterInterface
|
||||
*/
|
||||
private string $name = '';
|
||||
|
||||
/**
|
||||
* @var Collection<Regroupment>
|
||||
* @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<Regroupment>
|
||||
*/
|
||||
public function getRegroupments(): Collection
|
||||
{
|
||||
return $this->regroupments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
|
@ -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<Center>
|
||||
*/
|
||||
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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 ?',
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -34,9 +34,13 @@ class NotificationPresence
|
||||
$this->notificationRepository = $notificationRepository;
|
||||
}
|
||||
|
||||
public function countNotificationsForClassAndEntity(string $relatedEntityClass, int $relatedEntityId): array
|
||||
/**
|
||||
* @param list<array{relatedEntityClass: class-string, relatedEntityId: int}> $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<array{relatedEntityClass: class-string, relatedEntityId: int}> $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
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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<array{relatedEntityClass: class-string, relatedEntityId: int}> $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<array{relatedEntityClass: class-string, relatedEntityId: int}> $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 '';
|
||||
|
@ -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<array{relatedEntityClass: class-string, relatedEntityId: int}> $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<array{relatedEntityClass: class-string, relatedEntityId: int}> $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<array{relatedEntityClass: class-string, relatedEntityId: int}> $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;
|
||||
|
@ -38,6 +38,19 @@ final class PermissionsGroupRepository implements ObjectRepository
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<PermissionsGroup>
|
||||
*/
|
||||
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
|
||||
|
@ -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<Regroupment>
|
||||
*/
|
||||
public function findRegroupmentAssociatedToNoCenter(): array
|
||||
{
|
||||
return $this->repository->createQueryBuilder('r')
|
||||
->where('SIZE(r.centers) = 0')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
{
|
||||
return Regroupment::class;
|
||||
|
@ -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
|
||||
|
@ -14,6 +14,9 @@ namespace Chill\MainBundle\Repository;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
/**
|
||||
* @template ObjectRepository<User>
|
||||
*/
|
||||
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<array{id: int, username: string, email: string, enabled: bool, civility_id: int, civility_abbreviation: string, civility_name: string, label: string, mainCenter_id: int, mainCenter_name: string, mainScope_id: int, mainScope_name: string, userJob_id: int, userJob_name: string, currentLocation_id: int, currentLocation_name: string, mainLocation_id: int, mainLocation_name: string, absenceStart: \DateTimeImmutable}>
|
||||
*/
|
||||
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<array{id: int, username: string, email: string, enabled: bool, center_id: int, center_name: string, permissionsGroup_id: int, permissionsGroup_name: string}>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
@ -39,8 +39,13 @@
|
||||
{{ 'This will eventually restrict your possibilities in filtering the data.'|trans }}</p>
|
||||
|
||||
<h3 class="m-3">{{ 'Center'|trans }}</h3>
|
||||
|
||||
{{ form_widget(form.centers.center) }}
|
||||
|
||||
<div class="mb-3 mt-3">
|
||||
<input id="toggle-check-all" class="btn btn-misc" type= "button" onclick='uncheckAll(this)' value="{{ 'uncheck all centers'|trans|e('html_attr') }}"/>
|
||||
</div>
|
||||
|
||||
{% if form.centers.regroupment is defined %}
|
||||
<h3 class="m-3">{{ 'Pick aggregated centers'|trans }}</h3>
|
||||
{{ form_widget(form.centers.regroupment) }}
|
||||
@ -53,3 +58,15 @@
|
||||
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
{% block js %}
|
||||
|
||||
<script>
|
||||
const uncheckAll = () => {
|
||||
const allCenters = document.getElementsByName('centers[center][]');
|
||||
|
||||
allCenters.forEach(checkbox => checkbox.checked = false)
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock js %}
|
||||
|
@ -9,4 +9,4 @@
|
||||
{{ 'notification.counter unread notifications'|trans({'unread': counter.unread }) }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -98,6 +98,18 @@
|
||||
<li class='cancel'>
|
||||
<a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans}}</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa fa-download"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{{ path('chill_main_users_export_list') }}">{{ 'admin.users.export_list_csv'|trans }}</a></li>
|
||||
<li><a class="dropdown-item" href="{{ path('chill_main_users_export_permissions') }}">{{ 'admin.users.export_permissions_csv'|trans }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_admin_user_new') }}" class="btn btn-create">{{ 'Create'|trans }}</a>
|
||||
</li>
|
||||
|
@ -37,3 +37,6 @@ services:
|
||||
Chill\MainBundle\Controller\RegroupmentController:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Controller\UserExportController:
|
||||
tags: ['controller.service_arguments']
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -34,5 +34,4 @@ class AccompanyingPeriodStepChangeMessageHandler implements MessageHandlerInterf
|
||||
|
||||
($this->changer)($period, $message->getTransition());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -84,5 +84,4 @@ class AccompanyingPeriodStepChangeRequestor
|
||||
$this->messageBus->dispatch(new AccompanyingPeriodStepChangeRequestMessage($accompanyingPeriodId, 'mark_active'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 <here> in "UPDATE table SET <here> "
|
||||
$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,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()),
|
||||
];
|
||||
|
@ -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);
|
||||
|
@ -59,6 +59,7 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
||||
* )
|
||||
* @Serializer\Groups({"read", "docgen:read"})
|
||||
* @ORM\OrderBy({"startDate": "DESC", "id": "DESC"})
|
||||
* @var Collection<AccompanyingPeriodWorkEvaluation>
|
||||
*
|
||||
* @internal /!\ the serialization for write evaluations is handled in `AccompanyingPeriodWorkDenormalizer`
|
||||
*/
|
||||
@ -278,6 +279,9 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
||||
return $this->accompanyingPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<AccompanyingPeriodWorkEvaluation>
|
||||
*/
|
||||
public function getAccompanyingPeriodWorkEvaluations(): Collection
|
||||
{
|
||||
return $this->accompanyingPeriodWorkEvaluations;
|
||||
|
@ -79,6 +79,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
|
||||
* )
|
||||
* @ORM\OrderBy({"createdAt": "DESC", "id": "DESC"})
|
||||
* @Serializer\Groups({"read"})
|
||||
* @var Collection<AccompanyingPeriodWorkEvaluationDocument>
|
||||
*/
|
||||
private Collection $documents;
|
||||
|
||||
@ -204,7 +205,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
* @return Collection<AccompanyingPeriodWorkEvaluationDocument>
|
||||
*/
|
||||
public function getDocuments()
|
||||
{
|
||||
|
@ -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');
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
|
||||
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function in_array;
|
||||
|
||||
class StepFilterBetweenDates implements FilterInterface
|
||||
{
|
||||
private const DEFAULT_CHOICE = [
|
||||
AccompanyingPeriod::STEP_CONFIRMED,
|
||||
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT,
|
||||
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG,
|
||||
];
|
||||
|
||||
private const STEPS = [
|
||||
'course.draft' => 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';
|
||||
}
|
||||
}
|
@ -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';
|
||||
}
|
||||
}
|
@ -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',
|
||||
|
@ -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'])
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Notification;
|
||||
|
||||
use Chill\MainBundle\Entity\Notification;
|
||||
use Chill\MainBundle\Notification\NotificationHandlerInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository;
|
||||
|
||||
final class AccompanyingPeriodWorkEvaluationDocumentNotificationHandler implements NotificationHandlerInterface
|
||||
{
|
||||
private AccompanyingPeriodWorkEvaluationDocumentRepository $accompanyingPeriodWorkEvaluationDocumentRepository;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkEvaluationDocumentRepository $accompanyingPeriodWorkEvaluationDocumentRepository)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Notification;
|
||||
|
||||
use Chill\MainBundle\Entity\Notification;
|
||||
use Chill\MainBundle\Notification\NotificationHandlerInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||
|
||||
final class AccompanyingPeriodWorkNotificationHandler implements NotificationHandlerInterface
|
||||
{
|
||||
private AccompanyingPeriodWorkRepository $accompanyingPeriodWorkRepository;
|
||||
|
||||
public function __construct(AccompanyingPeriodWorkRepository $accompanyingPeriodWorkRepository)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
@ -89,5 +89,4 @@ readonly class AccompanyingPeriodInfoRepository implements AccompanyingPeriodInf
|
||||
{
|
||||
return AccompanyingPeriodInfo::class;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -122,7 +122,8 @@
|
||||
<add-evaluation
|
||||
v-for="e in pickedEvaluations"
|
||||
v-bind:key="e.key"
|
||||
v-bind:evaluation="e">
|
||||
v-bind:evaluation="e"
|
||||
v-bind:docAnchorId="this.docAnchorId">
|
||||
</add-evaluation>
|
||||
|
||||
<!-- box to add new evaluation -->
|
||||
@ -296,7 +297,21 @@
|
||||
@go-to-generate-workflow="goToGenerateWorkflow"
|
||||
></list-workflow-modal>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<button v-if="AmIRefferer"
|
||||
class="btn btn-notify"
|
||||
@click="goToGenerateNotification(false)"
|
||||
></button>
|
||||
<template v-else>
|
||||
<button id="btnGroupNotifyButtons" type="button" class="btn btn-notify dropdown-toggle" :title="$t('notification_send')" data-bs-toggle="dropdown" aria-expanded="false"> </button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupNotifyButtons">
|
||||
<li><a class="dropdown-item" @click="goToGenerateNotification(true)">{{ $t('notification_notify_referrer') }}</a></li>
|
||||
<li><a class="dropdown-item" @click="goToGenerateNotification(false)">{{ $t('notification_notify_any') }}</a></li>
|
||||
</ul>
|
||||
</template>
|
||||
</li>
|
||||
|
||||
<li v-if="!isPosting">
|
||||
<button class="btn btn-save" @click="submit">
|
||||
{{ $t('action.save') }}
|
||||
@ -366,7 +381,10 @@ const i18n = {
|
||||
no_referrers: "Aucun agent traitant",
|
||||
choose_referrers: "Choisir des agents traitants",
|
||||
remove_referrer: "Enlever l'agent",
|
||||
private_comment: "Commentaire privé"
|
||||
private_comment: "Commentaire privé",
|
||||
notification_notify_referrer: "Notifier le référent",
|
||||
notification_notify_any: "Notifier d'autres utilisateurs",
|
||||
notification_send: "Envoyer une notification",
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -389,6 +407,7 @@ export default {
|
||||
i18n,
|
||||
data() {
|
||||
return {
|
||||
docAnchorId: null,
|
||||
isExpanded: false,
|
||||
editor: ClassicEditor,
|
||||
showAddObjective: false,
|
||||
@ -428,7 +447,14 @@ export default {
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
beforeMount() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
this.docAnchorId = urlParams.get('doc_id');
|
||||
},
|
||||
mounted() {
|
||||
this.scrollToElement(this.docAnchorId);
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'work',
|
||||
'resultsForAction',
|
||||
@ -441,6 +467,7 @@ export default {
|
||||
'isPosting',
|
||||
'errors',
|
||||
'templatesAvailablesForAction',
|
||||
'me',
|
||||
]),
|
||||
...mapGetters([
|
||||
'hasResultsForAction',
|
||||
@ -498,6 +525,10 @@ export default {
|
||||
this.$store.commit('setPersonsPickedIds', v);
|
||||
}
|
||||
},
|
||||
AmIRefferer() {
|
||||
return (!(this.work.accompanyingPeriod.user && this.me
|
||||
&& (this.work.accompanyingPeriod.user.id !== this.me.id)));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleSelect() {
|
||||
@ -548,6 +579,19 @@ export default {
|
||||
return this.$store.dispatch('submit', callback)
|
||||
.catch(e => { console.log(e); throw e; });
|
||||
},
|
||||
goToGenerateNotification(tos) {
|
||||
console.log('save before leave to notification');
|
||||
const callback = (data) => {
|
||||
if (tos === true) {
|
||||
window.location.assign(`/fr/notification/create?entityClass=Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork&entityId=${this.work.id}&tos[0]=${this.work.accompanyingPeriod.user.id}&returnPath=/fr/person/accompanying-period/${this.work.accompanyingPeriod.id}/work`);
|
||||
} else {
|
||||
window.location.assign(`/fr/notification/create?entityClass=Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork&entityId=${this.work.id}&returnPath=/fr/person/accompanying-period/${this.work.accompanyingPeriod.id}/work`);
|
||||
}
|
||||
}
|
||||
|
||||
return this.$store.dispatch('submit', callback)
|
||||
.catch(e => {console.log(e); throw e});
|
||||
},
|
||||
submit() {
|
||||
this.$store.dispatch('submit').catch((error) => {
|
||||
if (error.name === 'ValidationException' || error.name === 'AccessException') {
|
||||
@ -559,7 +603,7 @@ export default {
|
||||
});
|
||||
},
|
||||
saveFormOnTheFly(payload) {
|
||||
console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
|
||||
// console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
|
||||
|
||||
let body = { type: payload.type };
|
||||
body.name = payload.data.text;
|
||||
@ -581,6 +625,12 @@ export default {
|
||||
this.$toast.open({message: 'An error occurred'});
|
||||
}
|
||||
})
|
||||
},
|
||||
scrollToElement(docAnchorId) {
|
||||
const documentEl = document.getElementById(`document_${docAnchorId}`);
|
||||
if (documentEl) {
|
||||
documentEl.scrollIntoView({behavior: 'smooth'});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form-evaluation ref="FormEvaluation" :key="evaluation.key" :evaluation="evaluation"></form-evaluation>
|
||||
<form-evaluation ref="FormEvaluation" :key="evaluation.key" :evaluation="evaluation" :docAnchorId="docAnchorId"></form-evaluation>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li v-if="evaluation.workflows_availables.length > 0">
|
||||
@ -85,7 +85,7 @@ export default {
|
||||
Modal,
|
||||
ListWorkflowModal,
|
||||
},
|
||||
props: ['evaluation'],
|
||||
props: ['evaluation', 'docAnchorId'],
|
||||
i18n,
|
||||
data() {
|
||||
return {
|
||||
|
@ -79,8 +79,8 @@
|
||||
<h5>{{ $t('Documents') }} :</h5>
|
||||
|
||||
<div class="flex-table">
|
||||
<div class="item-bloc" v-for="(d, i) in evaluation.documents" :key="d.id">
|
||||
<div class="item-row">
|
||||
<div class="item-bloc" v-for="(d, i) in evaluation.documents" :key="d.id" :class="[parseInt(this.docAnchorId) === d.id ? 'bg-blink' : 'nothing']">
|
||||
<div :id="`document_${d.id}`" class="item-row">
|
||||
<div class="input-group input-group-lg mb-3 row">
|
||||
<label class="col-sm-3 col-form-label">Titre du document:</label>
|
||||
<div class="col-sm-9">
|
||||
@ -92,7 +92,7 @@
|
||||
:data-key="i"
|
||||
@input="onInputDocumentTitle"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col item-meta">
|
||||
@ -102,27 +102,32 @@
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">
|
||||
<ul class="record_actions" >
|
||||
<li v-if="d.workflows_availables.length > 0">
|
||||
<list-workflow-modal
|
||||
:workflows="d.workflows"
|
||||
:allowCreate="true"
|
||||
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument"
|
||||
:relatedEntityId="d.id"
|
||||
:workflowsAvailables="d.workflows_availables"
|
||||
:preventDefaultMoveToGenerate="true"
|
||||
:goToGenerateWorkflowPayload="{doc: d}"
|
||||
@go-to-generate-workflow="goToGenerateWorkflowEvaluationDocument"
|
||||
></list-workflow-modal>
|
||||
</li>
|
||||
<ul class="record_actions">
|
||||
<li v-if="d.workflows_availables.length > 0">
|
||||
<list-workflow-modal
|
||||
:workflows="d.workflows"
|
||||
:allowCreate="true"
|
||||
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument"
|
||||
:relatedEntityId="d.id"
|
||||
:workflowsAvailables="d.workflows_availables"
|
||||
:preventDefaultMoveToGenerate="true"
|
||||
:goToGenerateWorkflowPayload="{doc: d}"
|
||||
@go-to-generate-workflow="goToGenerateWorkflowEvaluationDocument"
|
||||
></list-workflow-modal>
|
||||
</li>
|
||||
<li>
|
||||
<add-async-upload
|
||||
:buttonTitle="$t('replace')"
|
||||
:options="asyncUploadOptions"
|
||||
:btnClasses="{'btn': true, 'btn-edit': true}"
|
||||
@addDocument="(arg) => replaceDocument(d, arg)"
|
||||
>
|
||||
</add-async-upload>
|
||||
<button
|
||||
v-if="AmIRefferer"
|
||||
class="btn btn-notify"
|
||||
@click="goToGenerateDocumentNotification(d, false)">
|
||||
</button>
|
||||
<template v-else>
|
||||
<button id="btnGroupNotifyButtons" type="button" class="btn btn-notify dropdown-toggle" :title="$t('notification_send')" data-bs-toggle="dropdown" aria-expanded="false"> </button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupNotifyButtons">
|
||||
<li><a class="dropdown-item" @click="goToGenerateDocumentNotification(d, true)">{{ $t('notification_notify_referrer') }}</a></li>
|
||||
<li><a class="dropdown-item" @click="goToGenerateDocumentNotification(d, false)">{{ $t('notification_notify_any') }}</a></li>
|
||||
</ul>
|
||||
</template>
|
||||
</li>
|
||||
<li>
|
||||
<document-action-buttons-group
|
||||
@ -133,6 +138,15 @@
|
||||
@on-stored-object-status-change="onStatusDocumentChanged"
|
||||
></document-action-buttons-group>
|
||||
</li>
|
||||
<li>
|
||||
<add-async-upload
|
||||
:buttonTitle="$t('replace')"
|
||||
:options="asyncUploadOptions"
|
||||
:btnClasses="{'btn': true, 'btn-edit': true}"
|
||||
@addDocument="(arg) => replaceDocument(d, arg)"
|
||||
>
|
||||
</add-async-upload>
|
||||
</li>
|
||||
<li v-if="d.workflows.length === 0">
|
||||
<a class="btn btn-delete" @click="removeDocument(d)">
|
||||
</a>
|
||||
@ -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});
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -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;}
|
||||
}
|
||||
</style>
|
||||
|
@ -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)
|
||||
|
@ -207,7 +207,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {dateToISO} from 'ChillMainAssets/chill/js/date';
|
||||
import {dateToISO, ISOToDate} from 'ChillMainAssets/chill/js/date';
|
||||
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
|
||||
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
|
||||
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
|
||||
@ -262,7 +262,7 @@ export default {
|
||||
},
|
||||
birthdate: function () {
|
||||
if (this.person.birthdate !== null || this.person.birthdate === "undefined") {
|
||||
return new Date(this.person.birthdate.datetime);
|
||||
return ISOToDate(this.person.birthdate.datetime);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@ -5,7 +5,8 @@
|
||||
# - displayAction: [true|false] default: false
|
||||
# - displayFontSmall: [true|false] default: false
|
||||
#}
|
||||
<div class="item-bloc{% if displayContent is defined %} {{ displayContent }}{% endif %}{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||
<div
|
||||
class="item-bloc{% if displayContent is defined %} {{ displayContent }}{% endif %}{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||
|
||||
<div class="item-row">
|
||||
<h2 class="badge-title">
|
||||
@ -111,9 +112,11 @@
|
||||
</div>
|
||||
|
||||
{% if displayContent is not defined or displayContent == 'short' %}
|
||||
<div class="item-row column{% if displayFontSmall is defined and displayFontSmall == true %} smallfont{% endif %}">
|
||||
{% include 'ChillPersonBundle:AccompanyingCourseWork:_objectifs_results_evaluations.html.twig' with {
|
||||
'displayContent': displayContent
|
||||
<div
|
||||
class="item-row column{% if displayFontSmall is defined and displayFontSmall == true %} smallfont{% endif %}">
|
||||
{% include '@ChillPerson/AccompanyingCourseWork/_objectifs_results_evaluations.html.twig' with {
|
||||
'displayContent': displayContent,
|
||||
'onlyone': false
|
||||
} %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -121,35 +124,56 @@
|
||||
{% if displayContent is not defined or displayContent == 'short' %}
|
||||
<div class="item-row separator">
|
||||
|
||||
{% import '@ChillPerson/AccompanyingCourseWork/_macros.html.twig' as macro %}
|
||||
{% import '@ChillPerson/AccompanyingCourseWork/_macros.html.twig' as macro %}
|
||||
|
||||
<div class="item-col item-meta">
|
||||
{{ macro.metadata(w) }}
|
||||
</div>
|
||||
|
||||
{% if displayAction is defined and displayAction == true %}
|
||||
<ul class="item-col record_actions">
|
||||
<li>{{ macro.workflowButton(w) }}</li>
|
||||
<li>
|
||||
<a class="btn btn-show" title="{{ 'Show'|trans }}"
|
||||
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_show', {'id': w.id }) }}"
|
||||
></a>
|
||||
</li>
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', w) %}
|
||||
<ul class="item-col record_actions">
|
||||
<li>{{ macro.workflowButton(w) }}</li>
|
||||
{% if displayNotification is defined and displayNotification == true %}
|
||||
<li>
|
||||
<div class="d-grid gap-2 {% if accompanyingCourse.hasUser and accompanyingCourse.user is not same as(app.user) %}btn-group{% endif %}" {% if accompanyingCourse.hasUser and accompanyingCourse.user is not same as(app.user) %}role="group"{% endif %}>
|
||||
{% if accompanyingCourse.hasUser and accompanyingCourse.user is not same as(app.user) %}
|
||||
<button id="btnGroupNotifyButtons" type="button" class="btn btn-notify dropdown-toggle" title="{{ 'notification.Notify'|trans }}" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupNotifyButtons">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', 'entityId': w.id, 'tos': [accompanyingCourse.user.id]}) }}">{{ 'notification.Notify referrer'|trans }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', 'entityId': w.id}) }}">{{ 'notification.Notify any'|trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<a class="btn btn-notify" title="{{ 'notification.Notify'|trans }}" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', 'entityId': w.id}) }}">
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<a class="btn btn-edit" title="{{ 'Edit'|trans }}"
|
||||
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
|
||||
<a class="btn btn-show" title="{{ 'Show'|trans }}"
|
||||
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_show', {'id': w.id }) }}"
|
||||
></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE', w) %}
|
||||
<li>
|
||||
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
|
||||
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
|
||||
></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', w) %}
|
||||
<li>
|
||||
<a class="btn btn-edit" title="{{ 'Edit'|trans }}"
|
||||
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
|
||||
></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE', w) %}
|
||||
<li>
|
||||
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
|
||||
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
|
||||
></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -157,47 +181,51 @@
|
||||
</div>
|
||||
|
||||
{#
|
||||
# This is for 'long' version of content
|
||||
# Note: this include is wrapped in a flex-table container.
|
||||
# We start by closing the flex-table so we can add more.
|
||||
# At the end we leave the last flex-table open, as it will be closed in the container.
|
||||
# This is for 'long' version of content
|
||||
# Note: this include is wrapped in a flex-table container.
|
||||
# We start by closing the flex-table so we can add more.
|
||||
# At the end we leave the last flex-table open, as it will be closed in the container.
|
||||
#}
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
</div>
|
||||
|
||||
{% if w.results|length > 0 or w.goals|length > 0 or w.accompanyingPeriodWorkEvaluations|length > 0 %}
|
||||
<h2 class="chill-blue">{{ 'Dispositifs' }}</h2>
|
||||
|
||||
<div class="flex-table">{# new flex-table wrapper #}
|
||||
<div
|
||||
class="item-bloc colored{% if displayContent is defined %} {{ displayContent }}{% endif %}{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||
{% include 'ChillPersonBundle:AccompanyingCourseWork:_objectifs_results_evaluations.html.twig' with {
|
||||
'displayContent': displayContent,
|
||||
'onlyone' : false
|
||||
} %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if w.results|length > 0 or w.goals|length > 0 or w.accompanyingPeriodWorkEvaluations|length > 0 %}
|
||||
<h2 class="chill-blue">{{ 'Dispositifs' }}</h2>
|
||||
<h2 class="chill-blue">{{ 'Comments'|trans }}</h2>
|
||||
|
||||
<div class="flex-table">{# new flex-table wrapper #}
|
||||
<div class="item-bloc colored{% if displayContent is defined %} {{ displayContent }}{% endif %}{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||
{% include 'ChillPersonBundle:AccompanyingCourseWork:_objectifs_results_evaluations.html.twig' with {
|
||||
'displayContent': displayContent
|
||||
} %}
|
||||
</div>
|
||||
<div class="flex-table">
|
||||
<div
|
||||
class="item-bloc no-altern{% if displayContent is defined %} {{ displayContent }}{% endif %}{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||
<h3 class="chill-beige">Public</h3>
|
||||
{% if w.note is not empty %}
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ w.note|chill_entity_render_box({'metadata': true }) }}
|
||||
</blockquote>
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'No comment associated'|trans }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if w.privateComment.hasCommentForUser(app.user) %}
|
||||
<div
|
||||
class="item-bloc no-altern{% if displayContent is defined %} {{ displayContent }}{% endif %}{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||
<h3 class="chill-beige">Privé</h3>
|
||||
<blockquote class="chill-user-quote private-quote">
|
||||
{{ w.privateComment.commentForUser(app.user)|chill_markdown_to_html }}
|
||||
</blockquote>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h2 class="chill-blue">{{ 'Comments'|trans }}</h2>
|
||||
|
||||
<div class="flex-table">
|
||||
<div class="item-bloc no-altern{% if displayContent is defined %} {{ displayContent }}{% endif %}{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||
<h3 class="chill-beige">Public</h3>
|
||||
{% if w.note is not empty %}
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ w.note|chill_entity_render_box({'metadata': true }) }}
|
||||
</blockquote>
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'No comment associated'|trans }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if w.privateComment.hasCommentForUser(app.user) %}
|
||||
<div class="item-bloc no-altern{% if displayContent is defined %} {{ displayContent }}{% endif %}{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||
<h3 class="chill-beige">Privé</h3>
|
||||
<blockquote class="chill-user-quote private-quote">
|
||||
{{ w.privateComment.commentForUser(app.user)|chill_markdown_to_html }}
|
||||
</blockquote>
|
||||
</div>
|
||||
{% endif %}
|
||||
{# Here flex-table stay open ! read above #}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
@ -1,7 +1,17 @@
|
||||
{% macro metadata(w) %}
|
||||
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', w.id) %}
|
||||
{% if notif_counter.total > 0 %}
|
||||
{{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', w.id) }}
|
||||
{% macro metadata(w, include_notif_counter = true) %}
|
||||
{% if include_notif_counter == true %}
|
||||
{% set more = [] %}
|
||||
{% for e in w.accompanyingPeriodWorkEvaluations %}
|
||||
{% for d in e.documents %}
|
||||
{% set more = more|merge([{'relatedEntityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument', 'relatedEntityId': d.id}]) %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', w.id, more) %}
|
||||
|
||||
{% if notif_counter.total > 0 %}
|
||||
{{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', w.id, more) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% import '@ChillPerson/Macro/updatedBy.html.twig' as macro %}
|
||||
{{ macro.updatedBy(w) }}
|
||||
@ -20,4 +30,4 @@
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{{ chill_entity_workflow_list('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', w.id, [], suppEvaluations) }}
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
@ -3,6 +3,7 @@
|
||||
# - displayContent: [short|long] default: short
|
||||
#}
|
||||
{% if w.results|length > 0 %}
|
||||
|
||||
<table class="obj-res-eval">
|
||||
<thead>
|
||||
<th class="obj"><h4 class="title_label">{{ 'accompanying_course_work.goal'|trans }}</h4></th>
|
||||
@ -64,9 +65,11 @@
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for e in w.accompanyingPeriodWorkEvaluations %}
|
||||
<tr>
|
||||
<td class="eval">
|
||||
{% if onlyone|default(false) %}
|
||||
{% for e in w.accompanyingPeriodWorkEvaluations %}
|
||||
{% if evalId is defined and evalId == e.id %}
|
||||
<tr>
|
||||
<td class="eval">
|
||||
<ul class="eval_title">
|
||||
<li>
|
||||
{{ e.evaluation.title|localize_translatable_string }}
|
||||
@ -78,13 +81,15 @@
|
||||
|
||||
{% if e.endDate %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ e.endDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% else %}
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -92,13 +97,15 @@
|
||||
|
||||
{% if e.maxDate %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ e.maxDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% else %}
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -107,13 +114,15 @@
|
||||
{% if e.warningInterval and e.warningInterval.d > 0 %}
|
||||
<li>
|
||||
{% set days = (e.warningInterval.d + e.warningInterval.m * 30) %}
|
||||
<span class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
|
||||
{{ 'accompanying_course_work.%days% days before max_date'|trans({'%days%': days }) }}
|
||||
</li>
|
||||
{% else %}
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
@ -122,45 +131,166 @@
|
||||
{% if e.timeSpent is not null and e.timeSpent > 0 %}
|
||||
<li>
|
||||
{% set minutes = (e.timeSpent / 60) %}
|
||||
<span class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span> {{ 'duration.minute'|trans({ '{m}' : minutes }) }}
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span> {{ 'duration.minute'|trans({ '{m}' : minutes }) }}
|
||||
</li>
|
||||
{% elseif displayContent is defined and displayContent == 'long' %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
{% endif %}
|
||||
|
||||
{% if e.comment is not empty %}
|
||||
<blockquote class="chill-user-quote">{{ e.comment|chill_entity_render_box }}</blockquote>
|
||||
|
||||
{% endfor %}
|
||||
{% if recordAction is defined %}
|
||||
{{ recordAction }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% for e in w.accompanyingPeriodWorkEvaluations %}
|
||||
<tr>
|
||||
<td class="eval">
|
||||
<ul class="eval_title">
|
||||
<li>
|
||||
{{ e.evaluation.title|localize_translatable_string }}
|
||||
<ul class="columns">
|
||||
<li>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.start_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ e.startDate|format_date('short') }}</b>
|
||||
</li>
|
||||
|
||||
{% if e.endDate %}
|
||||
<li>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ e.endDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% else %}
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
<li>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if e.maxDate %}
|
||||
<li>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ e.maxDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% else %}
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
<li>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if e.warningInterval and e.warningInterval.d > 0 %}
|
||||
<li>
|
||||
{% set days = (e.warningInterval.d + e.warningInterval.m * 30) %}
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
|
||||
{{ 'accompanying_course_work.%days% days before max_date'|trans({'%days%': days }) }}
|
||||
</li>
|
||||
{% else %}
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
<li>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if e.timeSpent is not null and e.timeSpent > 0 %}
|
||||
<li>
|
||||
{% set minutes = (e.timeSpent / 60) %}
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span> {{ 'duration.minute'|trans({ '{m}' : minutes }) }}
|
||||
</li>
|
||||
{% elseif displayContent is defined and displayContent == 'long' %}
|
||||
<li>
|
||||
<span
|
||||
class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'Not given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% if recordAction is defined %}
|
||||
{{ recordAction }}
|
||||
{% endif %}
|
||||
|
||||
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||
{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %}
|
||||
{% if displayContent is defined and displayContent == 'long' %}
|
||||
|
||||
{% if e.comment is not empty %}
|
||||
<blockquote
|
||||
class="chill-user-quote">{{ e.comment|chill_entity_render_box }}</blockquote>
|
||||
{% endif %}
|
||||
|
||||
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||
{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %}
|
||||
{% if e.documents|length > 0 %}
|
||||
<table class="table table-hover align-middle mt-4 mx-auto">
|
||||
{% for d in e.documents %}
|
||||
<tr>
|
||||
<td class="border-0">{{ d.title }}</td>
|
||||
<td class="border-0">{{ mm.mimeIcon(d.storedObject.type) }}</td>
|
||||
<td class="border-0 text-end">
|
||||
{% if accompanyingCourse.hasUser and accompanyingCourse.user is not same as(app.user) %}
|
||||
<button id="btnGroupNotifyButtons" type="button" class="btn btn-sm btn-notify dropdown-toggle" title="{{ 'notification.Notify'|trans }}"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupNotifyButtons">
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ chill_path_add_return_path('chill_main_notification_create', {
|
||||
'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument',
|
||||
'entityId': d.id, 'tos': [accompanyingCourse.user.id]}) }}">{{ 'notification.Notify referrer'|trans }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
href="{{ chill_path_add_return_path('chill_main_notification_create', {
|
||||
'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument',
|
||||
'entityId': d.id}) }}">{{ 'notification.Notify any'|trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<a class="btn btn-notify btn-sm"
|
||||
title="{{ 'notification.Notify'|trans }}"
|
||||
href="{{ chill_path_add_return_path('chill_main_notification_create', {
|
||||
'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument',
|
||||
'entityId': d.id }) }}"></a>
|
||||
{% endif %}
|
||||
|
||||
{{ d.storedObject|chill_document_button_group(d.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', w), {'small': true}) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'No document found'|trans }}</span>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if e.documents|length > 0 %}
|
||||
<table class="table mt-4 mx-auto">
|
||||
{% for d in e.documents %}
|
||||
<tr class="border-0">
|
||||
<td class="border-0">{{ d.title }}</td>
|
||||
<td class="border-0">{{ mm.mimeIcon(d.storedObject.type) }}</td>
|
||||
<td class="border-0 text-end">{{ d.storedObject|chill_document_button_group(d.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', w), {'small': true}) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'No document found'|trans }}</span>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
@ -26,7 +26,8 @@
|
||||
'displayAction': true,
|
||||
'displayContent': 'short',
|
||||
'displayFontSmall': true,
|
||||
'itemBlocClass': ''
|
||||
'itemBlocClass': '',
|
||||
'displayNotification': true
|
||||
} %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
@ -27,7 +27,20 @@
|
||||
'displayContent': 'long',
|
||||
'itemBlocClass': 'uniq extended',
|
||||
} %}
|
||||
<div class="p-3 mt-3">{{ macro.metadata(work) }}</div>
|
||||
<div class="p-3 mt-3">{{ macro.metadata(work, false) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="notification notification-list">
|
||||
{% set more = [] %}
|
||||
{% for e in work.accompanyingPeriodWorkEvaluations %}
|
||||
{% for d in e.documents %}
|
||||
{% set more = more|merge([{'relatedEntityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument', 'relatedEntityId': d.id}]) %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% set notifications = chill_list_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', work.id, more) %}
|
||||
{% if notifications is not empty %}
|
||||
{{ notifications|raw }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
@ -36,6 +49,25 @@
|
||||
class="btn btn-cancel">{{ 'Back to the list'|trans }}</a>
|
||||
</li>
|
||||
<li>{{ macro.workflowButton(work) }}</li>
|
||||
<li>
|
||||
<div class="d-grid gap-2 {% if accompanyingCourse.hasUser and accompanyingCourse.user is not same as(app.user) %}btn-group{% endif %}" {% if accompanyingCourse.hasUser and accompanyingCourse.user is not same as(app.user) %}role="group"{% endif %}>
|
||||
{% if accompanyingCourse.hasUser and accompanyingCourse.user is not same as(app.user) %}
|
||||
<button id="btnGroupNotifyButtons" type="button" class="btn btn-notify dropdown-toggle" title="{{ 'notification.Notify'|trans }}" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupNotifyButtons">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', 'entityId': work.id, 'tos': [accompanyingCourse.user.id]}) }}">{{ 'notification.Notify referrer'|trans }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', 'entityId': work.id}) }}">{{ 'notification.Notify any'|trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<a class="btn btn-notify" title="{{ 'notification.Notify'|trans }}" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork', 'entityId': work.id}) }}">
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', work) %}
|
||||
<li>
|
||||
<a class="btn btn-edit"
|
||||
@ -47,7 +79,8 @@
|
||||
<li>
|
||||
<a class="btn btn-delete"
|
||||
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': work.id } ) }}"
|
||||
>{{ 'Delete'|trans }}</a>
|
||||
title = "{{ 'Delete'|trans }}"
|
||||
></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
@ -0,0 +1,134 @@
|
||||
{% if document is not null %}
|
||||
{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %}
|
||||
<div class="flex-table accompanying-course-work">
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_EVALUATION_DOCUMENT_SHOW', document) %}
|
||||
{% set doc = document %}
|
||||
<div class="item-bloc evaluation-item bg-chill-llight-gray">
|
||||
<div class="item-row mb-2">
|
||||
<h1>{{ "Document"|trans }}: {{ doc.title }}</h1>
|
||||
</div>
|
||||
<div class="item-row mb-2">
|
||||
<h2 class="badge-title">
|
||||
<span class="title_label"></span>
|
||||
<span class="title_action">
|
||||
{{ evaluation.accompanyingPeriodWork.socialAction|chill_entity_render_string }}
|
||||
<ul class="small_in_title columns mt-1">
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.start_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ evaluation.accompanyingPeriodWork.startDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% if evaluation.accompanyingPeriodWork.endDate %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ evaluation.accompanyingPeriodWork.endDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="item-row mb-2">
|
||||
<div class="item-col" style="width: 17%;">
|
||||
<h4 class="title_label">
|
||||
{{ 'Participants'|trans }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="item-col list">
|
||||
{% for p in evaluation.accompanyingPeriodWork.persons %}
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
targetEntity: { name: 'person', id: p.id },
|
||||
action: 'show',
|
||||
displayBadge: true,
|
||||
buttonText: p|chill_entity_render_string,
|
||||
isDead: p.deathdate is not null
|
||||
} %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row column">
|
||||
<table class="obj-res-eval my-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="eval">
|
||||
<h4 class="title_label">
|
||||
{{ 'Évaluation'|trans }}
|
||||
</h4>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="eval">
|
||||
<ul class="eval_title">
|
||||
<li class="my-2">
|
||||
{{ evaluation.evaluation.title|localize_translatable_string }}
|
||||
<ul class="columns pt-2">
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.start_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ evaluation.startDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% if evaluation.endDate %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ evaluation.endDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if evaluation.maxDate %}
|
||||
<li>
|
||||
<span class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
|
||||
<b>{{ evaluation.maxDate|format_date('short') }}</b>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if evaluation.warningInterval and evaluation.warningInterval.d > 0 %}
|
||||
<li>
|
||||
{% set days = (evaluation.warningInterval.d + evaluation.warningInterval.m * 30) %}
|
||||
<span class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
|
||||
{{ 'accompanying_course_work.%days% days before max_date'|trans({'%days%': days }) }}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{% if evaluation.createdBy is not null %}
|
||||
<span class="item-key">créé par</span>
|
||||
<b>{{ evaluation.createdBy.username }}</b>
|
||||
{% endif %}
|
||||
{% if evaluation.createdAt is not null %}
|
||||
<span class="item-key">{{ 'le'|trans }}</span>
|
||||
<b>{{ evaluation.createdAt|format_date('short') }}</b>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
{% if evaluation.comment %}
|
||||
<blockquote class="chill-user-quote" style="margin-left: 0;">
|
||||
{{ evaluation.comment }}
|
||||
</blockquote>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="btn btn-show"
|
||||
href="{{ path('chill_person_accompanying_period_work_show', { 'id': document.accompanyingPeriodWorkEvaluation.accompanyingPeriodWork.id }) }}"
|
||||
title="{{ 'See the document'|trans }}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="alert alert-warning border-warning border-1">
|
||||
{{ 'This is the minimal period details'|trans ~ ': ' ~ document.id }}<br>
|
||||
{{ 'You are getting a notification for a period you are not allowed to see'|trans }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning border-warning border-1">
|
||||
{{ 'You are getting a notification for a period which does not exists any more'|trans }}
|
||||
</div>
|
||||
{% endif %}
|
@ -0,0 +1,30 @@
|
||||
{% macro recordAction(work) %}
|
||||
<li>
|
||||
<a class="btn btn-show" title="{{ 'Show'|trans }}"
|
||||
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_show', {'id': work.id }) }}"
|
||||
></a>
|
||||
</li>
|
||||
{% endmacro %}
|
||||
{% if work is not null %}
|
||||
<div class="flex-table accompanying-course-work">
|
||||
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_SEE', work) %}
|
||||
{% include "@ChillPerson/AccompanyingCourseWork/_item.html.twig" with {
|
||||
'itemBlocClass': 'bg-chill-llight-gray',
|
||||
'displayAction': true,
|
||||
'displayContent': 'short',
|
||||
'displayFontSmall': true,
|
||||
'displayNotification:':true,
|
||||
'w': work
|
||||
} %}
|
||||
{% else %}
|
||||
<div class="alert alert-warning border-warning border-1">
|
||||
{{ 'This is the minimal period details'|trans ~ ': ' ~ work.id }}<br>
|
||||
{{ 'You are getting a notification for a period you are not allowed to see'|trans }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning border-warning border-1">
|
||||
{{ 'You are getting a notification for a period which does not exists any more'|trans }}
|
||||
</div>
|
||||
{% endif %}
|
@ -16,11 +16,13 @@
|
||||
</div>
|
||||
<div class="wh-col">
|
||||
{% if period.step == 'DRAFT' %}
|
||||
<span class="badge bg-secondary">{{- 'Draft'|trans|upper -}}</span>
|
||||
{% elseif period.step == 'CONFIRMED' %}
|
||||
<span class="badge bg-primary">{{- 'Confirmed'|trans|upper -}}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">{{- 'Closed'|trans|upper -}}</span>
|
||||
<span class="badge bg-secondary" style="font-size: 85%;" title="{{ 'course.draft'|trans|e('html_attr') }}">{{ 'course.draft'|trans }}</span>
|
||||
{% elseif period.step == 'CLOSED' %}
|
||||
<span class="badge bg-danger" style="font-size: 85%;" title="{{ 'course.closed'|trans|e('html_attr') }}">{{ 'course.closed'|trans }}</span>
|
||||
{% elseif period.step == 'CONFIRMED_INACTIVE_SHORT' %}
|
||||
<span class="badge bg-chill-yellow text-primary" style="font-size: 85%;" title="{{ 'course.inactive_short'|trans|e('html_attr') }}">{{ 'course.inactive_short'|trans }}</span>
|
||||
{% elseif period.step == 'CONFIRMED_INACTIVE_LONG' %}
|
||||
<span class="badge bg-danger" style="font-size: 85%;" title="{{ 'course.inactive_long'|trans|e('html_attr') }}">{{ 'course.inactive_long'|trans }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
{% if period is not null %}
|
||||
<div class="flex-table">
|
||||
{% if is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', period) %}
|
||||
{% include 'ChillPersonBundle:AccompanyingPeriod:_list_item.html.twig' with {
|
||||
{% include "@ChillPerson/AccompanyingPeriod/_list_item.html.twig" with {
|
||||
'recordAction': _self.recordAction(notification.relatedEntityId),
|
||||
'itemBlocClass': 'bg-chill-llight-gray'
|
||||
} %}
|
||||
|
@ -17,6 +17,15 @@
|
||||
<div class="col-md-10">
|
||||
<h1>{{ 'My accompanying periods'|trans }}</h1>
|
||||
|
||||
<ul class="nav nav-pills justify-content-center">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if active == true %}active{% endif %}" aria-current="page" href="{{ chill_path_forward_return_path('chill_person_accompanying_period_user', {'active': true}) }}">{{ ['Confirmed'|trans, 'course.inactive_short'|trans, 'course.inactive_long'|trans]|join(', ') }}</a>
|
||||
</li>
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link {% if active == false %}active{% endif %}" href="{{ chill_path_forward_return_path('chill_person_accompanying_period_user', {'active': false}) }}">{{ 'course.closed'|trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>{{ 'Number of periods'|trans }}: <span class="badge rounded-pill bg-primary">{{ pagination.totalItems }}</span></p>
|
||||
|
||||
<div class="flex-table accompanyingcourse-list">
|
||||
|
@ -40,13 +40,14 @@
|
||||
{{ 'Household summary'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{# TODO: add ACL to check if user is allowed to edit household? #}
|
||||
<li>
|
||||
<a class="btn btn-create"
|
||||
href="{{ path ('chill_household_accompanying_course_new', {'household_id' : household.id } ) }}" role="button">
|
||||
{{ 'Create an accompanying period'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% if is_granted('CHILL_PERSON_HOUSEHOLD_EDIT', household) %}
|
||||
<li>
|
||||
<a class="btn btn-create"
|
||||
href="{{ path ('chill_household_accompanying_course_new', {'household_id' : household.id } ) }}" role="button">
|
||||
{{ 'Create an accompanying period'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
<li><b>{{ person.counters.nb_activity }}</b> {{ (person.counters.nb_activity > 1)? 'échanges' : 'échange' }}</li>
|
||||
<li><b>{{ person.counters.nb_task }}</b> {{ (person.counters.nb_task > 1)? 'tâches' : 'tâche' }}</li>
|
||||
<li><b>{{ person.counters.nb_document }}</b> {{ (person.counters.nb_document > 1)? 'documents' : 'document' }}</li>
|
||||
<li><b>{{ person.counters.nb_event }}</b> {{ (person.counters.nb_event > 1)? 'événements' : 'événement' }}</li>
|
||||
{# <li><b>{{ person.counters.nb_event }}</b> {{ (person.counters.nb_event > 1)? 'événements' : 'événement' }}</li>#}
|
||||
<li><b>{{ person.counters.nb_addresses }}</b> {{ (person.counters.nb_addresses > 1)? 'adresses' : 'adresse' }}</li>
|
||||
|
||||
</ul>
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
<h1>{{ 'Merge duplicate persons folders'|trans }}</h1>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-11">
|
||||
<p><b>{{ 'Old person'|trans }}</b>:
|
||||
{{ 'Old person explain'|trans }}
|
||||
</p>
|
||||
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-11">
|
||||
<p><b>{{ 'New person'|trans }}</b>:
|
||||
{{ 'New person explain'|trans }}
|
||||
</p>
|
||||
@ -63,10 +63,10 @@
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
<div class="col-md-4 centered">
|
||||
<div class="col-md-12 centered">
|
||||
|
||||
<div class="container-fluid" style="padding-top: 1em;">
|
||||
<div class="col-1 clear" style="padding-top: 10px;">
|
||||
<div class="clear" style="padding-top: 10px;">
|
||||
{{ form_widget(form.confirm) }}
|
||||
</div>
|
||||
<div class="col-11">
|
||||
|
@ -3,6 +3,14 @@
|
||||
{{ 'workflow.SocialAction deleted'|trans }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="flex-table accompanying-course-work">
|
||||
{% include '@ChillPerson/AccompanyingCourseWork/_item.html.twig' with {
|
||||
'w': work,
|
||||
'displayAction': false,
|
||||
'displayContent': 'short',
|
||||
'itemBlocClass': 'bg-chill-light-gray'
|
||||
} %}
|
||||
</div>
|
||||
{% if display_action is defined and display_action == true %}
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
|
@ -123,7 +123,7 @@
|
||||
<ul class="record_actions">
|
||||
<li>{{ doc.storedObject|chill_document_button_group(doc.title, is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE', evaluation.accompanyingPeriodWork)) }}</li>
|
||||
<li>
|
||||
<a class="btn btn-show" href="{{ path('chill_person_accompanying_period_work_edit', {'id': evaluation.accompanyingPeriodWork.id}) }}">
|
||||
<a class="btn btn-show" href="{{ path('chill_person_accompanying_period_work_edit', {'id': evaluation.accompanyingPeriodWork.id, 'doc_id': doc.id}) }}">
|
||||
{{ 'Show'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
|
@ -18,6 +18,7 @@ use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
@ -119,6 +120,7 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH
|
||||
->generate(self::class)
|
||||
->addCheckFor(null, [self::CREATE, self::REASSIGN_BULK])
|
||||
->addCheckFor(AccompanyingPeriod::class, [self::TOGGLE_CONFIDENTIAL, ...self::ALL])
|
||||
->addCheckFor(Household::class, [self::SEE])
|
||||
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||
->addCheckFor(Center::class, [self::STATS])
|
||||
->build();
|
||||
|
@ -22,10 +22,14 @@ use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||
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 DateTime;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@ -40,6 +44,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* @see AccompanyingPeriodContextTest
|
||||
* @template-implements DocGeneratorContextWithPublicFormInterface<AccompanyingPeriod>
|
||||
*/
|
||||
class AccompanyingPeriodContext implements
|
||||
@ -62,6 +67,10 @@ class AccompanyingPeriodContext implements
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
private ThirdPartyRender $thirdPartyRender;
|
||||
|
||||
private ThirdPartyRepository $thirdPartyRepository;
|
||||
|
||||
public function __construct(
|
||||
DocumentCategoryRepository $documentCategoryRepository,
|
||||
NormalizerInterface $normalizer,
|
||||
@ -70,7 +79,9 @@ class AccompanyingPeriodContext implements
|
||||
PersonRenderInterface $personRender,
|
||||
PersonRepository $personRepository,
|
||||
TranslatorInterface $translator,
|
||||
BaseContextData $baseContextData
|
||||
BaseContextData $baseContextData,
|
||||
ThirdPartyRender $thirdPartyRender,
|
||||
ThirdPartyRepository $thirdPartyRepository
|
||||
) {
|
||||
$this->documentCategoryRepository = $documentCategoryRepository;
|
||||
$this->normalizer = $normalizer;
|
||||
@ -80,6 +91,8 @@ class AccompanyingPeriodContext implements
|
||||
$this->personRepository = $personRepository;
|
||||
$this->translator = $translator;
|
||||
$this->baseContextData = $baseContextData;
|
||||
$this->thirdPartyRender = $thirdPartyRender;
|
||||
$this->thirdPartyRepository = $thirdPartyRepository;
|
||||
}
|
||||
|
||||
public function adminFormReverseTransform(array $data): array
|
||||
@ -103,11 +116,12 @@ class AccompanyingPeriodContext 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('Third party'),
|
||||
];
|
||||
|
||||
if (array_key_exists('category', $data)) {
|
||||
$r['category'] = array_key_exists('category', $data) ?
|
||||
$this->documentCategoryRepository->find($data['category']) : null;
|
||||
$r['category'] = $this->documentCategoryRepository->find($data['category']);
|
||||
}
|
||||
|
||||
return $r;
|
||||
@ -140,6 +154,14 @@ class AccompanyingPeriodContext implements
|
||||
'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,
|
||||
])
|
||||
->add('category', EntityType::class, [
|
||||
'placeholder' => 'Choose a document category',
|
||||
'class' => DocumentCategory::class,
|
||||
@ -190,6 +212,28 @@ class AccompanyingPeriodContext implements
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$thirdParties = [...array_values(array_filter([$entity->getRequestorThirdParty()])), ...array_values(array_filter(
|
||||
array_map(
|
||||
fn (Resource $r): ?ThirdParty => $r->getThirdParty(),
|
||||
$entity->getResources()->filter(
|
||||
static fn (Resource $r): bool => null !== $r->getThirdParty()
|
||||
)->toArray()
|
||||
)
|
||||
))];
|
||||
|
||||
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 getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
|
||||
@ -215,6 +259,13 @@ class AccompanyingPeriodContext implements
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['thirdParty']) {
|
||||
$data['thirdParty'] = $this->normalizer->normalize($contextGenerationData['thirdParty'], 'docgen', [
|
||||
'docgen:expects' => ThirdParty::class,
|
||||
'groups' => 'docgen:read'
|
||||
]);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@ -254,7 +305,7 @@ class AccompanyingPeriodContext implements
|
||||
{
|
||||
$options = $template->getOptions();
|
||||
|
||||
return $options['mainPerson'] || $options['person1'] || $options['person2'];
|
||||
return $options['mainPerson'] || $options['person1'] || $options['person2'] || $options ['thirdParty'];
|
||||
}
|
||||
|
||||
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
|
||||
@ -264,6 +315,8 @@ class AccompanyingPeriodContext implements
|
||||
$normalized[$k] = null !== ($data[$k] ?? null) ? $data[$k]->getId() : null;
|
||||
}
|
||||
|
||||
$normalized['thirdParty'] = ($data['thirdParty'] ?? null)?->getId();
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
@ -279,6 +332,12 @@ class AccompanyingPeriodContext implements
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== ($id = ($data['thirdParty'] ?? null))) {
|
||||
$denormalized['thirdParty'] = $this->thirdPartyRepository->find($id);
|
||||
} else {
|
||||
$denormalized['thirdParty'] = null;
|
||||
}
|
||||
|
||||
return $denormalized;
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,18 @@ use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepository;
|
||||
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\FormBuilderInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @implements DocGeneratorContextWithPublicFormInterface<AccompanyingPeriodWorkEvaluation>
|
||||
@ -43,18 +48,26 @@ class AccompanyingPeriodWorkEvaluationContext implements
|
||||
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
private ThirdPartyRender $thirdPartyRender;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
AccompanyingPeriodWorkContext $accompanyingPeriodWorkContext,
|
||||
EntityManagerInterface $em,
|
||||
EvaluationRepository $evaluationRepository,
|
||||
NormalizerInterface $normalizer,
|
||||
TranslatableStringHelperInterface $translatableStringHelper
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
ThirdPartyRender $thirdPartyRender,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->accompanyingPeriodWorkContext = $accompanyingPeriodWorkContext;
|
||||
$this->em = $em;
|
||||
$this->evaluationRepository = $evaluationRepository;
|
||||
$this->normalizer = $normalizer;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->thirdPartyRender = $thirdPartyRender;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function adminFormReverseTransform(array $data): array
|
||||
@ -102,6 +115,31 @@ class AccompanyingPeriodWorkEvaluationContext implements
|
||||
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
|
||||
{
|
||||
$this->accompanyingPeriodWorkContext->buildPublicForm($builder, $template, $entity->getAccompanyingPeriodWork());
|
||||
|
||||
$thirdParties = [...array_values(array_filter($entity->getAccompanyingPeriodWork()->getThirdParties()->toArray())), ...array_values(array_filter([$entity->getAccompanyingPeriodWork()->getHandlingThierParty()])), ...array_values(
|
||||
array_filter(
|
||||
array_map(
|
||||
fn (Resource $r): ?ThirdParty => $r->getThirdParty(),
|
||||
$entity->getAccompanyingPeriodWork()->getAccompanyingPeriod()->getResources()->filter(
|
||||
static fn (Resource $r): bool => null !== $r->getThirdParty()
|
||||
)->toArray()
|
||||
)
|
||||
)
|
||||
)];
|
||||
|
||||
$options = $template->getOptions();
|
||||
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 getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
|
||||
@ -116,7 +154,6 @@ class AccompanyingPeriodWorkEvaluationContext implements
|
||||
AbstractNormalizer::GROUPS => ['docgen:read'],
|
||||
]
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -26,14 +26,22 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Person\PersonResource;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Person\ResidentialAddress;
|
||||
use Chill\PersonBundle\Repository\PersonRepository;
|
||||
use Chill\PersonBundle\Repository\ResidentialAddressRepository;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
|
||||
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use LogicException;
|
||||
use Service\DocGenerator\PersonContextTest;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
@ -43,6 +51,9 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* @see PersonContextTest
|
||||
*/
|
||||
final class PersonContext implements PersonContextInterface
|
||||
{
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
@ -67,6 +78,12 @@ final class PersonContext implements PersonContextInterface
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
private ThirdPartyRender $thirdPartyRender;
|
||||
|
||||
private ThirdPartyRepository $thirdPartyRepository;
|
||||
|
||||
private ResidentialAddressRepository $residentialAddressRepository;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationHelperInterface $authorizationHelper,
|
||||
BaseContextData $baseContextData,
|
||||
@ -78,7 +95,10 @@ final class PersonContext implements PersonContextInterface
|
||||
ScopeRepositoryInterface $scopeRepository,
|
||||
Security $security,
|
||||
TranslatorInterface $translator,
|
||||
TranslatableStringHelperInterface $translatableStringHelper
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
ThirdPartyRender $thirdPartyRender,
|
||||
ThirdPartyRepository $thirdPartyRepository,
|
||||
ResidentialAddressRepository $residentialAddressRepository
|
||||
) {
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->centerResolverManager = $centerResolverManager;
|
||||
@ -91,6 +111,9 @@ final class PersonContext implements PersonContextInterface
|
||||
$this->showScopes = $parameterBag->get('chill_main')['acl']['form_show_scopes'];
|
||||
$this->translator = $translator;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->thirdPartyRender = $thirdPartyRender;
|
||||
$this->thirdPartyRepository = $thirdPartyRepository;
|
||||
$this->residentialAddressRepository = $residentialAddressRepository;
|
||||
}
|
||||
|
||||
public function adminFormReverseTransform(array $data): array
|
||||
@ -110,11 +133,12 @@ final class PersonContext implements PersonContextInterface
|
||||
$r = [
|
||||
'mainPerson' => $data['mainPerson'] ?? false,
|
||||
'mainPersonLabel' => $data['mainPersonLabel'] ?? $this->translator->trans('docgen.Main person'),
|
||||
'thirdParty' => $data['thirdParty'] ?? false,
|
||||
'thirdPartyLabel' => $data['thirdPartyLabel'] ?? $this->translator->trans('Third party'),
|
||||
];
|
||||
|
||||
if (array_key_exists('category', $data)) {
|
||||
$r['category'] = array_key_exists('category', $data) ?
|
||||
$this->documentCategoryRepository->find($data['category']) : null;
|
||||
$r['category'] = $this->documentCategoryRepository->find($data['category']);
|
||||
}
|
||||
|
||||
return $r;
|
||||
@ -131,6 +155,14 @@ final class PersonContext implements PersonContextInterface
|
||||
->setParameter('docClass', PersonDocument::class),
|
||||
'choice_label' => fn ($entity = null) => $entity ? $this->translatableStringHelper->localize($entity->getName()) : '',
|
||||
'required' => true,
|
||||
])
|
||||
->add('thirdParty', CheckboxType::class, [
|
||||
'required' => false,
|
||||
'label' => 'docgen.Ask for thirdParty',
|
||||
])
|
||||
->add('thirdPartyLabel', TextType::class, [
|
||||
'label' => 'docgen.thirdParty label',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
@ -139,12 +171,47 @@ final class PersonContext implements PersonContextInterface
|
||||
*/
|
||||
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
|
||||
{
|
||||
$options = $template->getOptions();
|
||||
|
||||
$builder->add('title', TextType::class, [
|
||||
'required' => true,
|
||||
'label' => 'docgen.Document title',
|
||||
'data' => $this->translatableStringHelper->localize($template->getName()),
|
||||
]);
|
||||
|
||||
$thirdParties = [...array_values(
|
||||
array_filter(
|
||||
array_map(
|
||||
fn (ResidentialAddress $r): ?ThirdParty => $r->getHostThirdParty(),
|
||||
$this
|
||||
->residentialAddressRepository
|
||||
->findCurrentResidentialAddressByPerson($entity)
|
||||
)
|
||||
)
|
||||
), ...array_values(
|
||||
array_filter(
|
||||
array_map(
|
||||
fn (PersonResource $r): ?ThirdParty => $r->getThirdParty(),
|
||||
$entity->getResources()->filter(
|
||||
static fn (PersonResource $r): bool => null !== $r->getThirdParty()
|
||||
)->toArray()
|
||||
)
|
||||
)
|
||||
)];
|
||||
|
||||
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'),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->isScopeNecessary($entity)) {
|
||||
$builder->add('scope', ScopePickerType::class, [
|
||||
'center' => $this->centerResolverManager->resolveCenters($entity),
|
||||
@ -156,10 +223,6 @@ final class PersonContext implements PersonContextInterface
|
||||
|
||||
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
|
||||
{
|
||||
if (!$entity instanceof Person) {
|
||||
throw new UnexpectedTypeException($entity, Person::class);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$data = array_merge($data, $this->baseContextData->getData($contextGenerationData['creator'] ?? null));
|
||||
$data['person'] = $this->normalizer->normalize($entity, 'docgen', [
|
||||
@ -170,6 +233,13 @@ final class PersonContext implements PersonContextInterface
|
||||
'docgen:person:with-budget' => true,
|
||||
]);
|
||||
|
||||
if ($template->getOptions()['thirdParty']) {
|
||||
$data['thirdParty'] = $this->normalizer->normalize($contextGenerationData['thirdParty'], 'docgen', [
|
||||
'docgen:expects' => ThirdParty::class,
|
||||
'groups' => 'docgen:read'
|
||||
]);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@ -223,6 +293,7 @@ final class PersonContext implements PersonContextInterface
|
||||
return [
|
||||
'title' => $data['title'] ?? '',
|
||||
'scope_id' => $scope instanceof Scope ? $scope->getId() : null,
|
||||
'thirdParty' => ($data['thirdParty'] ?? null)?->getId(),
|
||||
];
|
||||
}
|
||||
|
||||
@ -242,6 +313,7 @@ final class PersonContext implements PersonContextInterface
|
||||
return [
|
||||
'title' => $data['title'] ?? '',
|
||||
'scope' => $scope,
|
||||
'thirdParty' => null !== ($id = ($data['thirdParty'] ?? null)) ? $this->thirdPartyRepository->find($id) : null,
|
||||
];
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user