mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 09:18:24 +00:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			issue435_n
			...
			20-update-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | bddb6e615e | ||
|  | a18ea30c8f | ||
|  | 2ba240525c | ||
|  | 5b76338fb0 | ||
|  | ce8399945a | ||
|  | 1605bc5d08 | 
							
								
								
									
										64
									
								
								.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								.env
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| ## | ||||
| ##  Manually dump .env files in .env.local.php with | ||||
| ## `$ composer symfony:dump-env prod` | ||||
| ## | ||||
|  | ||||
| ## Project environment | ||||
| APP_ENV=dev | ||||
|  | ||||
| ## Enable debug | ||||
| APP_DEBUG=true | ||||
|  | ||||
| ## Locale | ||||
| LOCALE=fr | ||||
|  | ||||
| ## Framework secret | ||||
| APP_SECRET=ThisTokenIsNotSoSecretChangeIt | ||||
|  | ||||
| ## Symfony/swiftmailer | ||||
| MAILER_TRANSPORT=smtp | ||||
| MAILER_HOST=smtp | ||||
| MAILER_PORT=1025 | ||||
| MAILER_CRYPT= | ||||
| MAILER_AUTH= | ||||
| MAILER_USER= | ||||
| MAILER_PASSWORD= | ||||
| MAILER_URL=${MAILER_TRANSPORT}://${MAILER_HOST}:${MAILER_PORT}?encryption=${MAILER_CRYPT}&auth_mode=${MAILER_AUTH}&username=${MAILER_USER}&password=${MAILER_PASSWORD} | ||||
|  | ||||
| ## Notifications | ||||
| NOTIFICATION_HOST=localhost:8001 | ||||
| NOTIFICATION_FROM_EMAIL=admin@chill.social | ||||
| NOTIFICATION_FROM_NAME=Chill | ||||
|  | ||||
| ## Gelf | ||||
| GELF_HOST=gelf | ||||
| GELF_PORT=12201 | ||||
|  | ||||
| ## OVH OpenStack Storage User/Role | ||||
| OS_USERNAME= | ||||
| OS_PASSWORD= | ||||
| OS_TENANT_ID= | ||||
| OS_REGION_NAME=GRA | ||||
| OS_AUTH_URL=https://auth.cloud.ovh.net/v2.0/ | ||||
|  | ||||
| ## OVH OpenStack Storage Container | ||||
| ASYNC_UPLOAD_TEMP_URL_KEY= | ||||
| ASYNC_UPLOAD_TEMP_URL_BASE_PATH= | ||||
| ASYNC_UPLOAD_TEMP_URL_CONTAINER= | ||||
|  | ||||
| ## Redis Cache | ||||
| REDIS_HOST=redis | ||||
| REDIS_PORT=6379 | ||||
| REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} | ||||
|  | ||||
| ## Twilio | ||||
| TWILIO_SID=~ | ||||
| TWILIO_SECRET=~ | ||||
|  | ||||
| ## DOCKER IMAGES REGISTRY | ||||
| #IMAGE_PHP= | ||||
| #IMAGE_NGINX= | ||||
|  | ||||
| ## DOCKER IMAGES VERSION | ||||
| #VERSION=test | ||||
| VERSION=prod | ||||
							
								
								
									
										47
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -9,52 +9,18 @@ and this project adheres to | ||||
| * date versioning for test releases | ||||
|  | ||||
| ## Unreleased | ||||
| * AddPersons: remove ul-li html tags from AddPersons (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/419) | ||||
|  | ||||
| <!-- write down unreleased development here --> | ||||
| * 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) | ||||
| * [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) | ||||
| * 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 | ||||
|  | ||||
|  | ||||
| ## Test releases | ||||
|  | ||||
| ### 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. | ||||
|  | ||||
| ### test release 2021-01-31 | ||||
|  | ||||
| [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] 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) | ||||
| @@ -65,6 +31,7 @@ and this project adheres to | ||||
| * [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 | ||||
| * [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal | ||||
|  | ||||
|  | ||||
| ### test release 2021-01-28 | ||||
| @@ -100,7 +67,7 @@ and this project adheres to | ||||
| * [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] | ||||
| * [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 | ||||
|   | ||||
| @@ -408,6 +408,24 @@ parameters: | ||||
| 			count: 1 | ||||
| 			path: src/Bundle/ChillCustomFieldsBundle/Form/CustomFieldsGroupType.php | ||||
|  | ||||
| 		- | ||||
| 			message: | ||||
| 				""" | ||||
| 					#^Call to deprecated method getReachableScopes\\(\\) of class Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\: | ||||
| 					Use getReachableCircles$# | ||||
| 				""" | ||||
| 			count: 1 | ||||
| 			path: src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php | ||||
|  | ||||
| 		- | ||||
| 			message: | ||||
| 				""" | ||||
| 					#^Parameter \\$translator of method Chill\\\\DocStoreBundle\\\\Controller\\\\DocumentPersonController\\:\\:__construct\\(\\) has typehint with deprecated interface Symfony\\\\Component\\\\Translation\\\\TranslatorInterface\\: | ||||
| 					since Symfony 4\\.2, use Symfony\\\\Contracts\\\\Translation\\\\TranslatorInterface instead$# | ||||
| 				""" | ||||
| 			count: 1 | ||||
| 			path: src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php | ||||
|  | ||||
| 		- | ||||
| 			message: | ||||
| 				""" | ||||
| @@ -909,6 +927,15 @@ parameters: | ||||
| 			count: 1 | ||||
| 			path: src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php | ||||
|  | ||||
| 		- | ||||
| 			message: | ||||
| 				""" | ||||
| 					#^Parameter \\$trans of method Chill\\\\PersonBundle\\\\Controller\\\\AccompanyingCourseWorkController\\:\\:__construct\\(\\) has typehint with deprecated interface Symfony\\\\Component\\\\Translation\\\\TranslatorInterface\\: | ||||
| 					since Symfony 4\\.2, use Symfony\\\\Contracts\\\\Translation\\\\TranslatorInterface instead$# | ||||
| 				""" | ||||
| 			count: 1 | ||||
| 			path: src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php | ||||
|  | ||||
| 		- | ||||
| 			message: | ||||
| 				""" | ||||
|   | ||||
| @@ -34,7 +34,6 @@ use Psr\Log\LoggerInterface; | ||||
| use RuntimeException; | ||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||||
| use Symfony\Component\EventDispatcher\EventDispatcherInterface; | ||||
| use Symfony\Component\Form\Extension\Core\Type\HiddenType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\SubmitType; | ||||
| use Symfony\Component\Form\FormInterface; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| @@ -201,36 +200,12 @@ final class ActivityController extends AbstractController | ||||
|             'role' => new Role('CHILL_ACTIVITY_UPDATE'), | ||||
|             'activityType' => $entity->getActivityType(), | ||||
|             'accompanyingPeriod' => $accompanyingPeriod, | ||||
|         ]); | ||||
|  | ||||
|         if ($form->has('documents')) { | ||||
|             $form->add('gendocTemplateId', HiddenType::class, [ | ||||
|                 'mapped' => false, | ||||
|                 'data' => null, | ||||
|                 'attr' => [ | ||||
|                     // required for js | ||||
|                     'data-template-id' => 'data-template-id', | ||||
|                 ], | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         $form->handleRequest($request); | ||||
|         ])->handleRequest($request); | ||||
|  | ||||
|         if ($form->isSubmitted() && $form->isValid()) { | ||||
|             $this->entityManager->persist($entity); | ||||
|             $this->entityManager->flush(); | ||||
|  | ||||
|             if ($form->has('gendocTemplateId') && null !== $form['gendocTemplateId']->getData()) { | ||||
|                 return $this->redirectToRoute( | ||||
|                     'chill_docgenerator_generate_from_template', | ||||
|                     [ | ||||
|                         'template' => $form->get('gendocTemplateId')->getData(), | ||||
|                         'entityClassName' => Activity::class, | ||||
|                         'entityId' => $entity->getId(), | ||||
|                     ] | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             $this->addFlash('success', $this->get('translator')->trans('Success : activity updated!')); | ||||
|  | ||||
|             $params = $this->buildParamsToUrl($person, $accompanyingPeriod); | ||||
| @@ -418,36 +393,12 @@ final class ActivityController extends AbstractController | ||||
|             'role' => new Role('CHILL_ACTIVITY_CREATE'), | ||||
|             'activityType' => $entity->getActivityType(), | ||||
|             'accompanyingPeriod' => $accompanyingPeriod, | ||||
|         ]); | ||||
|  | ||||
|         if ($form->has('documents')) { | ||||
|             $form->add('gendocTemplateId', HiddenType::class, [ | ||||
|                 'mapped' => false, | ||||
|                 'data' => null, | ||||
|                 'attr' => [ | ||||
|                     // required for js | ||||
|                     'data-template-id' => 'data-template-id', | ||||
|                 ], | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         $form->handleRequest($request); | ||||
|         ])->handleRequest($request); | ||||
|  | ||||
|         if ($form->isSubmitted() && $form->isValid()) { | ||||
|             $this->entityManager->persist($entity); | ||||
|             $this->entityManager->flush(); | ||||
|  | ||||
|             if ($form->has('gendocTemplateId') && null !== $form['gendocTemplateId']->getData()) { | ||||
|                 return $this->redirectToRoute( | ||||
|                     'chill_docgenerator_generate_from_template', | ||||
|                     [ | ||||
|                         'template' => $form->get('gendocTemplateId')->getData(), | ||||
|                         'entityClassName' => Activity::class, | ||||
|                         'entityId' => $entity->getId(), | ||||
|                     ] | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             $this->addFlash('success', $this->get('translator')->trans('Success : activity created!')); | ||||
|  | ||||
|             $params = $this->buildParamsToUrl($person, $accompanyingPeriod); | ||||
|   | ||||
| @@ -271,7 +271,7 @@ class ActivityType | ||||
|     public function checkSocialActionsVisibility(ExecutionContextInterface $context, $payload) | ||||
|     { | ||||
|         if ($this->socialIssuesVisible !== $this->socialActionsVisible) { | ||||
|             if (!(2 === $this->socialIssuesVisible && 1 === $this->socialActionsVisible)) { | ||||
|             if (!($this->socialIssuesVisible === 2 && $this->socialActionsVisible === 1)) { | ||||
|                 $context | ||||
|                     ->buildViolation('The socialActionsVisible value is not compatible with the socialIssuesVisible value') | ||||
|                     ->atPath('socialActionsVisible') | ||||
|   | ||||
| @@ -19,18 +19,14 @@ | ||||
|          </ul> | ||||
|       </div> | ||||
|  | ||||
|       <ul class="record_actions"> | ||||
|          <li class="add-persons"> | ||||
|             <add-persons | ||||
|                buttonTitle="activity.add_persons" | ||||
|                modalTitle="activity.add_persons" | ||||
|                v-bind:key="addPersons.key" | ||||
|                v-bind:options="addPersonsOptions" | ||||
|                @addNewPersons="addNewPersons" | ||||
|                ref="addPersons"> | ||||
|             </add-persons> | ||||
|          </li> | ||||
|       </ul> | ||||
|       <add-persons | ||||
|          buttonTitle="activity.add_persons" | ||||
|          modalTitle="activity.add_persons" | ||||
|          v-bind:key="addPersons.key" | ||||
|          v-bind:options="addPersonsOptions" | ||||
|          @addNewPersons="addNewPersons" | ||||
|          ref="addPersons"> | ||||
|       </add-persons> | ||||
|  | ||||
|    </teleport> | ||||
| </template> | ||||
|   | ||||
| @@ -2,15 +2,11 @@ import { createApp } from 'vue'; | ||||
| import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n' | ||||
| import { activityMessages } from './i18n' | ||||
| import store from './store' | ||||
| import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue'; | ||||
| import {fetchTemplates} from 'ChillDocGeneratorAssets/api/pickTemplate.js'; | ||||
|  | ||||
| import App from './App.vue'; | ||||
|  | ||||
| const i18n = _createI18n(activityMessages); | ||||
|  | ||||
| // app for activity | ||||
|  | ||||
| const hasSocialIssues = document.querySelector('#social-issues-acc') !== null; | ||||
| const hasLocation = document.querySelector('#location') !== null; | ||||
| const hasPerson = document.querySelector('#add-persons') !== null; | ||||
| @@ -33,54 +29,3 @@ const app = createApp({ | ||||
| .use(i18n) | ||||
| .component('app', App) | ||||
| .mount('#activity'); | ||||
|  | ||||
|  | ||||
| // app for picking template | ||||
|  | ||||
| const i18nGendoc = _createI18n({}); | ||||
|  | ||||
| document.querySelectorAll('div[data-docgen-template-picker]').forEach(el => { | ||||
|     fetchTemplates(el.dataset.entityClass).then(templates => { | ||||
|         const picker = { | ||||
|             template: | ||||
|                 '<pick-template :templates="this.templates" :preventDefaultMoveToGenerate="true" ' + | ||||
|                     ':entityClass="faked" @go-to-generate-document="generateDoc"></pick-template>', | ||||
|             components: { | ||||
|                 PickTemplate, | ||||
|             }, | ||||
|             data() { | ||||
|                 return { | ||||
|                     templates: templates, | ||||
|                     entityId: el.dataset.entityId, | ||||
|                 } | ||||
|             }, | ||||
|             methods: { | ||||
|                 generateDoc({event, link, template}) { | ||||
|                     console.log('generateDoc'); | ||||
|                     console.log('link', link); | ||||
|                     console.log('template', template); | ||||
|  | ||||
|                     let hiddenInput = document.querySelector("input[data-template-id]"); | ||||
|  | ||||
|                     if (hiddenInput === null) { | ||||
|                         console.error('hidden input not found'); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     hiddenInput.value = template; | ||||
|  | ||||
|                     let form = document.querySelector('form[name="chill_activitybundle_activity"'); | ||||
|  | ||||
|                     if (form === null) { | ||||
|                         console.error('form not found'); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     form.submit(); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         createApp(picker).use(i18nGendoc).mount(el); | ||||
|     }) | ||||
|  | ||||
| }); | ||||
|   | ||||
| @@ -68,7 +68,7 @@ | ||||
|                     <div class="wl-col title"><h3>{{ 'Referrer'|trans }}</h3></div> | ||||
|                     <div class="wl-col list"> | ||||
|                         <p class="wl-item"> | ||||
|                             {{ activity.user|chill_entity_render_box }} | ||||
|                             {{ activity.user|chill_entity_render_string|capitalize }} | ||||
|                         </p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|   | ||||
| @@ -89,9 +89,9 @@ | ||||
|  | ||||
| {%- if edit_form.documents is defined -%} | ||||
|     {{ form_row(edit_form.documents) }} | ||||
|     <div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\ActivityBundle\Entity\Activity" data-entity-id="{{ entity.id }}"></div> | ||||
| {% endif %} | ||||
|  | ||||
| <div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\ActivityBundle\Entity\Activity" data-entity-id="{{ entity.id }}"></div> | ||||
|  | ||||
| {% set person_id = null %} | ||||
| {% if entity.person %} | ||||
|   | ||||
| @@ -24,10 +24,12 @@ | ||||
|         window.activity = {{ activity_json|json_encode|raw }}; | ||||
|     </script> | ||||
|     {{ encore_entry_script_tags('vue_activity') }} | ||||
|     {{ encore_entry_script_tags('mod_docgen_picktemplate') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block css %} | ||||
|     {{ parent() }} | ||||
|     {{ encore_entry_link_tags('mod_async_upload') }} | ||||
|     {{ encore_entry_link_tags('vue_activity') }} | ||||
|     {{ encore_entry_link_tags('mod_docgen_picktemplate') }} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -39,9 +39,11 @@ | ||||
|         window.activity = {{ activity_json|json_encode|raw }}; | ||||
|     </script> | ||||
|     {{ encore_entry_script_tags('vue_activity') }} | ||||
|     {{ encore_entry_script_tags('mod_docgen_picktemplate') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block css %} | ||||
|     {{ encore_entry_link_tags('mod_async_upload') }} | ||||
|     {{ encore_entry_link_tags('vue_activity') }} | ||||
|     {{ encore_entry_link_tags('mod_docgen_picktemplate') }} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
|                         <p class="date-label">{{ activity.date|format_date('short') }}</p> | ||||
|                     {%- endif -%} | ||||
|  | ||||
|                     <span class="like-h3">{{ activity.type.name|localize_translatable_string }}</span> | ||||
|                     <span class="like-h3">{{ activity.type.name | localize_translatable_string }}</span> | ||||
|  | ||||
|                     {% if activity.emergency %} | ||||
|                         <span class="badge bg-danger rounded-pill fs-6">{{ 'Emergency'|trans|upper }}</span> | ||||
| @@ -41,7 +41,7 @@ | ||||
|                         {% if activity.user and t.userVisible %} | ||||
|                             <li> | ||||
|                                 <span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span> | ||||
|                                 <b>{{ activity.user|chill_entity_render_box}}</b> | ||||
|                                 <b>{{ activity.user.usernameCanonical }}</b> | ||||
|                             </li> | ||||
|                         {% endif %} | ||||
|  | ||||
|   | ||||
| @@ -87,7 +87,6 @@ | ||||
|  | ||||
| {%- if form.documents is defined -%} | ||||
|     {{ form_row(form.documents) }} | ||||
|     <div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\ActivityBundle\Entity\Activity" data-entity-id="{{ entity.id }}"></div> | ||||
| {% endif %} | ||||
|  | ||||
| {%- if form.attendee is defined -%} | ||||
|   | ||||
| @@ -17,6 +17,10 @@ | ||||
|     {{ parent() }} | ||||
|     {{ encore_entry_script_tags('mod_async_upload') }} | ||||
|     <script type="text/javascript"> | ||||
|         window.addEventListener('DOMContentLoaded', function (e) { | ||||
|             chill.displayAlertWhenLeavingUnsubmittedForm('form[name="{{ form.vars.form.vars.name }}"]', | ||||
|             '{{ "You are going to leave a page with unsubmitted data. Are you sure you want to leave ?"|trans }}'); | ||||
|         }); | ||||
|         window.activity = {{ activity_json|json_encode|raw }}; | ||||
|         {% if default_location is not null %}window.default_location_id = {{ default_location.id }}{% endif %}; | ||||
|     </script> | ||||
|   | ||||
| @@ -17,6 +17,7 @@ use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface; | ||||
| use Chill\DocGeneratorBundle\Context\Exception\UnexpectedTypeException; | ||||
| use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate; | ||||
| use Chill\DocGeneratorBundle\Service\Context\BaseContextData; | ||||
| use Chill\DocStoreBundle\Entity\Document; | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Chill\DocStoreBundle\Repository\DocumentCategoryRepository; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelperInterface; | ||||
| @@ -209,8 +210,11 @@ class ActivityContext implements | ||||
|      */ | ||||
|     public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void | ||||
|     { | ||||
|         $entity->addDocument($storedObject); | ||||
|         $doc = new StoredObject(); | ||||
|         // TODO push document to remote | ||||
|  | ||||
|         $this->em->persist($storedObject); | ||||
|         $this->em->persist($doc); | ||||
|  | ||||
|         $entity->addDocument($doc); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -167,6 +167,7 @@ class AsideActivityCategory | ||||
|         } | ||||
|  | ||||
|         $this->parent = $parent; | ||||
|         dump($this); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|   | ||||
| @@ -97,6 +97,8 @@ class CalendarType extends AbstractType | ||||
|                     return $res; | ||||
|                 }, | ||||
|                 static function (?string $dateAsString): DateTimeImmutable { | ||||
|                     dump($dateAsString); | ||||
|  | ||||
|                     return new DateTimeImmutable($dateAsString); | ||||
|                 } | ||||
|             )); | ||||
|   | ||||
| @@ -1,13 +0,0 @@ | ||||
|  | ||||
| const buildLink = function(templateId, entityId, entityClass) { | ||||
|     const | ||||
|         entityIdEncoded = encodeURI(entityId), | ||||
|         returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash), | ||||
|         entityClassEncoded = encodeURI(entityClass), | ||||
|         url = `/fr/doc/gen/generate/from/${templateId}/for/${entityClassEncoded}/${entityIdEncoded}?returnPath=${returnPath}` | ||||
|     ; | ||||
|     console.log('computed Url'); | ||||
|     return url; | ||||
| }; | ||||
|  | ||||
| export {buildLink}; | ||||
| @@ -20,8 +20,8 @@ | ||||
|                                     <option v-bind:value="t.id">{{ t.name.fr || 'Aucun nom défini' }}</option> | ||||
|                                 </template> | ||||
|                             </select> | ||||
|                             <a v-if="canGenerate" class="btn btn-update btn-sm change-icon" :href="buildUrlGenerate" @click.prevent="clickGenerate($event, buildUrlGenerate)"><i class="fa fa-fw fa-cog"></i></a> | ||||
|                             <a v-else class="btn btn-update btn-sm change-icon" href="#" disabled ><i class="fa fa-fw fa-cog"></i></a> | ||||
|                             <button v-if="canGenerate" class="btn btn-update btn-sm change-icon" type="button" @click="generateDocument"><i class="fa fa-fw fa-cog"></i></button> | ||||
|                             <button v-else class="btn btn-update btn-sm change-icon" type="button" disabled ><i class="fa fa-fw fa-cog"></i></button> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
| @@ -39,27 +39,24 @@ | ||||
|  | ||||
| <script> | ||||
|  | ||||
| import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator'; | ||||
|  | ||||
| export default { | ||||
|     name: "PickTemplate", | ||||
|     props: { | ||||
|         entityId: [String, Number], | ||||
|         entityClass: { | ||||
|             type: String, | ||||
|             required: false, | ||||
|             required: true, | ||||
|         }, | ||||
|         templates: { | ||||
|             type: Array, | ||||
|             required: true, | ||||
|         }, | ||||
|         preventDefaultMoveToGenerate: { | ||||
|             type: Boolean, | ||||
|         // beforeMove execute "something" before | ||||
|         beforeMove: { | ||||
|             type: Function, | ||||
|             required: false, | ||||
|             default: false, | ||||
|         } | ||||
|     }, | ||||
|     emits: ['goToGenerateDocument'], | ||||
|     data() { | ||||
|         return { | ||||
|             template: null, | ||||
| @@ -77,37 +74,66 @@ export default { | ||||
|             return true; | ||||
|         }, | ||||
|         getDescription() { | ||||
|             if (null === this.template) { | ||||
|                 return ''; | ||||
|             } | ||||
|             let desc = this.templates.find(t => t.id === this.template); | ||||
|             if (null === desc) { | ||||
|                 return ''; | ||||
|             } | ||||
|             return desc.description || ''; | ||||
|         }, | ||||
|         buildUrlGenerate() { | ||||
|             if (null === this.template) { | ||||
|                 return '#'; | ||||
|             } | ||||
|  | ||||
|             return buildLink(this.template, this.entityId, this.entityClass); | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         clickGenerate(event, link) { | ||||
|             if (!this.preventDefaultMoveToGenerate) { | ||||
|                 window.location.assign(link); | ||||
|         generateDocument() { | ||||
|             console.log('generateDocument'); | ||||
|             console.log('beforeMove', this.beforeMove); | ||||
|             if (this.beforeMove != null) { | ||||
|                 console.log('execute before move'); | ||||
|                 let r = this.beforeMove(); | ||||
|                 if (r instanceof Promise) { | ||||
|                     r.then((obj) => this.goToGenerate(obj)); | ||||
|                 } else { | ||||
|                     this.goToGenerate(); | ||||
|                 } | ||||
|             } else { | ||||
|                 this.goToGenerate(); | ||||
|             } | ||||
|         }, | ||||
|         goToGenerate(obj) { | ||||
|             console.log('goToGenerate'); | ||||
|             console.log('obj', obj); | ||||
|             console.log('entityId', this.entityId); | ||||
|             let | ||||
|                 realId = this.entityId, | ||||
|                 realEntityClass = this.entityClass | ||||
|             ; | ||||
|  | ||||
|             if (obj !== undefined) { | ||||
|                 if (obj.entityId !== undefined) { | ||||
|                     realId = obj.entityId; | ||||
|                 } | ||||
|                 if (obj.entityClass !== undefined) { | ||||
|                     realEntityClass = obj.entityClass; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             this.$emit('goToGenerateDocument', {event, link, template: this.template}); | ||||
|             console.log('realId', realId); | ||||
|             console.log('realEntityClass', realEntityClass); | ||||
|  | ||||
|             const | ||||
|                 entityId = encodeURI(realId), | ||||
|                 returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash), | ||||
|                 fqdnEntityClass = encodeURI(realEntityClass), | ||||
|                 url = `/fr/doc/gen/generate/from/${this.template}/for/${fqdnEntityClass}/${entityId}?returnPath=${returnPath}` | ||||
|             ; | ||||
|  | ||||
|             console.log('I will generate your doc at', url); | ||||
|             window.location.assign(url); | ||||
|         }, | ||||
|     }, | ||||
|     i18n: { | ||||
|         messages: { | ||||
|             fr: { | ||||
|                 generate_document: 'Générer un document', | ||||
|                 select_a_template: 'Choisir un modèle', | ||||
|                 select_a_template: 'Choisir un gabarit', | ||||
|                 choose_a_template: 'Choisir', | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -13,9 +13,7 @@ namespace Chill\DocStoreBundle\Controller; | ||||
|  | ||||
| use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument; | ||||
| use Chill\DocStoreBundle\Form\AccompanyingCourseDocumentType; | ||||
| use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository; | ||||
| use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter; | ||||
| use Chill\MainBundle\Pagination\PaginatorFactory; | ||||
| use Chill\MainBundle\Security\Authorization\AuthorizationHelper; | ||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod; | ||||
| use DateTime; | ||||
| @@ -31,15 +29,20 @@ use Symfony\Contracts\Translation\TranslatorInterface; | ||||
|  */ | ||||
| class DocumentAccompanyingCourseController extends AbstractController | ||||
| { | ||||
|     protected AuthorizationHelper $authorizationHelper; | ||||
|     /** | ||||
|      * @var AuthorizationHelper | ||||
|      */ | ||||
|     protected $authorizationHelper; | ||||
|  | ||||
|     protected EventDispatcherInterface $eventDispatcher; | ||||
|     /** | ||||
|      * @var EventDispatcherInterface | ||||
|      */ | ||||
|     protected $eventDispatcher; | ||||
|  | ||||
|     protected TranslatorInterface $translator; | ||||
|  | ||||
|     private AccompanyingCourseDocumentRepository $courseRepository; | ||||
|  | ||||
|     private PaginatorFactory $paginatorFactory; | ||||
|     /** | ||||
|      * @var TranslatorInterface | ||||
|      */ | ||||
|     protected $translator; | ||||
|  | ||||
|     /** | ||||
|      * DocumentAccompanyingCourseController constructor. | ||||
| @@ -47,15 +50,11 @@ class DocumentAccompanyingCourseController extends AbstractController | ||||
|     public function __construct( | ||||
|         TranslatorInterface $translator, | ||||
|         EventDispatcherInterface $eventDispatcher, | ||||
|         AuthorizationHelper $authorizationHelper, | ||||
|         PaginatorFactory $paginatorFactory, | ||||
|         AccompanyingCourseDocumentRepository $courseRepository | ||||
|         AuthorizationHelper $authorizationHelper | ||||
|     ) { | ||||
|         $this->translator = $translator; | ||||
|         $this->eventDispatcher = $eventDispatcher; | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->paginatorFactory = $paginatorFactory; | ||||
|         $this->courseRepository = $courseRepository; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -131,15 +130,11 @@ class DocumentAccompanyingCourseController extends AbstractController | ||||
|  | ||||
|         $this->denyAccessUnlessGranted(AccompanyingCourseDocumentVoter::SEE, $course); | ||||
|  | ||||
|         $total = $this->courseRepository->countByCourse($course); | ||||
|         $pagination = $this->paginatorFactory->create($total); | ||||
|  | ||||
|         $documents = $this->courseRepository | ||||
|         $documents = $em | ||||
|             ->getRepository('ChillDocStoreBundle:AccompanyingCourseDocument') | ||||
|             ->findBy( | ||||
|                 ['course' => $course], | ||||
|                 ['date' => 'DESC'], | ||||
|                 $pagination->getItemsPerPage(), | ||||
|                 $pagination->getCurrentPageFirstItemNumber() | ||||
|                 ['date' => 'DESC'] | ||||
|             ); | ||||
|  | ||||
|         return $this->render( | ||||
| @@ -147,7 +142,6 @@ class DocumentAccompanyingCourseController extends AbstractController | ||||
|             [ | ||||
|                 'documents' => $documents, | ||||
|                 'accompanyingCourse' => $course, | ||||
|                 'pagination' => $pagination, | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
|   | ||||
| @@ -13,8 +13,7 @@ namespace Chill\DocStoreBundle\Controller; | ||||
|  | ||||
| use Chill\DocStoreBundle\Entity\PersonDocument; | ||||
| use Chill\DocStoreBundle\Form\PersonDocumentType; | ||||
| use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface; | ||||
| use Chill\MainBundle\Pagination\PaginatorFactory; | ||||
| use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter; | ||||
| use Chill\MainBundle\Security\Authorization\AuthorizationHelper; | ||||
| use Chill\PersonBundle\Entity\Person; | ||||
| use Chill\PersonBundle\Privacy\PrivacyEvent; | ||||
| @@ -25,7 +24,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| use Symfony\Component\HttpFoundation\Response; | ||||
| use Symfony\Component\Routing\Annotation\Route; | ||||
| use Symfony\Contracts\Translation\TranslatorInterface; | ||||
| use Symfony\Component\Translation\TranslatorInterface; | ||||
|  | ||||
| /** | ||||
|  * Class DocumentPersonController. | ||||
| @@ -36,15 +35,20 @@ use Symfony\Contracts\Translation\TranslatorInterface; | ||||
|  */ | ||||
| class DocumentPersonController extends AbstractController | ||||
| { | ||||
|     protected AuthorizationHelper $authorizationHelper; | ||||
|     /** | ||||
|      * @var AuthorizationHelper | ||||
|      */ | ||||
|     protected $authorizationHelper; | ||||
|  | ||||
|     protected EventDispatcherInterface $eventDispatcher; | ||||
|     /** | ||||
|      * @var EventDispatcherInterface | ||||
|      */ | ||||
|     protected $eventDispatcher; | ||||
|  | ||||
|     protected TranslatorInterface $translator; | ||||
|  | ||||
|     private PaginatorFactory $paginatorFactory; | ||||
|  | ||||
|     private PersonDocumentACLAwareRepositoryInterface $personDocumentACLAwareRepository; | ||||
|     /** | ||||
|      * @var TranslatorInterface | ||||
|      */ | ||||
|     protected $translator; | ||||
|  | ||||
|     /** | ||||
|      * DocumentPersonController constructor. | ||||
| @@ -52,15 +56,11 @@ class DocumentPersonController extends AbstractController | ||||
|     public function __construct( | ||||
|         TranslatorInterface $translator, | ||||
|         EventDispatcherInterface $eventDispatcher, | ||||
|         AuthorizationHelper $authorizationHelper, | ||||
|         PaginatorFactory $paginatorFactory, | ||||
|         PersonDocumentACLAwareRepositoryInterface $personDocumentACLAwareRepository | ||||
|         AuthorizationHelper $authorizationHelper | ||||
|     ) { | ||||
|         $this->translator = $translator; | ||||
|         $this->eventDispatcher = $eventDispatcher; | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->paginatorFactory = $paginatorFactory; | ||||
|         $this->personDocumentACLAwareRepository = $personDocumentACLAwareRepository; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -156,15 +156,19 @@ class DocumentPersonController extends AbstractController | ||||
|  | ||||
|         $this->denyAccessUnlessGranted(PersonVoter::SEE, $person); | ||||
|  | ||||
|         $total = $this->personDocumentACLAwareRepository->countByPerson($person); | ||||
|         $pagination = $this->paginatorFactory->create($total); | ||||
|         $reachableScopes = $this->authorizationHelper | ||||
|             ->getReachableScopes( | ||||
|                 $this->getUser(), | ||||
|                 PersonDocumentVoter::SEE, | ||||
|                 $person->getCenter() | ||||
|             ); | ||||
|  | ||||
|         $documents = $this->personDocumentACLAwareRepository->findByPerson( | ||||
|             $person, | ||||
|             [], | ||||
|             $pagination->getItemsPerPage(), | ||||
|             $pagination->getCurrentPageFirstItemNumber() | ||||
|         ); | ||||
|         $documents = $em | ||||
|             ->getRepository('ChillDocStoreBundle:PersonDocument') | ||||
|             ->findBy( | ||||
|                 ['person' => $person, 'scope' => $reachableScopes], | ||||
|                 ['date' => 'DESC'] | ||||
|             ); | ||||
|  | ||||
|         $event = new PrivacyEvent($person, [ | ||||
|             'element_class' => PersonDocument::class, | ||||
| @@ -177,7 +181,6 @@ class DocumentPersonController extends AbstractController | ||||
|             [ | ||||
|                 'documents' => $documents, | ||||
|                 'person' => $person, | ||||
|                 'pagination' => $pagination, | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
|   | ||||
| @@ -38,7 +38,6 @@ class ChillDocStoreExtension extends Extension implements PrependExtensionInterf | ||||
|         $loader->load('services/menu.yaml'); | ||||
|         $loader->load('services/fixtures.yaml'); | ||||
|         $loader->load('services/form.yaml'); | ||||
|         $loader->load('services/templating.yaml'); | ||||
|     } | ||||
|  | ||||
|     public function prepend(ContainerBuilder $container) | ||||
|   | ||||
| @@ -11,13 +11,7 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Entity; | ||||
|  | ||||
| use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate; | ||||
| use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; | ||||
| use Chill\MainBundle\Doctrine\Model\TrackCreationTrait; | ||||
| use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; | ||||
| use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait; | ||||
| use Chill\MainBundle\Entity\HasScopeInterface; | ||||
| use Chill\MainBundle\Entity\Scope; | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use DateTimeInterface; | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
| @@ -26,12 +20,8 @@ use Symfony\Component\Validator\Constraints as Assert; | ||||
| /** | ||||
|  * @ORM\MappedSuperclass | ||||
|  */ | ||||
| class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdateInterface | ||||
| class Document implements HasScopeInterface | ||||
| { | ||||
|     use TrackCreationTrait; | ||||
|  | ||||
|     use TrackUpdateTrait; | ||||
|  | ||||
|     /** | ||||
|      * @ORM\ManyToOne(targetEntity="Chill\DocStoreBundle\Entity\DocumentCategory") | ||||
|      * @ORM\JoinColumns({ | ||||
| @@ -77,11 +67,6 @@ class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdate | ||||
|      */ | ||||
|     private $scope; | ||||
|  | ||||
|     /** | ||||
|      * @ORM\ManyToOne(targetEntity="Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate") | ||||
|      */ | ||||
|     private $template; | ||||
|  | ||||
|     /** | ||||
|      * @ORM\Column(type="text") | ||||
|      * @Assert\Length( | ||||
| @@ -97,6 +82,9 @@ class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdate | ||||
|      */ | ||||
|     private $user; | ||||
|  | ||||
|     /** | ||||
|      * @return DocumentCategory | ||||
|      */ | ||||
|     public function getCategory(): ?DocumentCategory | ||||
|     { | ||||
|         return $this->category; | ||||
| @@ -127,16 +115,11 @@ class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdate | ||||
|      * | ||||
|      * @return \Chill\MainBundle\Entity\Scope | ||||
|      */ | ||||
|     public function getScope(): ?Scope | ||||
|     public function getScope() | ||||
|     { | ||||
|         return $this->scope; | ||||
|     } | ||||
|  | ||||
|     public function getTemplate(): ?DocGeneratorTemplate | ||||
|     { | ||||
|         return $this->template; | ||||
|     } | ||||
|  | ||||
|     public function getTitle(): ?string | ||||
|     { | ||||
|         return $this->title; | ||||
| @@ -182,13 +165,6 @@ class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdate | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function setTemplate(?DocGeneratorTemplate $template): self | ||||
|     { | ||||
|         $this->template = $template; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function setTitle(string $title): self | ||||
|     { | ||||
|         $this->title = $title; | ||||
|   | ||||
| @@ -18,12 +18,10 @@ use Chill\MainBundle\Form\Type\ChillDateType; | ||||
| use Chill\MainBundle\Form\Type\ChillTextareaType; | ||||
| use Chill\MainBundle\Form\Type\ScopePickerType; | ||||
| use Chill\MainBundle\Security\Authorization\AuthorizationHelper; | ||||
| use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelper; | ||||
| use Doctrine\ORM\EntityRepository; | ||||
| use Doctrine\Persistence\ObjectManager; | ||||
| use Symfony\Bridge\Doctrine\Form\Type\EntityType; | ||||
| use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; | ||||
| use Symfony\Component\Form\AbstractType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\TextType; | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
| @@ -53,25 +51,14 @@ class PersonDocumentType extends AbstractType | ||||
|      */ | ||||
|     protected $user; | ||||
|  | ||||
|     private ParameterBagInterface $parameterBag; | ||||
|  | ||||
|     private ScopeResolverDispatcher $scopeResolverDispatcher; | ||||
|  | ||||
|     public function __construct( | ||||
|         TranslatableStringHelper $translatableStringHelper, | ||||
|         ScopeResolverDispatcher $scopeResolverDispatcher, | ||||
|         ParameterBagInterface $parameterBag | ||||
|         TranslatableStringHelper $translatableStringHelper | ||||
|     ) { | ||||
|         $this->translatableStringHelper = $translatableStringHelper; | ||||
|         $this->scopeResolverDispatcher = $scopeResolverDispatcher; | ||||
|         $this->parameterBag = $parameterBag; | ||||
|     } | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options) | ||||
|     { | ||||
|         $document = $options['data']; | ||||
|         $isScopeConcerned = $this->scopeResolverDispatcher->isConcerned($document); | ||||
|  | ||||
|         $builder | ||||
|             ->add('title', TextType::class) | ||||
|             ->add('description', ChillTextareaType::class, [ | ||||
| @@ -80,6 +67,10 @@ class PersonDocumentType extends AbstractType | ||||
|             ->add('object', StoredObjectType::class, [ | ||||
|                 'error_bubbling' => true, | ||||
|             ]) | ||||
|             ->add('scope', ScopePickerType::class, [ | ||||
|                 'center' => $options['center'], | ||||
|                 'role' => $options['role'], | ||||
|             ]) | ||||
|             ->add('date', ChillDateType::class) | ||||
|             ->add('category', EntityType::class, [ | ||||
|                 'placeholder' => 'Choose a document category', | ||||
| @@ -93,13 +84,6 @@ class PersonDocumentType extends AbstractType | ||||
|                     return $entity ? $this->translatableStringHelper->localize($entity->getName()) : ''; | ||||
|                 }, | ||||
|             ]); | ||||
|  | ||||
|         if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) { | ||||
|             $builder->add('scope', ScopePickerType::class, [ | ||||
|                 'center' => $options['center'], | ||||
|                 'role' => $options['role'], | ||||
|             ]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function configureOptions(OptionsResolver $resolver) | ||||
|   | ||||
| @@ -1,75 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Repository; | ||||
|  | ||||
| use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument; | ||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Doctrine\ORM\EntityRepository; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Doctrine\Persistence\ObjectRepository; | ||||
|  | ||||
| class AccompanyingCourseDocumentRepository implements ObjectRepository | ||||
| { | ||||
|     private EntityManagerInterface $em; | ||||
|  | ||||
|     private EntityRepository $repository; | ||||
|  | ||||
|     public function __construct(EntityManagerInterface $em) | ||||
|     { | ||||
|         $this->em = $em; | ||||
|         $this->repository = $em->getRepository(AccompanyingCourseDocument::class); | ||||
|     } | ||||
|  | ||||
|     public function buildQueryByCourse(AccompanyingPeriod $course): QueryBuilder | ||||
|     { | ||||
|         $qb = $this->repository->createQueryBuilder('d'); | ||||
|  | ||||
|         $qb | ||||
|             ->where($qb->expr()->eq('d.course', ':course')) | ||||
|             ->setParameter('course', $course); | ||||
|  | ||||
|         return $qb; | ||||
|     } | ||||
|  | ||||
|     public function countByCourse(AccompanyingPeriod $course): int | ||||
|     { | ||||
|         $qb = $this->buildQueryByCourse($course)->select('COUNT(d)'); | ||||
|  | ||||
|         return $qb->getQuery()->getSingleScalarResult(); | ||||
|     } | ||||
|  | ||||
|     public function find($id): ?AccompanyingCourseDocument | ||||
|     { | ||||
|         return $this->repository->find($id); | ||||
|     } | ||||
|  | ||||
|     public function findAll(): array | ||||
|     { | ||||
|         return $this->repository->findAll(); | ||||
|     } | ||||
|  | ||||
|     public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) | ||||
|     { | ||||
|         return $this->repository->findBy($criteria, $orderBy, $limit, $offset); | ||||
|     } | ||||
|  | ||||
|     public function findOneBy(array $criteria): ?AccompanyingCourseDocument | ||||
|     { | ||||
|         return $this->findOneBy($criteria); | ||||
|     } | ||||
|  | ||||
|     public function getClassName() | ||||
|     { | ||||
|         return AccompanyingCourseDocument::class; | ||||
|     } | ||||
| } | ||||
| @@ -1,90 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Repository; | ||||
|  | ||||
| use Chill\DocStoreBundle\Entity\PersonDocument; | ||||
| use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter; | ||||
| use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; | ||||
| use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; | ||||
| use Chill\PersonBundle\Entity\Person; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Symfony\Component\Security\Core\Security; | ||||
|  | ||||
| class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareRepositoryInterface | ||||
| { | ||||
|     private AuthorizationHelperInterface $authorizationHelper; | ||||
|  | ||||
|     private CenterResolverDispatcher $centerResolverDispatcher; | ||||
|  | ||||
|     private EntityManagerInterface $em; | ||||
|  | ||||
|     private Security $security; | ||||
|  | ||||
|     public function __construct(EntityManagerInterface $em, AuthorizationHelperInterface $authorizationHelper, CenterResolverDispatcher $centerResolverDispatcher, Security $security) | ||||
|     { | ||||
|         $this->em = $em; | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->centerResolverDispatcher = $centerResolverDispatcher; | ||||
|         $this->security = $security; | ||||
|     } | ||||
|  | ||||
|     public function buildQueryByPerson(Person $person): QueryBuilder | ||||
|     { | ||||
|         $qb = $this->em->getRepository(PersonDocument::class)->createQueryBuilder('d'); | ||||
|  | ||||
|         $qb | ||||
|             ->where($qb->expr()->eq('d.person', ':person')) | ||||
|             ->setParameter('person', $person); | ||||
|  | ||||
|         return $qb; | ||||
|     } | ||||
|  | ||||
|     public function countByPerson(Person $person): int | ||||
|     { | ||||
|         $qb = $this->buildQueryByPerson($person)->select('COUNT(d)'); | ||||
|  | ||||
|         $this->addACL($qb, $person); | ||||
|  | ||||
|         return $qb->getQuery()->getSingleScalarResult(); | ||||
|     } | ||||
|  | ||||
|     public function findByPerson(Person $person, array $orderBy = [], int $limit = 20, int $offset = 0): array | ||||
|     { | ||||
|         $qb = $this->buildQueryByPerson($person)->select('d'); | ||||
|  | ||||
|         $this->addACL($qb, $person); | ||||
|  | ||||
|         foreach ($orderBy as [$field, $order]) { | ||||
|             $qb->addOrderBy($field, $order); | ||||
|         } | ||||
|  | ||||
|         $qb->setFirstResult($offset)->setMaxResults($limit); | ||||
|  | ||||
|         return $qb->getQuery()->getResult(); | ||||
|     } | ||||
|  | ||||
|     private function addACL(QueryBuilder $qb, Person $person): void | ||||
|     { | ||||
|         $center = $this->centerResolverDispatcher->resolveCenter($person); | ||||
|  | ||||
|         $reachableScopes = $this->authorizationHelper | ||||
|             ->getReachableScopes( | ||||
|                 $this->security->getUser(), | ||||
|                 PersonDocumentVoter::SEE, | ||||
|                 $center | ||||
|             ); | ||||
|  | ||||
|         $qb->andWhere($qb->expr()->in('d.scope', ':scopes')) | ||||
|             ->setParameter('scopes', $reachableScopes); | ||||
|     } | ||||
| } | ||||
| @@ -1,21 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Repository; | ||||
|  | ||||
| use Chill\PersonBundle\Entity\Person; | ||||
|  | ||||
| interface PersonDocumentACLAwareRepositoryInterface | ||||
| { | ||||
|     public function countByPerson(Person $person): int; | ||||
|  | ||||
|     public function findByPerson(Person $person, array $orderBy = [], int $limit = 20, int $offset = 0): array; | ||||
| } | ||||
| @@ -1,5 +1,4 @@ | ||||
| {% import "@ChillDocStore/Macro/macro.html.twig" as m %} | ||||
| {% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %} | ||||
|  | ||||
| <div class="flex-table accompanying_course_work-list"> | ||||
|     <div class="item-bloc document-item bg-chill-llight-gray"> | ||||
| @@ -9,15 +8,14 @@ | ||||
|             </div> | ||||
|             <div class="col-8"> | ||||
|                 <h3>{{ document.title }}</h3> | ||||
|  | ||||
|                 {{ mm.mimeIcon(document.object.type) }} | ||||
|  | ||||
|                 <small>{{ document.object.type }}</small> | ||||
|              | ||||
|                 {% if document.description is not empty %} | ||||
|                     <blockquote class="chill-user-quote mt-4"> | ||||
|                     <blockquote class="chill-user-quote mt-2"> | ||||
|                         {{ document.description }} | ||||
|                     </blockquote> | ||||
|                 {% endif %} | ||||
|  | ||||
|          | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| @@ -34,31 +32,30 @@ | ||||
|  | ||||
| {% if display_action is defined and display_action == true %} | ||||
|     <ul class="record_actions"> | ||||
|         {% if document.course != null and is_granted('CHILL_PERSON_ACCOMPANYING_PERIOD_SEE', document.course) %} | ||||
|             <li> | ||||
|                 <a href="{{ path('chill_person_accompanying_course_index', {'accompanying_period_id': document.course.id}) }}" class="btn btn-show change-icon"> | ||||
|                     <i class="fa fa-random"></i> {{ 'Course number'|trans }} {{ document.course.id }} | ||||
|                 </a> | ||||
|             </li> | ||||
|         {% endif %} | ||||
|         <li> | ||||
|             {{ m.download_button(document.object, document.title) }} | ||||
|         </li> | ||||
|         <li> | ||||
|         {% if chill_document_is_editable(document.object) %} | ||||
|             {% if not freezed %} | ||||
|                 {{ document.object|chill_document_edit_button({'title': document.title|e('html') }) }} | ||||
|             {% else %} | ||||
|                 <a class="btn btn-wopilink disabled" href="#" title="{{ 'workflow.freezed document'|trans }}"> | ||||
|                     {{ 'Update document'|trans }} | ||||
|                 </a> | ||||
|             {% endif %} | ||||
|         {% endif %} | ||||
|         {% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE', document) and document.course != null %} | ||||
|             <li> | ||||
|                 <a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': document.course.id, 'id': document.id}) }}" class="btn btn-show"></a> | ||||
|             </li> | ||||
|         {% if not freezed %} | ||||
|             {% set button = { | ||||
|                 'changeIcon': 'fa-unlock', | ||||
|             } %}{# | ||||
|                 'changeClass'   string | ||||
|                 'noText'        boolean | ||||
|             #} | ||||
|             {# vue component #} | ||||
|             <span | ||||
|                 data-module="wopi-link" | ||||
|                 data-wopi-url="{{ path('chill_wopi_file_edit', {'fileId': document.object.uuid}) }}" | ||||
|                 data-doc-title="{{ document.title|e('html_attr') }}" | ||||
|                 data-doc-type="{{ document.object.type|e('html_attr') }}" | ||||
|                 data-button="{{ button|json_encode }}" | ||||
|             ></span> | ||||
|         {% else %} | ||||
|             <a class="btn btn-update change-icon disabled" href="#" title="{{ 'workflow.freezed document'|trans }}"> | ||||
|                 <i class="fa fa-lock me-2"></i>{{ 'Update document'|trans }} | ||||
|             </a> | ||||
|         {% endif %} | ||||
|         </li> | ||||
|     </ul> | ||||
| {% endif %} | ||||
| {% endif %} | ||||
| @@ -19,7 +19,7 @@ | ||||
| 	{{ form_row(form.description) }} | ||||
| 	{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }} | ||||
|  | ||||
| 	<ul class="record_actions sticky-form-buttons"> | ||||
| 	<ul class="record_actions"> | ||||
| 		<li class="cancel"> | ||||
| 			<a href="{{ path('accompanying_course_document_index', {'course': accompanyingCourse.id}) }}" class="btn btn-cancel"> | ||||
| 				{{ 'Back to the list' | trans }} | ||||
|   | ||||
| @@ -2,6 +2,8 @@ | ||||
|  | ||||
| {% set activeRouteKey = '' %} | ||||
|  | ||||
| {% import "@ChillDocStore/Macro/macro.html.twig" as m %} | ||||
|  | ||||
| {% block title %} | ||||
| 	{{ 'Documents' }} | ||||
| {% endblock %} | ||||
| @@ -9,35 +11,64 @@ | ||||
| {% block js %} | ||||
| 	{{ parent() }} | ||||
| 	{{ encore_entry_script_tags('mod_async_upload') }} | ||||
|     {{ encore_entry_script_tags('mod_docgen_picktemplate') }} | ||||
|   {{ encore_entry_script_tags('mod_docgen_picktemplate') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block css %} | ||||
| 	{{ parent() }} | ||||
| 	{{ encore_entry_link_tags('mod_async_upload') }} | ||||
|     {{ encore_entry_link_tags('mod_docgen_picktemplate') }} | ||||
|   {{ encore_entry_link_tags('mod_docgen_picktemplate') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| <div class="col-md-10 col-xxl"> | ||||
| 	<h1>{{ 'Documents' }}</h1> | ||||
|  | ||||
| 	{% if documents|length == 0 %} | ||||
|             <p class="chill-no-data-statement">{{ 'No documents'|trans }}</p> | ||||
|     {% else %} | ||||
| 		<div class="flex-table chill-task-list"> | ||||
| 	<table class="table table-bordered border-dark table-striped"> | ||||
|  | ||||
| 		<thead> | ||||
| 			<tr> | ||||
| 				<th>{{ 'Title' | trans }}</th> | ||||
| 				<th>{{ 'Category'|trans }}</th> | ||||
| 				<th>{{ 'Actions' | trans }}</th> | ||||
| 			</tr> | ||||
| 		</thead> | ||||
| 		<tbody> | ||||
| 			{% for document in documents %} | ||||
| 				{% include 'ChillDocStoreBundle:List:list_item.html.twig' %} | ||||
| 				<tr> | ||||
| 					<td>{{ document.title }}</td> | ||||
| 					<td>{% if document.category %}{{ document.category.name|localize_translatable_string }}{% endif %}</td> | ||||
| 					<td> | ||||
| 						<ul class="record_actions"> | ||||
| 							{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS', document) %} | ||||
| 								<li> | ||||
| 									{{ m.download_button(document.object, document.title) }} | ||||
| 								</li> | ||||
| 								<li> | ||||
| 									<a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-show"></a> | ||||
| 								</li> | ||||
| 							{% endif %} | ||||
| 							{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %} | ||||
| 								<li> | ||||
| 									<a href="{{ path('accompanying_course_document_edit', {'course': accompanyingCourse.id, 'id': document.id }) }}" class="btn btn-update"></a> | ||||
| 								</li> | ||||
| 							{% endif %} | ||||
| 						</ul> | ||||
| 					</td> | ||||
| 				</tr> | ||||
| 			{% else %} | ||||
| 				<tr> | ||||
| 					<td colspan="9" style="text-align:center;"> | ||||
| 						<span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span> | ||||
| 					</td> | ||||
| 				</tr> | ||||
| 			{% endfor %} | ||||
| 		</div> | ||||
|     {% endif %} | ||||
| 		</tbody> | ||||
| 	</table> | ||||
|  | ||||
| 	{{ chill_pagination(pagination) }} | ||||
|  | ||||
| 	<div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\PersonBundle\Entity\AccompanyingPeriod" data-entity-id="{{ accompanyingCourse.id }}"></div> | ||||
|     <div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\PersonBundle\Entity\AccompanyingPeriod" data-entity-id="{{ accompanyingCourse.id }}"></div> | ||||
|  | ||||
| 	{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_CREATE', accompanyingCourse) %} | ||||
| 		<ul class="record_actions sticky-form-buttons"> | ||||
| 		<ul class="record_actions"> | ||||
| 			<li class="create"> | ||||
| 				<a href="{{ path('accompanying_course_document_new', {'course': accompanyingCourse.id}) }}" class="btn btn-create"> | ||||
| 					{{ 'Create'|trans }} | ||||
| @@ -45,6 +76,4 @@ | ||||
| 			</li> | ||||
| 		</ul> | ||||
| 	{% endif %} | ||||
|  | ||||
| </div> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| {% set activeRouteKey = '' %} | ||||
|  | ||||
| {% import "@ChillDocStore/Macro/macro.html.twig" as m %} | ||||
| {% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %} | ||||
|  | ||||
| {% block title %} | ||||
| 	{# {{ 'Detail of document of %name%'|trans({ '%name%': accompanyingCourse|chill_entity_render_string } ) }} #} | ||||
| @@ -19,18 +18,16 @@ | ||||
| {% block content %} | ||||
| <div class="document-show"> | ||||
|     <h1>{{ block('title') }}</h1> | ||||
|  | ||||
|     {{ mm.mimeIcon(document.object.type) }} | ||||
|  | ||||
|     <dl class="chill_view_data mt-4"> | ||||
|      | ||||
|     <dl class="chill_view_data"> | ||||
|         <dt>{{ 'Title'|trans }}</dt> | ||||
|         <dd>{{ document.title }}</dd> | ||||
|  | ||||
|          | ||||
|         {% if document.category is not null %} | ||||
|             <dt>{{ 'Category'|trans }}</dt> | ||||
|             <dd>{{ document.category.name|localize_translatable_string }}</dd> | ||||
|         {% endif %} | ||||
|  | ||||
|          | ||||
|         <dt>{{ 'Description' | trans }}</dt> | ||||
|         <dd> | ||||
|             {% if document.description is empty %} | ||||
| @@ -42,7 +39,7 @@ | ||||
|             {% endif %} | ||||
|         </dd> | ||||
|     </dl> | ||||
|  | ||||
|      | ||||
|     <ul class="record_actions sticky-form-buttons"> | ||||
|         <li class="cancel"> | ||||
|             <a href="{{ path('accompanying_course_document_index', {'course': accompanyingCourse.id}) }}" class="btn btn-cancel"> | ||||
| @@ -52,15 +49,11 @@ | ||||
|         <li> | ||||
|             {{ m.download_button(document.object, document.title) }} | ||||
|         </li> | ||||
|         {% if chill_document_is_editable(document.object) %} | ||||
|             <li> | ||||
|                 {{ document.object|chill_document_edit_button }} | ||||
|             </li> | ||||
|         {% endif %} | ||||
|         {% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %} | ||||
|             <li> | ||||
|                 <a href="{{ path('accompanying_course_document_edit', {'id': document.id, 'course': accompanyingCourse.id}) }}" | ||||
|                    class="btn btn-edit" title="{{ 'Edit attributes' | trans }}"></a> | ||||
|                 <a href="{{ path('accompanying_course_document_edit', {'id': document.id, 'course': accompanyingCourse.id}) }}" class="btn btn-edit"> | ||||
|                     {{ 'Edit' | trans }} | ||||
|                 </a> | ||||
|             </li> | ||||
|         {% endif %} | ||||
|         {% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %} | ||||
| @@ -72,7 +65,7 @@ | ||||
|     </ul> | ||||
| </div> | ||||
| {% endblock %} | ||||
|  | ||||
|   | ||||
| {% block block_post_menu %} | ||||
|     <div class="post-menu pt-4"></div> | ||||
| {% endblock %} | ||||
| @@ -81,4 +74,4 @@ | ||||
|     {{ parent() }} | ||||
|     {{ encore_entry_script_tags('mod_async_upload') }} | ||||
|     {{ encore_entry_script_tags('mod_entity_workflow_pick') }} | ||||
| {% endblock %} | ||||
| {% endblock %} | ||||
| @@ -1,9 +0,0 @@ | ||||
|  | ||||
| {# Twig way | ||||
|     TODO: une route, un template avec un header CHILL et un iframe | ||||
| #} | ||||
| <a href="{{ chill_path_add_return_path('chill_wopi_file_edit', {'fileId': document.uuid}) }}" | ||||
|     class="btn btn-wopilink"> | ||||
|     {{ 'online_edit_document'|trans }} | ||||
| </a> | ||||
|  | ||||
| @@ -1,83 +0,0 @@ | ||||
| {% import "@ChillDocStore/Macro/macro.html.twig" as m %} | ||||
| {% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %} | ||||
| <div class="item-bloc"> | ||||
|     <div class="item-row"> | ||||
|         <div class="item-col"> | ||||
|             <div class="denomination h2"> | ||||
|                 {{ document.title }} | ||||
|             </div> | ||||
|             <div> | ||||
|                 {{ mm.mimeIcon(document.object.type) }} | ||||
|             </div> | ||||
|             <div> | ||||
|                 <p>{{ document.category.name|localize_translatable_string }}</p> | ||||
|             </div> | ||||
|             {% if document.template is not null %} | ||||
|                 <div> | ||||
|                     <p>{{ document.template.name.fr }}</p> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|  | ||||
|         <div class="item-col"> | ||||
|             <div class="container"> | ||||
|                 {% if document.date is not null %} | ||||
|                     <div class="dates row" style="float: right;"> | ||||
|                         <span>{{ document.createdAt|format_date('short') }}</span> | ||||
|                     </div> | ||||
|                 {% endif %} | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     {% if document.description is not empty %} | ||||
|         <div class="item-row separator"> | ||||
|             <blockquote class="chill-user-quote"> | ||||
|                 {{ document.description|chill_markdown_to_html }} | ||||
|             </blockquote> | ||||
|         </div> | ||||
|     {% endif %} | ||||
|     <div class="item-row separator"> | ||||
|         <div class="item-col item-meta"> | ||||
|             <div class="updatedBy"> | ||||
|                 {{ 'Created by'|trans }}: | ||||
|                 <span class="user">{{ document.createdBy|chill_entity_render_string }}</span> | ||||
|                 <span class="date">le {{ document.createdAt|format_date('long') }}</span> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div> | ||||
|     {% if document.course is defined %} | ||||
|         <ul class="record_actions"> | ||||
|             {% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS', document) %} | ||||
|                 <li> | ||||
|                     {{ m.download_button(document.object, document.title) }} | ||||
|                 </li> | ||||
|                 <li> | ||||
|                     <a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-show"></a> | ||||
|                 </li> | ||||
|             {% endif %} | ||||
|             {% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %} | ||||
|                 <li> | ||||
|                     <a href="{{ path('accompanying_course_document_edit', {'course': accompanyingCourse.id, 'id': document.id }) }}" class="btn btn-update"></a> | ||||
|                 </li> | ||||
|             {% endif %} | ||||
|         </ul> | ||||
|     {% else %} | ||||
|         <ul class="record_actions"> | ||||
|             {% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %} | ||||
|                 <li> | ||||
|                     {{ m.download_button(document.object, document.title) }} | ||||
|                 </li> | ||||
|                 <li> | ||||
|                     <a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="btn btn-show"></a> | ||||
|                 </li> | ||||
|             {% endif %} | ||||
|             {% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %} | ||||
|                 <li> | ||||
|                     <a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="btn btn-update"></a> | ||||
|                 </li> | ||||
|             {% endif %} | ||||
|         </ul> | ||||
|     {% endif %} | ||||
|     </div> | ||||
| </div> | ||||
| @@ -1,55 +0,0 @@ | ||||
| {% macro mimeIcon(type) %} | ||||
|      | ||||
|     {# mapping | ||||
|         forkawesome and mime type  https://gist.github.com/colemanw/9c9a12aae16a4bfe2678de86b661d922 | ||||
|     #} | ||||
|     {% set mapmime = { | ||||
|         'image': 'fa-file-image-o', | ||||
|         'audio': 'fa-file-audio-o', | ||||
|         'video': 'fa-file-video-o', | ||||
|         'application/pdf': 'fa-file-pdf-o', | ||||
|         'application/msword': 'fa-file-word-o', | ||||
|         'application/vnd.ms-word': 'fa-file-word-o', | ||||
|         'application/vnd.oasis.opendocument.text': 'fa-file-word-o', | ||||
|         'application/vnd.openxmlformats-officedocument.wordprocessingml': 'fa-file-word-o', | ||||
|         'application/vnd.ms-excel': 'fa-file-excel-o', | ||||
|         'application/vnd.openxmlformats-officedocument.spreadsheetml': 'fa-file-excel-o', | ||||
|         'application/vnd.oasis.opendocument.spreadsheet': 'fa-file-excel-o', | ||||
|         'application/vnd.ms-powerpoint': 'fa-file-powerpoint-o', | ||||
|         'application/vnd.openxmlformats-officedocument.presentationml': 'fa-file-powerpoint-o', | ||||
|         'application/vnd.oasis.opendocument.presentation': 'fa-file-powerpoint-o', | ||||
|         'text/plain': 'fa-file-text-o', | ||||
|         'text/html': 'fa-file-code-o', | ||||
|         'application/json': 'fa-file-code-o', | ||||
|         'application/gzip': 'fa-file-archive-o', | ||||
|         'application/zip': 'fa-file-archive-o', | ||||
|     } %} | ||||
|      | ||||
|     {% set icon = 'fa-file-o' %} | ||||
|     {% for key,val in mapmime %} | ||||
|         {% if type starts with key %} | ||||
|             {% set icon = val %} | ||||
|         {% endif %} | ||||
|     {% endfor %} | ||||
|      | ||||
|     {# TODO improve mapping | ||||
|         mime type and friendly name  https://gist.github.com/rosskmurphy/3724501 | ||||
|     #} | ||||
|     {% set maptype = { | ||||
|         'fa-file-word-o': 'office document/texte', | ||||
|         'fa-file-excel-o': 'office document/tableur', | ||||
|         'fa-file-powerpoint-o': 'office document/presentation', | ||||
|     } %} | ||||
|      | ||||
|     {% set label = type %} | ||||
|     {% for key, val in maptype %} | ||||
|         {% if icon == key %} | ||||
|             {% set label = val %} | ||||
|         {% endif %} | ||||
|     {% endfor %} | ||||
|      | ||||
|     <div class="metadata"> | ||||
|         <i class="fa {{ icon }} fa-lg me-1"></i> | ||||
|         {{ label|capitalize }} | ||||
|     </div> | ||||
| {% endmacro %} | ||||
| @@ -30,9 +30,7 @@ | ||||
|     {{ form_row(form.title) }} | ||||
|     {{ form_row(form.date) }} | ||||
|     {{ form_row(form.category) }} | ||||
|     {% if form.scope is defined %} | ||||
|         {{ form_row(form.scope) }} | ||||
|     {% endif %} | ||||
|     {{ form_row(form.scope) }} | ||||
|     {{ form_row(form.description) }} | ||||
|     {{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }} | ||||
|  | ||||
|   | ||||
| @@ -30,24 +30,54 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block personcontent %} | ||||
|  | ||||
| <div class="col-md-10 col-xxl"> | ||||
| 	<h1>{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1> | ||||
|  | ||||
| 	{% if documents|length == 0 %} | ||||
|             <p class="chill-no-data-statement">{{ 'No documents'|trans }}</p> | ||||
|     {% else %} | ||||
| 		<div class="flex-table chill-task-list"> | ||||
| 			{% for document in documents %} | ||||
| 				{% include 'ChillDocStoreBundle:List:list_item.html.twig' %} | ||||
| 			{% endfor %} | ||||
| 		</div> | ||||
|     {% endif %} | ||||
| 	<table class="table table-bordered border-dark table-striped"> | ||||
|  | ||||
| 	{{ chill_pagination(pagination) }} | ||||
| 		<thead> | ||||
| 			<tr> | ||||
| 				<th>{{ 'Title' | trans }}</th> | ||||
| 				<th>{{ 'Category'|trans }}</th> | ||||
| 				<th>{{ 'Circle' | trans }}</th> | ||||
| 				<th>{{ 'Actions' | trans }}</th> | ||||
| 			</tr> | ||||
| 		</thead> | ||||
| 		<tbody> | ||||
| 			{% for document in documents %} | ||||
| 				<tr> | ||||
| 					<td>{{ document.title }}</td> | ||||
| 					<td>{{ document.category.name|localize_translatable_string }}</td> | ||||
| 					<td>{{ document.scope.name|localize_translatable_string }}</td> | ||||
| 					<td> | ||||
| 						<ul class="record_actions"> | ||||
| 							{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %} | ||||
| 								<li> | ||||
| 									{{ m.download_button(document.object, document.title) }} | ||||
| 								</li> | ||||
| 								<li> | ||||
| 									<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="btn btn-show"></a> | ||||
| 								</li> | ||||
| 							{% endif %} | ||||
| 							{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %} | ||||
| 								<li> | ||||
| 									<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="btn btn-update"></a> | ||||
| 								</li> | ||||
| 							{% endif %} | ||||
| 						</ul> | ||||
| 					</td> | ||||
| 				</tr> | ||||
| 			{% else %} | ||||
| 				<tr> | ||||
| 					<td colspan="9" style="text-align:center;"> | ||||
| 						<span class="chill-no-data-statement">{{ 'Any document found'|trans }}</span> | ||||
| 					</td> | ||||
| 				</tr> | ||||
| 			{% endfor %} | ||||
| 		</tbody> | ||||
| 	</table> | ||||
|  | ||||
| 	{% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %} | ||||
| 		<ul class="record_actions sticky-form-buttons"> | ||||
| 		<ul class="record_actions"> | ||||
| 			<li class="create"> | ||||
| 				<a href="{{ path('person_document_new', {'person': person.id}) }}" class="btn btn-create"> | ||||
| 					{{ 'Create new document' | trans }} | ||||
| @@ -55,6 +85,4 @@ | ||||
| 			</li> | ||||
| 		</ul> | ||||
| 	{% endif %} | ||||
|  | ||||
| </div> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| {% set activeRouteKey = '' %} | ||||
|  | ||||
| {% import "@ChillDocStore/Macro/macro.html.twig" as m %} | ||||
| {% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %} | ||||
|  | ||||
| {% block title %}{{ 'Detail of document of %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}{% endblock %} | ||||
|  | ||||
| @@ -30,16 +29,12 @@ | ||||
| {% block personcontent %} | ||||
|     <h1>{{ 'Document %title%' | trans({ '%title%': document.title }) }}</h1> | ||||
|  | ||||
|     {{ mm.mimeIcon(document.object.type) }} | ||||
|  | ||||
|     <dl class="chill_view_data"> | ||||
|         <dt>{{ 'Title'|trans }}</dt> | ||||
|         <dd>{{ document.title }}</dd> | ||||
|  | ||||
|         {% if document.scope is not null %} | ||||
|         <dt>{{ 'Scope' | trans }}</dt> | ||||
|         <dd>{{ document.scope.name | localize_translatable_string }}</dd> | ||||
|         {% endif %} | ||||
|  | ||||
|         <dt>{{ 'Category'|trans }}</dt> | ||||
|         <dd>{{ document.category.name|localize_translatable_string }}</dd> | ||||
| @@ -57,7 +52,7 @@ | ||||
|  | ||||
|     </dl> | ||||
|  | ||||
|     <ul class="record_actions sticky-form-buttons"> | ||||
|     <ul class="record_actions"> | ||||
|         <li class="cancel"> | ||||
|             <a href="{{ path('person_document_index', {'person': person.id}) }}" class="btn btn-cancel"> | ||||
|                 {{ 'Back to the list' | trans }} | ||||
| @@ -68,12 +63,6 @@ | ||||
|             {{ m.download_button(document.object, document.title) }} | ||||
|         </li> | ||||
|  | ||||
|         {% if chill_document_is_editable(document.object) %} | ||||
|             <li> | ||||
|                 {{ document.object|chill_document_edit_button }} | ||||
|             </li> | ||||
|         {% endif %} | ||||
|  | ||||
|         {% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %} | ||||
|             <li> | ||||
|                 <a href="{{ path('person_document_edit', {'id': document.id, 'person': person.id}) }}" class="btn btn-edit"> | ||||
|   | ||||
| @@ -1,36 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Templating; | ||||
|  | ||||
| use Twig\Extension\AbstractExtension; | ||||
| use Twig\TwigFilter; | ||||
| use Twig\TwigFunction; | ||||
|  | ||||
| class WopiEditTwigExtension extends AbstractExtension | ||||
| { | ||||
|     public function getFilters(): array | ||||
|     { | ||||
|         return [ | ||||
|             new TwigFilter('chill_document_edit_button', [WopiEditTwigExtensionRuntime::class, 'renderEditButton'], [ | ||||
|                 'needs_environment' => true, | ||||
|                 'is_safe' => ['html'], | ||||
|             ]), | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function getFunctions(): array | ||||
|     { | ||||
|         return [ | ||||
|             new TwigFunction('chill_document_is_editable', [WopiEditTwigExtensionRuntime::class, 'isEditable']), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @@ -1,55 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Templating; | ||||
|  | ||||
| use ChampsLibres\WopiLib\Contract\Service\Discovery\DiscoveryInterface; | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Twig\Environment; | ||||
| use Twig\Extension\RuntimeExtensionInterface; | ||||
| use function array_key_exists; | ||||
|  | ||||
| class WopiEditTwigExtensionRuntime implements RuntimeExtensionInterface | ||||
| { | ||||
|     public const TEMPLATE = '@ChillDocStore/Button/wopi_edit_document.html.twig'; | ||||
|  | ||||
|     private DiscoveryInterface $discovery; | ||||
|  | ||||
|     public function __construct(DiscoveryInterface $discovery) | ||||
|     { | ||||
|         $this->discovery = $discovery; | ||||
|     } | ||||
|  | ||||
|     public function isEditable(StoredObject $document): bool | ||||
|     { | ||||
|         $mime_type = $this->discovery->discoverMimeType($document->getType()); | ||||
|  | ||||
|         if ([] === $mime_type) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         foreach ($mime_type as $item) { | ||||
|             if (array_key_exists('default', $item) && 'true' === $item['default']) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public function renderEditButton(Environment $environment, StoredObject $document, ?array $options = null): string | ||||
|     { | ||||
|         return $environment->render(self::TEMPLATE, [ | ||||
|             'document' => $document, | ||||
|             'options' => $options, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @@ -6,6 +6,7 @@ services: | ||||
|     autowire: true | ||||
|     autoconfigure: true | ||||
|     resource: "../Repository/" | ||||
|     tags: ["doctrine.repository_service"] | ||||
|  | ||||
|   Chill\DocStoreBundle\Form\DocumentCategoryType: | ||||
|     class: Chill\DocStoreBundle\Form\DocumentCategoryType | ||||
| @@ -15,10 +16,8 @@ services: | ||||
|  | ||||
|   Chill\DocStoreBundle\Form\PersonDocumentType: | ||||
|     class: Chill\DocStoreBundle\Form\PersonDocumentType | ||||
|     autowire: true | ||||
|     autoconfigure: true | ||||
|     # arguments: | ||||
|     #   - "@chill.main.helper.translatable_string" | ||||
|     arguments: | ||||
|       - "@chill.main.helper.translatable_string" | ||||
|     tags: | ||||
|       - { name: form.type, alias: chill_docstorebundle_form_document } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| services: | ||||
|  | ||||
|   Chill\DocStoreBundle\Templating\: | ||||
|     resource: ../../Templating | ||||
|     autoconfigure: true | ||||
|     autowire: true | ||||
| @@ -1,76 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\Migrations\DocStore; | ||||
|  | ||||
| use Doctrine\DBAL\Schema\Schema; | ||||
| use Doctrine\Migrations\AbstractMigration; | ||||
|  | ||||
| /** | ||||
|  * Implementations of create and update traits for Document entity + template property added. | ||||
|  */ | ||||
| final class Version20220131093117 extends AbstractMigration | ||||
| { | ||||
|     public function down(Schema $schema): void | ||||
|     { | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document DROP CONSTRAINT FK_41DA53C5DA0FB8'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document DROP CONSTRAINT FK_41DA53C3174800F'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document DROP CONSTRAINT FK_41DA53C65FF1AEC'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document DROP template_id'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document DROP createdAt'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document DROP updatedAt'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document DROP createdBy_id'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document DROP updatedBy_id'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP CONSTRAINT FK_A45098F65DA0FB8'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP CONSTRAINT FK_A45098F63174800F'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP CONSTRAINT FK_A45098F665FF1AEC'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP template_id'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP createdAt'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP updatedAt'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP createdBy_id'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document DROP updatedBy_id'); | ||||
|     } | ||||
|  | ||||
|     public function getDescription(): string | ||||
|     { | ||||
|         return 'Implementations of create and update traits for Document entity + template property added'; | ||||
|     } | ||||
|  | ||||
|     public function up(Schema $schema): void | ||||
|     { | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD template_id INT DEFAULT NULL'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD createdBy_id INT DEFAULT NULL'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD updatedBy_id INT DEFAULT NULL'); | ||||
|         $this->addSql('COMMENT ON COLUMN chill_doc.accompanyingcourse_document.createdAt IS \'(DC2Type:datetime_immutable)\''); | ||||
|         $this->addSql('COMMENT ON COLUMN chill_doc.accompanyingcourse_document.updatedAt IS \'(DC2Type:datetime_immutable)\''); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD CONSTRAINT FK_A45098F65DA0FB8 FOREIGN KEY (template_id) REFERENCES chill_docgen_template (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD CONSTRAINT FK_A45098F63174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.accompanyingcourse_document ADD CONSTRAINT FK_A45098F665FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|         $this->addSql('CREATE INDEX IDX_A45098F65DA0FB8 ON chill_doc.accompanyingcourse_document (template_id)'); | ||||
|         $this->addSql('CREATE INDEX IDX_A45098F63174800F ON chill_doc.accompanyingcourse_document (createdBy_id)'); | ||||
|         $this->addSql('CREATE INDEX IDX_A45098F665FF1AEC ON chill_doc.accompanyingcourse_document (updatedBy_id)'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document ADD template_id INT DEFAULT NULL'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document ADD updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document ADD createdBy_id INT DEFAULT NULL'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document ADD updatedBy_id INT DEFAULT NULL'); | ||||
|         $this->addSql('COMMENT ON COLUMN chill_doc.person_document.createdAt IS \'(DC2Type:datetime_immutable)\''); | ||||
|         $this->addSql('COMMENT ON COLUMN chill_doc.person_document.updatedAt IS \'(DC2Type:datetime_immutable)\''); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C5DA0FB8 FOREIGN KEY (template_id) REFERENCES chill_docgen_template (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|         $this->addSql('ALTER TABLE chill_doc.person_document ADD CONSTRAINT FK_41DA53C65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|         $this->addSql('CREATE INDEX IDX_41DA53C5DA0FB8 ON chill_doc.person_document (template_id)'); | ||||
|         $this->addSql('CREATE INDEX IDX_41DA53C3174800F ON chill_doc.person_document (createdBy_id)'); | ||||
|         $this->addSql('CREATE INDEX IDX_41DA53C65FF1AEC ON chill_doc.person_document (updatedBy_id)'); | ||||
|     } | ||||
| } | ||||
| @@ -10,7 +10,6 @@ New document for %name%: Nouveau document pour %name% | ||||
| Editing document for %name%: Modification d'un document pour %name% | ||||
| Edit Document: Modification d'un document | ||||
| Update document: Modifier le document | ||||
| Edit attributes: Modifier les propriétés du document | ||||
| Existing document: Document existant | ||||
| No document to download: Aucun document à télécharger | ||||
| 'Choose a document category': Choisissez une catégorie de document | ||||
| @@ -52,7 +51,4 @@ Id inside bundle: Identifiant | ||||
| Document class: Classe de document | ||||
| no records found: | ||||
| Create new category: Créer une nouvelle catégorie | ||||
| Back to the category list: Retour à la liste | ||||
|  | ||||
| # WOPI EDIT | ||||
| online_edit_document: Éditer en ligne | ||||
| Back to the category list: Retour à la liste | ||||
| @@ -12,8 +12,6 @@ declare(strict_types=1); | ||||
| namespace Chill\MainBundle\Controller; | ||||
|  | ||||
| use Chill\MainBundle\CRUD\Controller\ApiController; | ||||
| use Chill\MainBundle\Pagination\PaginatorInterface; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
|  | ||||
| /** | ||||
| @@ -21,7 +19,7 @@ use Symfony\Component\HttpFoundation\Request; | ||||
|  */ | ||||
| class LocationApiController extends ApiController | ||||
| { | ||||
|     protected function customizeQuery(string $action, Request $request, $query): void | ||||
|     public function customizeQuery(string $action, Request $request, $query): void | ||||
|     { | ||||
|         $query | ||||
|             ->leftJoin('e.locationType', 'lt') | ||||
| @@ -33,14 +31,4 @@ class LocationApiController extends ApiController | ||||
|                 ) | ||||
|             ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param QueryBuilder $query | ||||
|      * @param mixed $_format | ||||
|      */ | ||||
|     protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format) | ||||
|     { | ||||
|         return $query | ||||
|             ->addOrderBy('e.name', 'ASC'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -236,10 +236,6 @@ class NotificationController extends AbstractController | ||||
|                         '_fragment' => 'comment-' . $commentId, | ||||
|                     ]); | ||||
|                 } | ||||
|  | ||||
|                 if ($editedCommentForm->isSubmitted() && !$editedCommentForm->isValid()) { | ||||
|                     $this->addFlash('error', $this->translator->trans('This form contains errors')); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -261,10 +257,6 @@ class NotificationController extends AbstractController | ||||
|                         'id' => $notification->getId(), | ||||
|                     ]); | ||||
|                 } | ||||
|  | ||||
|                 if ($appendCommentForm->isSubmitted() && !$appendCommentForm->isValid()) { | ||||
|                     $this->addFlash('error', $this->translator->trans('This form contains errors')); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,6 @@ use Chill\MainBundle\Pagination\PaginatorFactory; | ||||
| use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository; | ||||
| use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter; | ||||
| use Chill\MainBundle\Workflow\EntityWorkflowManager; | ||||
| use Chill\MainBundle\Workflow\Validator\StepDestValid; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| @@ -167,7 +166,6 @@ class WorkflowController extends AbstractController | ||||
|  | ||||
|         $handler = $this->entityWorkflowManager->getHandler($entityWorkflow); | ||||
|         $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()); | ||||
|         $errors = []; | ||||
|  | ||||
|         if (count($workflow->getEnabledTransitions($entityWorkflow)) > 0) { | ||||
|             // possible transition | ||||
| @@ -203,18 +201,9 @@ class WorkflowController extends AbstractController | ||||
|                     $entityWorkflow->getCurrentStep()->addDestUser($user); | ||||
|                 } | ||||
|  | ||||
|                 $errors = $this->validator->validate( | ||||
|                     $entityWorkflow->getCurrentStep(), | ||||
|                     new StepDestValid() | ||||
|                 ); | ||||
|                 $this->entityManager->flush(); | ||||
|  | ||||
|                 if (count($errors) === 0) { | ||||
|                     $this->entityManager->flush(); | ||||
|  | ||||
|                     return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); | ||||
|                 } | ||||
|  | ||||
|                 return new Response((string) $errors, Response::HTTP_UNPROCESSABLE_ENTITY); | ||||
|                 return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); | ||||
|             } | ||||
|  | ||||
|             if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) { | ||||
| @@ -246,7 +235,6 @@ class WorkflowController extends AbstractController | ||||
|                 'handler_template_data' => $handler->getTemplateData($entityWorkflow), | ||||
|                 'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null, | ||||
|                 'entity_workflow' => $entityWorkflow, | ||||
|                 'transition_form_errors' => $errors, | ||||
|                 //'comment_form' => $commentForm->createView(), | ||||
|             ] | ||||
|         ); | ||||
|   | ||||
| @@ -41,6 +41,11 @@ class Configuration implements ConfigurationInterface | ||||
|  | ||||
|         $rootNode | ||||
|             ->children() | ||||
|             ->scalarNode('phonenumber_default_country_code') | ||||
|             ->cannotBeEmpty() | ||||
|             ->isRequired() | ||||
|             ->defaultValue('+32') | ||||
|             ->end() // end of scalar 'phonenumber_default_country_code' | ||||
|             ->scalarNode('installation_name') | ||||
|             ->cannotBeEmpty() | ||||
|             ->defaultValue('Chill') | ||||
|   | ||||
| @@ -18,7 +18,6 @@ use DateTimeInterface; | ||||
| use Doctrine\ORM\Event\LifecycleEventArgs; | ||||
| use Doctrine\ORM\Event\PreFlushEventArgs; | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
| use Symfony\Component\Validator\Constraints as Assert; | ||||
|  | ||||
| /** | ||||
|  * @ORM\Entity | ||||
| @@ -29,7 +28,6 @@ class NotificationComment implements TrackCreationInterface, TrackUpdateInterfac | ||||
| { | ||||
|     /** | ||||
|      * @ORM\Column(type="text") | ||||
|      * @Assert\NotBlank(message="notification.Comment content might not be blank") | ||||
|      */ | ||||
|     private string $content = ''; | ||||
|  | ||||
| @@ -138,9 +136,9 @@ class NotificationComment implements TrackCreationInterface, TrackUpdateInterfac | ||||
|         $this->recentlyPersisted = true; | ||||
|     } | ||||
|  | ||||
|     public function setContent(?string $content): self | ||||
|     public function setContent(string $content): self | ||||
|     { | ||||
|         $this->content = (string) $content; | ||||
|         $this->content = $content; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|   | ||||
| @@ -135,7 +135,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface | ||||
|         if (!$this->steps->contains($step)) { | ||||
|             $this->steps[] = $step; | ||||
|             $step->setEntityWorkflow($this); | ||||
|             $this->stepsChainedCache = null; | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
| @@ -175,20 +174,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public function getCurrentStepChained(): ?EntityWorkflowStep | ||||
|     { | ||||
|         $steps = $this->getStepsChained(); | ||||
|         $currentStep = $this->getCurrentStep(); | ||||
|  | ||||
|         foreach ($steps as $step) { | ||||
|             if ($step === $currentStep) { | ||||
|                 return $step; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public function getCurrentStepCreatedAt(): ?DateTimeInterface | ||||
|     { | ||||
|         if (null !== $previous = $this->getPreviousStepIfAny()) { | ||||
| @@ -333,26 +318,32 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface | ||||
|  | ||||
|     public function isFinal(): bool | ||||
|     { | ||||
|         foreach ($this->getStepsChained() as $step) { | ||||
|             if ($step->isFinal()) { | ||||
|                 return true; | ||||
|             } | ||||
|         $steps = $this->getStepsChained(); | ||||
|  | ||||
|         if (1 === count($steps)) { | ||||
|             // the initial step cannot be finalized | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         /** @var EntityWorkflowStep $last */ | ||||
|         $last = end($steps); | ||||
|  | ||||
|         return $last->isFinal(); | ||||
|     } | ||||
|  | ||||
|     public function isFreeze(): bool | ||||
|     { | ||||
|         $steps = $this->getStepsChained(); | ||||
|  | ||||
|         foreach ($this->getStepsChained() as $step) { | ||||
|             if ($step->isFreezeAfter()) { | ||||
|                 return true; | ||||
|             } | ||||
|         if (1 === count($steps)) { | ||||
|             // the initial step cannot be finalized | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         /** @var EntityWorkflowStep $last */ | ||||
|         $last = end($steps); | ||||
|  | ||||
|         return $last->getPrevious()->isFreezeAfter(); | ||||
|     } | ||||
|  | ||||
|     public function isUserSubscribedToFinal(User $user): bool | ||||
| @@ -429,7 +420,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface | ||||
|         $newStep->setCurrentStep($step); | ||||
|  | ||||
|         // copy the freeze | ||||
|         if ($this->isFreeze()) { | ||||
|         if ($this->getCurrentStep()->isFreezeAfter()) { | ||||
|             $newStep->setFreezeAfter(true); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -48,8 +48,6 @@ class WorkflowStepType extends AbstractType | ||||
|         $entityWorkflow = $options['entity_workflow']; | ||||
|         $handler = $this->entityWorkflowManager->getHandler($entityWorkflow); | ||||
|         $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()); | ||||
|         $place = $workflow->getMarking($entityWorkflow); | ||||
|         $placeMetadata = $workflow->getMetadataStore()->getPlaceMetadata(array_keys($place->getPlaces())[0]); | ||||
|  | ||||
|         if (true === $options['transition']) { | ||||
|             if (null === $options['entity_workflow']) { | ||||
| @@ -70,77 +68,40 @@ class WorkflowStepType extends AbstractType | ||||
|                 $transitions | ||||
|             ); | ||||
|  | ||||
|             if (array_key_exists('validationFilterInputLabels', $placeMetadata)) { | ||||
|                 $inputLabels = $placeMetadata['validationFilterInputLabels']; | ||||
|  | ||||
|                 $builder->add('transitionFilter', ChoiceType::class, [ | ||||
|                     'multiple' => false, | ||||
|                     'label' => 'workflow.My decision', | ||||
|                     'choices' => [ | ||||
|                         'forward' => 'forward', | ||||
|                         'backward' => 'backward', | ||||
|                         'neutral' => 'neutral', | ||||
|                     ], | ||||
|                     'choice_label' => function (string $key) use ($inputLabels) { | ||||
|                         return $this->translatableStringHelper->localize($inputLabels[$key]); | ||||
|                     }, | ||||
|                     'choice_attr' => static function (string $key) { | ||||
|                         return [ | ||||
|                             $key => $key, | ||||
|                         ]; | ||||
|                     }, | ||||
|                     'mapped' => false, | ||||
|                     'expanded' => true, | ||||
|                     'data' => 'forward', | ||||
|                 ]); | ||||
|             } | ||||
|  | ||||
|             $builder | ||||
|                 ->add('transition', ChoiceType::class, [ | ||||
|                     'label' => 'workflow.Next step', | ||||
|                     'label' => 'workflow.Transition to apply', | ||||
|                     'mapped' => false, | ||||
|                     'multiple' => false, | ||||
|                     'expanded' => true, | ||||
|                     'choices' => $choices, | ||||
|                     'choice_label' => function (Transition $transition) use ($workflow) { | ||||
|                         $meta = $workflow->getMetadataStore()->getTransitionMetadata($transition); | ||||
|                             $meta = $workflow->getMetadataStore()->getTransitionMetadata($transition); | ||||
|  | ||||
|                         if (array_key_exists('label', $meta)) { | ||||
|                             return $this->translatableStringHelper->localize($meta['label']); | ||||
|                         } | ||||
|  | ||||
|                         return $transition->getName(); | ||||
|                     }, | ||||
|                     'choice_attr' => static function (Transition $transition) use ($workflow) { | ||||
|                         $toFinal = true; | ||||
|                         $isForward = 'neutral'; | ||||
|  | ||||
|                         $metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition); | ||||
|  | ||||
|                         if (array_key_exists('isForward', $metadata)) { | ||||
|                             if ($metadata['isForward']) { | ||||
|                                 $isForward = 'forward'; | ||||
|                             } else { | ||||
|                                 $isForward = 'backward'; | ||||
|                             if (array_key_exists('label', $meta)) { | ||||
|                                 return $this->translatableStringHelper->localize($meta['label']); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         foreach ($transition->getTos() as $to) { | ||||
|                             $meta = $workflow->getMetadataStore()->getPlaceMetadata($to); | ||||
|                             return $transition->getName(); | ||||
|                         }, | ||||
|                     'choice_attr' => static function (Transition $transition) use ($workflow) { | ||||
|                             $toFinal = true; | ||||
|  | ||||
|                             if ( | ||||
|                             foreach ($transition->getTos() as $to) { | ||||
|                                 $meta = $workflow->getMetadataStore()->getPlaceMetadata($to); | ||||
|  | ||||
|                                 if ( | ||||
|                                     !array_key_exists('isFinal', $meta) || false === $meta['isFinal'] | ||||
|                                 ) { | ||||
|                                 $toFinal = false; | ||||
|                                     $toFinal = false; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         return [ | ||||
|                             'data-is-transition' => 'data-is-transition', | ||||
|                             'data-to-final' => $toFinal ? '1' : '0', | ||||
|                             'data-is-forward' => $isForward, | ||||
|                         ]; | ||||
|                     }, | ||||
|                             return [ | ||||
|                                 'data-is-transition' => 'data-is-transition', | ||||
|                                 'data-to-final' => $toFinal ? '1' : '0', | ||||
|                             ]; | ||||
|                         }, | ||||
|                 ]) | ||||
|                 ->add('future_dest_users', PickUserDynamicType::class, [ | ||||
|                     'label' => 'workflow.dest for next steps', | ||||
|   | ||||
| @@ -79,8 +79,11 @@ class NotificationMailer | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             $email = new Email(); | ||||
|             $email | ||||
|                 ->subject($notification->getTitle()); | ||||
|  | ||||
|             if ($notification->isSystem()) { | ||||
|                 $email = new Email(); | ||||
|                 $email | ||||
|                     ->text($notification->getMessage()); | ||||
|             } else { | ||||
| @@ -93,9 +96,7 @@ class NotificationMailer | ||||
|                     ]); | ||||
|             } | ||||
|  | ||||
|             $email | ||||
|                 ->subject($notification->getTitle()) | ||||
|                 ->to($addressee->getEmail()); | ||||
|             $email->to($addressee->getEmail()); | ||||
|  | ||||
|             try { | ||||
|                 $this->mailer->send($email); | ||||
|   | ||||
| @@ -110,8 +110,7 @@ class EntityWorkflowRepository implements ObjectRepository | ||||
|         $qb->where( | ||||
|             $qb->expr()->andX( | ||||
|                 $qb->expr()->isMemberOf(':user', 'step.destUser'), | ||||
|                 $qb->expr()->isNull('step.transitionAfter'), | ||||
|                 $qb->expr()->eq('step.isFinal', "'FALSE'") | ||||
|                 $qb->expr()->isNull('step.transitionAfter') | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|   | ||||
| @@ -1,79 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\MainBundle\Repository\Workflow; | ||||
|  | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Doctrine\ORM\EntityRepository; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Doctrine\Persistence\ObjectRepository; | ||||
|  | ||||
| class EntityWorkflowStepRepository implements ObjectRepository | ||||
| { | ||||
|     private EntityRepository $repository; | ||||
|  | ||||
|     public function __construct(EntityManagerInterface $entityManager) | ||||
|     { | ||||
|         $this->repository = $entityManager->getRepository(EntityWorkflowStep::class); | ||||
|     } | ||||
|  | ||||
|     public function countUnreadByUser(User $user, ?array $orderBy = null, $limit = null, $offset = null): int | ||||
|     { | ||||
|         $qb = $this->buildQueryByUser($user)->select('count(e)'); | ||||
|  | ||||
|         return (int) $qb->getQuery()->getSingleScalarResult(); | ||||
|     } | ||||
|  | ||||
|     public function find($id): ?EntityWorkflowStep | ||||
|     { | ||||
|         return $this->repository->find($id); | ||||
|     } | ||||
|  | ||||
|     public function findAll(): array | ||||
|     { | ||||
|         return $this->repository->findAll(); | ||||
|     } | ||||
|  | ||||
|     public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array | ||||
|     { | ||||
|         return $this->repository->findBy($criteria, $orderBy, $limit, $offset); | ||||
|     } | ||||
|  | ||||
|     public function findOneBy(array $criteria): ?EntityWorkflowStep | ||||
|     { | ||||
|         return $this->repository->findOneBy($criteria); | ||||
|     } | ||||
|  | ||||
|     public function getClassName() | ||||
|     { | ||||
|         return EntityWorkflowStep::class; | ||||
|     } | ||||
|  | ||||
|     private function buildQueryByUser(User $user): QueryBuilder | ||||
|     { | ||||
|         $qb = $this->repository->createQueryBuilder('e'); | ||||
|  | ||||
|         $qb->where( | ||||
|             $qb->expr()->andX( | ||||
|                 $qb->expr()->isMemberOf(':user', 'e.destUser'), | ||||
|                 $qb->expr()->isNull('e.transitionAt'), | ||||
|                 $qb->expr()->eq('e.isFinal', ':bool'), | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         $qb->setParameter('user', $user); | ||||
|         $qb->setParameter('bool', false); | ||||
|  | ||||
|         return $qb; | ||||
|     } | ||||
| } | ||||
| @@ -25,7 +25,6 @@ import { chill } from './js/chill.js'; | ||||
| global.chill = chill; | ||||
|  | ||||
| require('./js/date.js'); | ||||
| require('./js/counter.js'); | ||||
|  | ||||
| /// Load fonts | ||||
| require('./fonts/OpenSans/OpenSans.scss') | ||||
|   | ||||
| @@ -1,35 +0,0 @@ | ||||
| /** | ||||
|  * | ||||
|  * This script search for span.counter elements like | ||||
|  *   <span class="counter">Il y a 4 notifications</span> | ||||
|  * and return | ||||
|  *   <span class="counter">Il y a <span>4</span> notifications</span> | ||||
|  * | ||||
|  */ | ||||
| const isNum = (v) => !isNaN(v); | ||||
|  | ||||
| const parseCounter = () => { | ||||
|     document.querySelectorAll('span.counter') | ||||
|         .forEach(el => { | ||||
|             let r = []; | ||||
|             el.innerText | ||||
|                 .trim() | ||||
|                 .split(' ') | ||||
|                 .forEach(w => { | ||||
|                     if (isNum(w)) { | ||||
|                         r.push(`<span>${w}</span>`); | ||||
|                     } else { | ||||
|                         r.push(w); | ||||
|                     } | ||||
|                 }) | ||||
|             ; | ||||
|             el.innerHTML = r.join(' '); | ||||
|         }) | ||||
|     ; | ||||
| }; | ||||
|  | ||||
| window.addEventListener('DOMContentLoaded', function (e) { | ||||
|     parseCounter(); | ||||
| }); | ||||
|  | ||||
| export { parseCounter }; | ||||
| @@ -15,7 +15,6 @@ $chill-theme-buttons: ( | ||||
|    "action":        $chill-orange, | ||||
|    "edit":          $chill-orange, | ||||
|    "update":        $chill-orange, | ||||
|    "wopilink":      $chill-orange, | ||||
|    "show":          $chill-blue, | ||||
|    "view":          $chill-blue, | ||||
|    "misc":          $gray-300, | ||||
| @@ -55,7 +54,6 @@ $chill-theme-buttons: ( | ||||
|    &.btn-action, | ||||
|    &.btn-edit, | ||||
|    &.btn-tpchild, | ||||
|    &.btn-wopilink, | ||||
|    &.btn-update { | ||||
|        &, &:hover { | ||||
|           color: $light; | ||||
| @@ -68,7 +66,6 @@ $chill-theme-buttons: ( | ||||
|    &.btn-create::before, | ||||
|    &.btn-edit::before, | ||||
|    &.btn-update::before, | ||||
|    &.btn-wopilink::before, | ||||
|    &.btn-show::before, | ||||
|    &.btn-view::before, | ||||
|    &.btn-save::before, | ||||
| @@ -101,7 +98,6 @@ $chill-theme-buttons: ( | ||||
|    &.btn-create::before    { content: "\f067"; } // fa-plus | ||||
|    &.btn-edit::before, | ||||
|    &.btn-update::before    { content: "\f040"; } // fa-pencil | ||||
|    &.btn-wopilink::before  { content: "\f1dd"; } // fa-paragraph | ||||
|    &.btn-show::before, | ||||
|    &.btn-view::before      { content: "\f06e"; } // fa-eye | ||||
|    &.btn-save::before      { content: "\f0c7"; } // fa-floppy-o | ||||
|   | ||||
| @@ -82,7 +82,7 @@ div#notification-fold { | ||||
|  | ||||
| // Counter | ||||
| div.notification-counter { | ||||
|     span.counter { | ||||
|     span { | ||||
|         &:not(:first-child) { | ||||
|             &::before { | ||||
|                 content: '/ '; | ||||
| @@ -90,11 +90,3 @@ div.notification-counter { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| span.counter { | ||||
|     & > span { | ||||
|         font-weight: bold; | ||||
|         background-color: $chill-ll-gray; | ||||
|         padding: 0 0.4rem; | ||||
|         border-radius: 50%; | ||||
|     } | ||||
| } | ||||
| @@ -1,19 +1,19 @@ | ||||
| /** | ||||
|  * Create a control to show or hide values | ||||
|  * | ||||
|  *  | ||||
|  * Possible options are: | ||||
|  * | ||||
|  *  | ||||
|  *  - froms: an Element, an Array of Element, or a NodeList. A | ||||
|  *    listener will be attached to **all** input of those elements | ||||
|  *    and will trigger the check on changes | ||||
|  *  - test: a function which will test the element and will return true | ||||
|  *  - test: a function which will test the element and will return true  | ||||
|  *    if the content must be shown, false if it must be hidden. | ||||
|  *    The function will receive the `froms` as first argument, and the | ||||
|  *    The function will receive the `froms` as first argument, and the  | ||||
|  *    event as second argument. | ||||
|  *  - container: an Element, an Array of Element, or a Node List. The | ||||
|  *  - container: an Element, an Array of Element, or a Node List. The  | ||||
|  *    child nodes will be hidden / shown inside this container | ||||
|  *  - event_name: the name of the event to listen to. `'change'` by default. | ||||
|  * | ||||
|  *  | ||||
|  * @param object options | ||||
|  */ | ||||
| var ShowHide = function(options) { | ||||
| @@ -26,10 +26,8 @@ var ShowHide = function(options) { | ||||
|         container_content = [], | ||||
|         debug = 'debug' in options ? options.debug : false, | ||||
|         load_event = 'load_event' in options ? options.load_event : 'load', | ||||
|         id = 'uid' in options ? options.id : Math.random(), | ||||
|         toggle_callback = 'toggle_callback' in options ? options.toggle_callback : null | ||||
|     ; | ||||
|  | ||||
|         id = 'uid' in options ? options.id : Math.random(); | ||||
|      | ||||
|     var bootstrap = function(event) { | ||||
|         if (debug) { | ||||
|             console.log('debug is activated on this show-hide', this); | ||||
| @@ -41,14 +39,15 @@ var ShowHide = function(options) { | ||||
|                 contents.push(el); | ||||
|             } | ||||
|             container_content.push(contents); | ||||
|             // console.log('container content', container_content); | ||||
|         } | ||||
|  | ||||
|         // attach the listener on each input | ||||
|         for (let f of froms.values()) { | ||||
|             let | ||||
|                 inputs = f.querySelectorAll('input'), | ||||
|             let  | ||||
|                 inputs = f.querySelectorAll('input'),  | ||||
|                 selects = f.querySelectorAll('select'); | ||||
|  | ||||
|          | ||||
|             for (let input of inputs.values()) { | ||||
|                 if (debug) { | ||||
|                     console.log('attaching event to input', input); | ||||
| @@ -68,10 +67,10 @@ var ShowHide = function(options) { | ||||
|         } | ||||
|  | ||||
|         // first launch of the show/hide | ||||
|         onChange(event); | ||||
|         onChange(event);     | ||||
|     }; | ||||
|  | ||||
|  | ||||
|      | ||||
|     var onChange = function (event) { | ||||
|         var result = test(froms, event), me; | ||||
|  | ||||
| @@ -90,53 +89,45 @@ var ShowHide = function(options) { | ||||
|         } else { | ||||
|             throw "the result of test is not a boolean"; | ||||
|         } | ||||
|  | ||||
|          | ||||
|     }; | ||||
|  | ||||
|      | ||||
|     var forceHide = function() { | ||||
|         if (debug) { | ||||
|             console.log('force hide'); | ||||
|         } | ||||
|         if (toggle_callback !== null) { | ||||
|             toggle_callback(container, 'hide'); | ||||
|         } else { | ||||
|             for (let contents of container_content.values()) { | ||||
|                 for (let el of contents.values()) { | ||||
|                     el.remove(); | ||||
|                 } | ||||
|         for (let contents of container_content.values()) { | ||||
|             for (let el of contents.values()) { | ||||
|                 el.remove(); | ||||
|             } | ||||
|         } | ||||
|         is_shown = false; | ||||
|     }; | ||||
|  | ||||
|      | ||||
|     var forceShow = function() { | ||||
|         if (debug) { | ||||
|             console.log('show'); | ||||
|         } | ||||
|         if (toggle_callback !== null) { | ||||
|             toggle_callback(container, 'show'); | ||||
|         } else { | ||||
|             for (let i of container_content.keys()) { | ||||
|                 var contents = container_content[i]; | ||||
|                 for (let el of contents.values()) { | ||||
|                     container[i].appendChild(el); | ||||
|                 } | ||||
|         for (let i of container_content.keys()) { | ||||
|             var contents = container_content[i]; | ||||
|             for (let el of contents.values()) { | ||||
|                 container[i].appendChild(el); | ||||
|             } | ||||
|         } | ||||
|         is_shown = true; | ||||
|     }; | ||||
|  | ||||
|      | ||||
|     var forceCompute = function(event) { | ||||
|         onChange(event); | ||||
|     }; | ||||
|  | ||||
|  | ||||
|      | ||||
|      | ||||
|     if (load_event !== null) { | ||||
|         window.addEventListener('load', bootstrap); | ||||
|     } else { | ||||
|         bootstrap(null); | ||||
|     } | ||||
|  | ||||
|      | ||||
|     return { | ||||
|         forceHide: forceHide, | ||||
|         forceShow: forceShow, | ||||
|   | ||||
| @@ -17,12 +17,7 @@ function loadDynamicPicker(element) { | ||||
|             isMultiple = parseInt(el.dataset.multiple) === 1, | ||||
|             uniqId = el.dataset.uniqid, | ||||
|             input = element.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'), | ||||
|             picked = isMultiple ? | ||||
|                 JSON.parse(input.value) : ( | ||||
|                     (input.value === '[]' || input.value === '') ? | ||||
|                         null : [ JSON.parse(input.value) ] | ||||
|                 ) | ||||
|         ; | ||||
|             picked = (isMultiple) ? (JSON.parse(input.value)) : ((input.value === '[]') ? (null) : ([JSON.parse(input.value)])); | ||||
|  | ||||
|         if (!isMultiple) { | ||||
|             if (input.value === '[]'){ | ||||
|   | ||||
| @@ -4,34 +4,20 @@ import {_createI18n} from "ChillMainAssets/vuejs/_js/i18n"; | ||||
|  | ||||
| const i18n = _createI18n({}); | ||||
|  | ||||
|  | ||||
| //TODO move to chillDocStore or ChillWopi | ||||
|  | ||||
| /* | ||||
|  | ||||
| tags to load module: | ||||
|  | ||||
| <span data-module="wopi-link" | ||||
|     data-wopi-url="{{ path('chill_wopi_file_edit', {'fileId': document.uuid}) }}" | ||||
|     data-doc-type="{{ document.type|e('html_attr') }}" | ||||
|     data-options="{{ options|json_encode }}" | ||||
|         ></span> | ||||
|  | ||||
| */ | ||||
|  | ||||
| window.addEventListener('DOMContentLoaded', function (e) { | ||||
|     document.querySelectorAll('span[data-module="wopi-link"]') | ||||
|         .forEach(function (el) { | ||||
|             createApp({ | ||||
|                 template: '<open-wopi-link :wopiUrl="wopiUrl" :type="type" :options="options"></open-wopi-link>', | ||||
|                 template: '<open-wopi-link :wopiUrl="wopiUrl" :title="title" :type="type" :button="button"></open-wopi-link>', | ||||
|                 components: { | ||||
|                     OpenWopiLink | ||||
|                 }, | ||||
|                 data() { | ||||
|                     return { | ||||
|                         wopiUrl: el.dataset.wopiUrl, | ||||
|                         title: el.dataset.docTitle, | ||||
|                         type: el.dataset.docType, | ||||
|                         options: el.dataset.options !== 'null' ? JSON.parse(el.dataset.options) : {} | ||||
|                         button: el.dataset.button ? JSON.parse(el.dataset.button) : {} | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
| @@ -40,4 +26,4 @@ window.addEventListener('DOMContentLoaded', function (e) { | ||||
|             ; | ||||
|         }) | ||||
|     ; | ||||
| }); | ||||
| }); | ||||
| @@ -2,89 +2,29 @@ import {ShowHide} from 'ChillMainAssets/lib/show_hide/show_hide.js'; | ||||
|  | ||||
| window.addEventListener('DOMContentLoaded', function() { | ||||
|     let | ||||
|         divTransitions = document.querySelector('#transitions'), | ||||
|         finalizeAfterContainer = document.querySelector('#finalizeAfter'), | ||||
|         futureDestUsersContainer = document.querySelector('#futureDestUsers') | ||||
|     ; | ||||
|  | ||||
|     if (null !== divTransitions) { | ||||
|         new ShowHide({ | ||||
|             load_event: null, | ||||
|             froms: [divTransitions], | ||||
|             container: [futureDestUsersContainer], | ||||
|             test: function(divs, arg2, arg3) { | ||||
|                 for (let div of divs) { | ||||
|                     for (let input of div.querySelectorAll('input')) { | ||||
|                         if (input.checked) { | ||||
|                             if (input.dataset.toFinal === "1") { | ||||
|                                 return false; | ||||
|                             } else { | ||||
|                                 return true; | ||||
|                             } | ||||
|                         } | ||||
|     if (null === finalizeAfterContainer) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     new ShowHide({ | ||||
|         load_event: null, | ||||
|         froms: [finalizeAfterContainer], | ||||
|         container: [futureDestUsersContainer], | ||||
|         test: function(containers, arg2, arg3) { | ||||
|             for (let container of containers) { | ||||
|                 for (let input of container.querySelectorAll('input')) { | ||||
|                     if (!input.checked) { | ||||
|                         return true; | ||||
|                     } else { | ||||
|                         return false; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 return true; | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     let | ||||
|         transitionFilterContainer = document.querySelector('#transitionFilter'), | ||||
|         transitions = document.querySelector('#transitions') | ||||
|     ; | ||||
|  | ||||
|     if (null !== transitionFilterContainer) { | ||||
|         transitions.querySelectorAll('.form-check').forEach(function(row) { | ||||
|  | ||||
|             const isForward = row.querySelector('input').dataset.isForward; | ||||
|  | ||||
|             new ShowHide({ | ||||
|                 load_event: null, | ||||
|                 froms: [transitionFilterContainer], | ||||
|                 container: row, | ||||
|                 test: function (containers) { | ||||
|                     for (let container of containers) { | ||||
|                         for (let input of container.querySelectorAll('input')) { | ||||
|                             if (input.checked) { | ||||
|                                 return isForward === input.value; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 toggle_callback: function (c, dir) { | ||||
|                     for (let div of c) { | ||||
|                         let input = div.querySelector('input'); | ||||
|                         if ('hide' === dir) { | ||||
|                             input.checked = false; | ||||
|                             input.disabled = true; | ||||
|                         } else { | ||||
|                             input.disabled = false; | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     // validate form | ||||
|     let form = document.querySelector('form[name="workflow_step"]'); | ||||
|  | ||||
|     if (form === null) { | ||||
|         console.error('form to validate not found'); | ||||
|     } | ||||
|  | ||||
|     form.addEventListener('submit', function (event) { | ||||
|         const datas = new FormData(event.target); | ||||
|  | ||||
|         if (datas.has('workflow_step[future_dest_users]')) { | ||||
|             const dests = JSON.parse(datas.get('workflow_step[future_dest_users]')); | ||||
|             if (dests === null || (dests instanceof Array && dests.length === 0)) { | ||||
|                 event.preventDefault(); | ||||
|                 console.log('no users!'); | ||||
|                 window.alert('Indiquez un utilisateur pour traiter la prochaine étape.'); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|         }, | ||||
|     }) | ||||
| }); | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
|          :deselect-label="$t('create_address')" | ||||
|          :selected-label="$t('multiselect.selected_label')" | ||||
|          @search-change="listenInputSearch" | ||||
|          :internal-search="false" | ||||
|          ref="addressSelector" | ||||
|          @select="selectAddress" | ||||
|          @remove="remove" | ||||
|   | ||||
| @@ -18,7 +18,6 @@ | ||||
|          :selected-label="$t('multiselect.selected_label')" | ||||
|          :taggable="true" | ||||
|          :multiple="false" | ||||
|          :internal-search="false" | ||||
|          @tag="addPostcode" | ||||
|          :tagPlaceholder="$t('create_postal_code')" | ||||
|          :loading="isLoading" | ||||
|   | ||||
| @@ -3,38 +3,16 @@ | ||||
|    <span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span> | ||||
|    <tab-table v-else> | ||||
|       <template v-slot:thead> | ||||
|          <th scope="col">{{ $t('opening_date') }}</th> | ||||
|          <th scope="col">{{ $t('social_issues') }}</th> | ||||
|          <th scope="col">{{ $t('concerned_persons') }}</th> | ||||
|          <th scope="col"></th> | ||||
|          <th scope="col">id</th> | ||||
|          <th scope="col">Ouvert le</th> | ||||
|          <th scope="col">Usagers concernés</th> | ||||
|          <th scope="col"></th> | ||||
|       </template> | ||||
|       <template v-slot:tbody> | ||||
|          <tr v-for="(c, i) in accompanyingCourses.results" :key="`course-${i}`"> | ||||
|             <td>{{ $d(c.openingDate.datetime, 'short') }}</td> | ||||
|             <td> | ||||
|                <span v-for="i in c.socialIssues" | ||||
|                   class="chill-entity entity-social-issue"> | ||||
|                   <span class="badge bg-chill-l-gray text-dark"> | ||||
|                      {{ i.title.fr }} | ||||
|                   </span> | ||||
|                </span> | ||||
|             </td> | ||||
|             <td> | ||||
|                <span v-for="p in c.participations" class="me-1" :key="p.person.id"> | ||||
|                   <on-the-fly | ||||
|                      :type="p.person.type" | ||||
|                      :id="p.person.id" | ||||
|                      :buttonText="p.person.textAge" | ||||
|                      :displayBadge="'true' === 'true'" | ||||
|                      action="show"> | ||||
|                   </on-the-fly> | ||||
|                </span> | ||||
|             </td> | ||||
|             <td> | ||||
|                <span v-if="c.emergency" class="badge rounded-pill bg-danger me-1">{{ $t('emergency') }}</span> | ||||
|                <span v-if="c.confidential" class="badge rounded-pill bg-danger">{{ $t('confidential') }}</span> | ||||
|             </td> | ||||
|             <td>{{ c.id}}</td> | ||||
|             <td>{{ $d(c.openingDate.datetime, 'long') }}</td> | ||||
|             <td>{{ c.participations.length }}</td> | ||||
|             <td> | ||||
|                <a class="btn btn-sm btn-show" :href="getUrl(c)"> | ||||
|                   {{ $t('show_entity', { entity: $t('the_course') }) }} | ||||
| @@ -48,13 +26,11 @@ | ||||
| <script> | ||||
| import { mapState, mapGetters } from "vuex"; | ||||
| import TabTable from "./TabTable"; | ||||
| import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly'; | ||||
|  | ||||
| export default { | ||||
|    name: "MyAccompanyingCourses", | ||||
|    components: { | ||||
|       TabTable, | ||||
|       OnTheFly, | ||||
|       TabTable | ||||
|    }, | ||||
|    computed: { | ||||
|       ...mapState([ | ||||
| @@ -80,7 +56,5 @@ export default { | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| span.badge.rounded-pill.bg-danger { | ||||
|    text-transform: uppercase; | ||||
| } | ||||
|  | ||||
| </style> | ||||
| @@ -6,34 +6,22 @@ | ||||
|          <div class="custom1"> | ||||
|             <ul class="list-unstyled"> | ||||
|                <li v-if="counter.notifications > 0"> | ||||
|                   <i18n-t keypath="counter.unread_notifications" tag="span" :class="counterClass" :plural="counter.notifications"> | ||||
|                      <template v-slot:n><span>{{ counter.notifications }}</span></template> | ||||
|                   </i18n-t> | ||||
|                   <b>{{ counter.notifications }}</b> {{ $t('counter.unread_notifications') }} | ||||
|                </li> | ||||
|                <li v-if="counter.accompanyingCourses > 0"> | ||||
|                   <i18n-t keypath="counter.assignated_courses" tag="span" :class="counterClass" :plural="counter.accompanyingCourses"> | ||||
|                      <template v-slot:n><span>{{ counter.accompanyingCourses }}</span></template> | ||||
|                   </i18n-t> | ||||
|                   <b>{{ counter.accompanyingCourses }}</b> {{ $t('counter.assignated_courses') }} | ||||
|                </li> | ||||
|                <li v-if="counter.works > 0"> | ||||
|                   <i18n-t keypath="counter.assignated_actions" tag="span" :class="counterClass" :plural="counter.works"> | ||||
|                      <template v-slot:n><span>{{ counter.works }}</span></template> | ||||
|                   </i18n-t> | ||||
|                   <b>{{ counter.works }}</b> {{ $t('counter.assignated_actions') }} | ||||
|                </li> | ||||
|                <li v-if="counter.evaluations > 0"> | ||||
|                   <i18n-t keypath="counter.assignated_evaluations" tag="span" :class="counterClass" :plural="counter.evaluations"> | ||||
|                      <template v-slot:n><span>{{ counter.evaluations }}</span></template> | ||||
|                   </i18n-t> | ||||
|                   <b>{{ counter.evaluations }}</b> {{ $t('counter.assignated_evaluations') }} | ||||
|                </li> | ||||
|                <li v-if="counter.tasksAlert > 0"> | ||||
|                   <i18n-t keypath="counter.alert_tasks" tag="span" :class="counterClass" :plural="counter.tasksAlert"> | ||||
|                      <template v-slot:n><span>{{ counter.tasksAlert }}</span></template> | ||||
|                   </i18n-t> | ||||
|                   <b>{{ counter.tasksAlert }}</b> {{ $t('counter.alert_tasks') }} | ||||
|                </li> | ||||
|                <li v-if="counter.tasksWarning > 0"> | ||||
|                   <i18n-t keypath="counter.warning_tasks" tag="span" :class="counterClass" :plural="counter.tasksWarning"> | ||||
|                      <template v-slot:n><span>{{ counter.tasksWarning }}</span></template> | ||||
|                   </i18n-t> | ||||
|                   <b>{{ counter.tasksWarning }}</b> {{ $t('counter.warning_tasks') }} | ||||
|                </li> | ||||
|             </ul> | ||||
|          </div> | ||||
| @@ -66,13 +54,6 @@ import Masonry from 'masonry-layout/masonry'; | ||||
|  | ||||
| export default { | ||||
|    name: "MyCustoms", | ||||
|    data() { | ||||
|       return { | ||||
|          counterClass: { | ||||
|             counter: true   //hack to pass class 'counter' in i18n-t | ||||
|          } | ||||
|       } | ||||
|    }, | ||||
|    computed: { | ||||
|       ...mapGetters(['counter']), | ||||
|       noResults() { | ||||
| @@ -86,16 +67,11 @@ export default { | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| <style scoped> | ||||
| div.custom4, | ||||
| div.custom3, | ||||
| div.custom2 { | ||||
|    font-style: italic; | ||||
|    color: var(--bs-chill-gray); | ||||
| } | ||||
| span.counter { | ||||
|    & > span { | ||||
|       background-color: unset; | ||||
|    } | ||||
| } | ||||
| </style> | ||||
| @@ -1,71 +1,32 @@ | ||||
| <template> | ||||
| <div class="accompanying_course_work"> | ||||
|    <div class="alert alert-light">{{ $t('my_evaluations.description') }}</div> | ||||
|    <span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span> | ||||
|    <tab-table v-else> | ||||
|       <template v-slot:thead> | ||||
|          <th scope="col">{{ $t('max_date') }}</th> | ||||
|          <th scope="col">{{ $t('evaluation') }}</th> | ||||
|          <th scope="col">{{ $t('SocialAction') }}</th> | ||||
|          <th scope="col">id</th> | ||||
|          <th scope="col"></th> | ||||
|       </template> | ||||
|       <template v-slot:tbody> | ||||
|          <tr v-for="(e, i) in evaluations.results" :key="`evaluation-${i}`"> | ||||
|             <td>{{ $d(e.maxDate.datetime, 'short') }}</td> | ||||
|             <td>{{ e.id}}</td> | ||||
|             <td> | ||||
|                {{ e.evaluation.title.fr }} | ||||
|             </td> | ||||
|             <td> | ||||
|                <span class="chill-entity entity-social-issue"> | ||||
|                   <span class="badge bg-chill-l-gray text-dark"> | ||||
|                      {{ e.accompanyingPeriodWork.socialAction.issue.text }} | ||||
|                   </span> | ||||
|                </span> | ||||
|                <h4 class="badge-title"> | ||||
|                   <span class="title_label"></span> | ||||
|                   <span class="title_action"> | ||||
|                      {{ e.accompanyingPeriodWork.socialAction.text }} | ||||
|                   </span> | ||||
|                </h4> | ||||
|                <span v-for="person in e.accompanyingPeriodWork.persons" class="me-1" :key="person.id"> | ||||
|                   <on-the-fly | ||||
|                      :type="person.type" | ||||
|                      :id="person.id" | ||||
|                      :buttonText="person.textAge" | ||||
|                      :displayBadge="'true' === 'true'" | ||||
|                      action="show"> | ||||
|                   </on-the-fly> | ||||
|                </span> | ||||
|             </td> | ||||
|             <td> | ||||
|                <div class="btn-group-vertical" role="group" aria-label="Actions"> | ||||
|                   <a class="btn btn-sm btn-show" :href="getUrl(e)"> | ||||
|                      {{ $t('show_entity', { entity: $t('the_evaluation') }) }} | ||||
|                   </a> | ||||
|                   <a class="btn btn-sm btn-update" :href="getUrl(e.accompanyingPeriodWork)"> | ||||
|                      {{ $t('show_entity', { entity: $t('the_action') }) }} | ||||
|                   </a> | ||||
|                   <a class="btn btn-sm btn-show" :href="getUrl(e.accompanyingPeriodWork.accompanyingPeriod)"> | ||||
|                      {{ $t('show_entity', { entity: $t('the_course') }) }} | ||||
|                   </a> | ||||
|                </div> | ||||
|                <a class="btn btn-sm btn-show" :href="getUrl(e)"> | ||||
|                   {{ $t('show_entity', { entity: $t('the_evaluation') }) }} | ||||
|                </a> | ||||
|             </td> | ||||
|          </tr> | ||||
|       </template> | ||||
|    </tab-table> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapState, mapGetters } from "vuex"; | ||||
| import TabTable from "./TabTable"; | ||||
| import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly'; | ||||
|  | ||||
| export default { | ||||
|    name: "MyEvaluations", | ||||
|    components: { | ||||
|       TabTable, | ||||
|       OnTheFly, | ||||
|       TabTable | ||||
|    }, | ||||
|    computed: { | ||||
|       ...mapState([ | ||||
| @@ -84,17 +45,8 @@ export default { | ||||
|    }, | ||||
|    methods: { | ||||
|       getUrl(e) { | ||||
|          switch (e.type) { | ||||
|             case 'accompanying_period_work_evaluation': | ||||
|                let anchor = '#evaluations'; | ||||
|                return `/fr/person/accompanying-period/work/${e.accompanyingPeriodWork.id}/edit${anchor}`; | ||||
|             case 'accompanying_period_work': | ||||
|                return `/fr/person/accompanying-period/work/${e.id}/edit` | ||||
|             case 'accompanying_period': | ||||
|                return `/fr/parcours/${e.id}` | ||||
|             default: | ||||
|                throw 'entity type unknown'; | ||||
|          } | ||||
|          let anchor = '#evaluations'; | ||||
|          return `/fr/person/accompanying-period/work/${e.id}/edit${anchor}` | ||||
|       } | ||||
|    }, | ||||
| } | ||||
|   | ||||
| @@ -17,8 +17,7 @@ | ||||
|                   <a :href="getNotificationUrl(n)">{{ n.title }}</a> | ||||
|                </span> | ||||
|             </td> | ||||
|             <td v-if="n.sender != null">{{ n.sender.text }}</td> | ||||
|             <td v-else>{{ $t('automatic_notification')}}</td> | ||||
|             <td>{{ n.sender.text }}</td> | ||||
|             <td> | ||||
|                <a class="btn btn-sm btn-show" | ||||
|                   :href="getEntityUrl(n)"> | ||||
| @@ -66,8 +65,6 @@ export default { | ||||
|                return appMessages.fr.the_activity; | ||||
|             case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod': | ||||
|                return appMessages.fr.the_course; | ||||
|             case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow': | ||||
|                return appMessages.fr.workflow; | ||||
|             default: | ||||
|                throw 'notification type unknown'; | ||||
|          } | ||||
| @@ -78,8 +75,6 @@ export default { | ||||
|                return `/fr/activity/${n.relatedEntityId}/show` | ||||
|             case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod': | ||||
|                return `/fr/parcours/${n.relatedEntityId}` | ||||
|             case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow': | ||||
|                return `/fr/main/workflow/${n.relatedEntityId}/show` | ||||
|             default: | ||||
|                throw 'notification type unknown'; | ||||
|          } | ||||
|   | ||||
| @@ -1,21 +1,15 @@ | ||||
| <template> | ||||
|     | ||||
|    <div class="alert alert-light">{{ $t('my_tasks.description_warning') }}</div> | ||||
|    <span v-if="noResultsAlert" class="chill-no-data-statement">{{ $t('no_data') }}</span> | ||||
|    <div class="alert alert-light">{{ $t('my_tasks.description_alert') }}</div> | ||||
|    <span v-if="noResultsWarning" class="chill-no-data-statement">{{ $t('no_data') }}</span> | ||||
|    <tab-table v-else> | ||||
|       <template v-slot:thead> | ||||
|          <th scope="col">{{ $t('warning_date') }}</th> | ||||
|          <th scope="col">{{ $t('max_date') }}</th> | ||||
|          <th scope="col">{{ $t('task') }}</th> | ||||
|          <th scope="col">id</th> | ||||
|          <th scope="col"></th> | ||||
|       </template> | ||||
|       <template v-slot:tbody> | ||||
|          <tr v-for="(t, i) in tasks.alert.results" :key="`task-alert-${i}`"> | ||||
|             <td>{{ $d(t.warningDate.datetime, 'short') }}</td> | ||||
|             <td> | ||||
|                <span class="outdated">{{ $d(t.endDate.datetime, 'short') }}</span> | ||||
|             </td> | ||||
|             <td>{{ t.title }}</td> | ||||
|          <tr v-for="(t, i) in tasks.warning" :key="`task-warning-${i}`"> | ||||
|             <td>{{ t.id}}</td> | ||||
|             <td> | ||||
|                <a class="btn btn-sm btn-show" :href="getUrl(t)"> | ||||
|                   {{ $t('show_entity', { entity: $t('the_task') }) }} | ||||
| @@ -25,22 +19,16 @@ | ||||
|       </template> | ||||
|    </tab-table> | ||||
|     | ||||
|    <div class="alert alert-light">{{ $t('my_tasks.description_alert') }}</div> | ||||
|    <span v-if="noResultsWarning" class="chill-no-data-statement">{{ $t('no_data') }}</span> | ||||
|    <div class="alert alert-light">{{ $t('my_tasks.description_warning') }}</div> | ||||
|    <span v-if="noResultsAlert" class="chill-no-data-statement">{{ $t('no_data') }}</span> | ||||
|    <tab-table v-else> | ||||
|       <template v-slot:thead> | ||||
|          <th scope="col">{{ $t('warning_date') }}</th> | ||||
|          <th scope="col">{{ $t('max_date') }}</th> | ||||
|          <th scope="col">{{ $t('task') }}</th> | ||||
|          <th scope="col">id</th> | ||||
|          <th scope="col"></th> | ||||
|       </template> | ||||
|       <template v-slot:tbody> | ||||
|          <tr v-for="(t, i) in tasks.warning.results" :key="`task-warning-${i}`"> | ||||
|             <td> | ||||
|                <span class="outdated">{{ $d(t.warningDate.datetime, 'short') }}</span> | ||||
|             </td> | ||||
|             <td>{{ $d(t.endDate.datetime, 'short') }}</td> | ||||
|             <td>{{ t.title }}</td> | ||||
|          <tr v-for="(t, i) in tasks.alert" :key="`task-alert-${i}`"> | ||||
|             <td>{{ t.id}}</td> | ||||
|             <td> | ||||
|                <a class="btn btn-sm btn-show" :href="getUrl(t)"> | ||||
|                   {{ $t('show_entity', { entity: $t('the_task') }) }} | ||||
| @@ -93,8 +81,5 @@ export default { | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| span.outdated { | ||||
|    font-weight: bold; | ||||
|    color: var(--bs-warning); | ||||
| } | ||||
|  | ||||
| </style> | ||||
| @@ -6,18 +6,12 @@ | ||||
|       <template v-slot:thead> | ||||
|          <th scope="col">{{ $t('StartDate') }}</th> | ||||
|          <th scope="col">{{ $t('SocialAction') }}</th> | ||||
|          <th scope="col">{{ $t('concerned_persons') }}</th> | ||||
|          <th scope="col"></th> | ||||
|       </template> | ||||
|       <template v-slot:tbody> | ||||
|          <tr v-for="(w, i) in works.results" :key="`works-${i}`"> | ||||
|             <td>{{ $d(w.startDate.datetime, 'short') }}</td> | ||||
|             <td> | ||||
|                <span class="chill-entity entity-social-issue"> | ||||
|                   <span class="badge bg-chill-l-gray text-dark"> | ||||
|                      {{ w.socialAction.issue.text }} | ||||
|                   </span> | ||||
|                </span> | ||||
|                <h4 class="badge-title"> | ||||
|                   <span class="title_label"></span> | ||||
|                   <span class="title_action"> | ||||
| @@ -26,18 +20,7 @@ | ||||
|                </h4> | ||||
|             </td> | ||||
|             <td> | ||||
|                <span v-for="person in w.persons" class="me-1" :key="person.id"> | ||||
|                   <on-the-fly | ||||
|                      :type="person.type" | ||||
|                      :id="person.id" | ||||
|                      :buttonText="person.textAge" | ||||
|                      :displayBadge="'true' === 'true'" | ||||
|                      action="show"> | ||||
|                   </on-the-fly> | ||||
|                </span> | ||||
|             </td> | ||||
|             <td> | ||||
|                <div class="btn-group-vertical" role="group" aria-label="Actions"> | ||||
|                <div class="btn-group" role="group" aria-label="Actions"> | ||||
|                   <a class="btn btn-sm btn-update" :href="getUrl(w)"> | ||||
|                      {{ $t('show_entity', { entity: $t('the_action') }) }} | ||||
|                   </a> | ||||
| @@ -55,13 +38,11 @@ | ||||
| <script> | ||||
| import { mapState, mapGetters } from "vuex"; | ||||
| import TabTable from "./TabTable"; | ||||
| import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly'; | ||||
|  | ||||
| export default { | ||||
|    name: "MyWorks", | ||||
|    components: { | ||||
|       TabTable, | ||||
|       OnTheFly, | ||||
|       TabTable | ||||
|    }, | ||||
|    computed: { | ||||
|       ...mapState([ | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <template> | ||||
|    <span v-if="isCounterAvailable" | ||||
|          class="badge rounded-pill bg-danger"> | ||||
|          class="badge rounded-pill bg-danger counter"> | ||||
|       {{ count }} | ||||
|    </span> | ||||
| </template> | ||||
|   | ||||
| @@ -22,15 +22,8 @@ const appMessages = { | ||||
|             tab: "Mes notifications", | ||||
|             description: "Liste des notifications reçues et non lues.", | ||||
|         }, | ||||
|         opening_date: "Date d'ouverture", | ||||
|         social_issues: "Problématiques sociales", | ||||
|         concerned_persons: "Usagers concernés", | ||||
|         max_date: "Date d'échéance", | ||||
|         warning_date: "Date de rappel", | ||||
|         evaluation: "Évaluation", | ||||
|         task: "Tâche", | ||||
|         Date: "Date", | ||||
|         From: "Expéditeur", | ||||
|         From: "De", | ||||
|         Subject: "Objet", | ||||
|         Entity: "Associé à", | ||||
|         show_entity: "Voir {entity}", | ||||
| @@ -39,22 +32,18 @@ const appMessages = { | ||||
|         the_action: "l'action", | ||||
|         the_evaluation: "l'évaluation", | ||||
|         the_task: "la tâche", | ||||
|         workflow: "le workflow", | ||||
|         StartDate: "Date d'ouverture", | ||||
|         SocialAction: "Action d'accompagnement", | ||||
|         no_data: "Aucun résultats", | ||||
|         no_dashboard: "Pas de tableaux de bord", | ||||
|         counter: { | ||||
|             unread_notifications: "{n} notification non lue | {n} notifications non lues", | ||||
|             assignated_courses: "{n} parcours récent assigné | {n} parcours récents assignés", | ||||
|             assignated_actions: "{n} action assignée | {n} actions assignées", | ||||
|             assignated_evaluations: "{n} évaluation assignée | {n} évaluations assignées", | ||||
|             alert_tasks: "{n} tâche en rappel | {n} tâches en rappel", | ||||
|             warning_tasks: "{n} tâche à échéance | {n} tâches à échéance", | ||||
|         }, | ||||
|         emergency: "Urgent", | ||||
|         confidential: "Confidentiel", | ||||
|         automatic_notification: "Notification automatique" | ||||
|             unread_notifications: "notifications non lues", | ||||
|             assignated_courses: "parcours récents assignés", | ||||
|             assignated_actions: "actions assignées", | ||||
|             assignated_evaluations: "évaluations assignées", | ||||
|             alert_tasks: "tâches en rappel", | ||||
|             warning_tasks: "tâches à échéances", | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -62,27 +62,27 @@ const store = createStore({ | ||||
|     }, | ||||
|     mutations: { | ||||
|         addWorks(state, works) { | ||||
|             //console.log('addWorks', works); | ||||
|             console.log('addWorks', works); | ||||
|             state.works = works; | ||||
|         }, | ||||
|         addEvaluations(state, evaluations) { | ||||
|             //console.log('addEvaluations', evaluations); | ||||
|             console.log('addEvaluations', evaluations); | ||||
|             state.evaluations = evaluations; | ||||
|         }, | ||||
|         addTasksWarning(state, tasks) { | ||||
|             //console.log('addTasksWarning', tasks); | ||||
|             console.log('addTasksWarning', tasks); | ||||
|             state.tasks.warning = tasks; | ||||
|         }, | ||||
|         addTasksAlert(state, tasks) { | ||||
|             //console.log('addTasksAlert', tasks); | ||||
|             console.log('addTasksAlert', tasks); | ||||
|             state.tasks.alert = tasks; | ||||
|         }, | ||||
|         addCourses(state, courses) { | ||||
|             //console.log('addCourses', courses); | ||||
|             console.log('addCourses', courses); | ||||
|             state.accompanyingCourses = courses; | ||||
|         }, | ||||
|         addNotifications(state, notifications) { | ||||
|             //console.log('addNotifications', notifications); | ||||
|             console.log('addNotifications', notifications); | ||||
|             state.notifications = notifications; | ||||
|         }, | ||||
|         setLoading(state, bool) { | ||||
|   | ||||
| @@ -90,7 +90,7 @@ export default { | ||||
|       OnTheFlyThirdparty, | ||||
|       OnTheFlyCreate | ||||
|    }, | ||||
|    props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'isDead', 'parent'], | ||||
|    props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'isDead', 'parent', 'canCloseModal'], | ||||
|    emits: ['saveFormOnTheFly'], | ||||
|    data() { | ||||
|       return { | ||||
| @@ -160,10 +160,17 @@ export default { | ||||
|       }, | ||||
|       badgeType() { | ||||
|          return 'entity-' + this.type + ' badge-' + this.type; | ||||
|       }, | ||||
|       getReturnPath() { | ||||
|          return `?returnPath=${window.location.pathname}${window.location.search}${window.location.hash}`; | ||||
|       }, | ||||
|       } | ||||
|    }, | ||||
|    watch: { | ||||
|       canCloseModal: { | ||||
|          handler: function(val, oldVal) { | ||||
|             if (val) { | ||||
|                this.closeModal(); | ||||
|             } | ||||
|          }, | ||||
|          deep: true | ||||
|       } | ||||
|    }, | ||||
|    methods: { | ||||
|       closeModal() { | ||||
| @@ -210,9 +217,9 @@ export default { | ||||
|       buildLocation(id, type) { | ||||
|          if (type === 'person') { | ||||
|             // TODO i18n | ||||
|             return encodeURI(`/fr/person/${id}/general${this.getReturnPath}`); | ||||
|             return `/fr/person/${id}/general`; | ||||
|          } else if (type === 'thirdparty') { | ||||
|             return encodeURI(`/fr/3party/3party/${id}/view${this.getReturnPath}`); | ||||
|             return `/fr/3party/3party/${id}/view`; | ||||
|          } | ||||
|       } | ||||
|    } | ||||
|   | ||||
| @@ -5,8 +5,8 @@ | ||||
|         </li> | ||||
|     </ul> | ||||
|     <ul class="record_actions"> | ||||
|         <li class="add-persons"> | ||||
|             <add-persons | ||||
|         <li> | ||||
|             <AddPersons | ||||
|                 :options="addPersonsOptions" | ||||
|                 :key="uniqid" | ||||
|                 :buttonTitle="translatedListOfTypes" | ||||
| @@ -14,7 +14,7 @@ | ||||
|                 ref="addPersons" | ||||
|                 @addNewPersons="addNewEntity" | ||||
|             > | ||||
|             </add-persons> | ||||
|             </AddPersons> | ||||
|         </li> | ||||
|     </ul> | ||||
| </template> | ||||
|   | ||||
| @@ -1,7 +1,4 @@ | ||||
| import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n'; | ||||
| import { thirdpartyMessages } from 'ChillThirdPartyAssets/vuejs/_js/i18n'; | ||||
| import { addressMessages } from 'ChillMainAssets/vuejs/Address/i18n'; | ||||
| import { ontheflyMessages } from 'ChillMainAssets/vuejs/OnTheFly/i18n'; | ||||
|  | ||||
| const appMessages = { | ||||
|     fr: { | ||||
| @@ -15,6 +12,6 @@ const appMessages = { | ||||
|     } | ||||
| } | ||||
|  | ||||
| Object.assign(appMessages.fr, personMessages.fr, thirdpartyMessages.fr, addressMessages.fr, ontheflyMessages.fr ); | ||||
| Object.assign(appMessages.fr, personMessages.fr); | ||||
|  | ||||
| export { appMessages }; | ||||
|   | ||||
| @@ -42,11 +42,57 @@ | ||||
|                   class="street"> | ||||
|                   {{ address.text }} | ||||
|                </p> | ||||
|                <p v-if="address.postcode" | ||||
|                   class="postcode"> | ||||
|                   {{ address.postcode.code }} {{ address.postcode.name }} | ||||
|                </p> | ||||
|                <p v-if="address.country" | ||||
|                   class="country"> | ||||
|                   {{ address.country.name.fr }} | ||||
|                </p> | ||||
|             </div> | ||||
|          </div> | ||||
|  | ||||
|       </component> | ||||
|  | ||||
|       <!-- <div v-if="isMultiline === true" class="address-more"> | ||||
|          <div v-if="address.floor"> | ||||
|             <span class="floor"> | ||||
|                <b>{{ $t('floor') }}</b>: {{ address.floor }} | ||||
|             </span> | ||||
|          </div> | ||||
|          <div v-if="address.corridor"> | ||||
|             <span class="corridor"> | ||||
|                <b>{{ $t('corridor') }}</b>: {{ address.corridor }} | ||||
|             </span> | ||||
|          </div> | ||||
|          <div v-if="address.steps"> | ||||
|             <span class="steps"> | ||||
|                <b>{{ $t('steps') }}</b>: {{ address.steps }} | ||||
|             </span> | ||||
|          </div> | ||||
|          <div v-if="address.flat"> | ||||
|             <span class="flat"> | ||||
|                <b>{{ $t('flat') }}</b>: {{ address.flat }} | ||||
|             </span> | ||||
|          </div> | ||||
|          <div v-if="address.buildingName"> | ||||
|                <span class="buildingName"> | ||||
|                   <b>{{ $t('buildingName') }}</b>: {{ address.buildingName }} | ||||
|                </span> | ||||
|          </div> | ||||
|          <div v-if="address.extra"> | ||||
|                <span class="extra"> | ||||
|                   <b>{{ $t('extra') }}</b>: {{ address.extra }} | ||||
|                </span> | ||||
|          </div> | ||||
|          <div v-if="address.distribution"> | ||||
|                <span class="distribution"> | ||||
|                   <b>{{ $t('distribution') }}</b>: {{ address.distribution }} | ||||
|                </span> | ||||
|          </div> | ||||
|       </div> --> | ||||
|  | ||||
|       <div v-if="useDatePane === true" class="address-more"> | ||||
|          <div v-if="address.validFrom"> | ||||
|             <span class="validFrom"> | ||||
|   | ||||
| @@ -93,9 +93,8 @@ export default { | ||||
|       }, | ||||
|       getPopTitle(step) { | ||||
|          if (step.transitionPrevious != null) { | ||||
|             //console.log(step.transitionPrevious.text); | ||||
|             let freezed = step.isFreezed ? `<i class="fa fa-snowflake-o fa-sm me-1"></i>` : ``; | ||||
|             return `${freezed}${step.transitionPrevious.text}`; | ||||
|             return `${freezed}${step.currentStep.text}`; | ||||
|          } | ||||
|       }, | ||||
|       getPopContent(step) { | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
|  | ||||
|     | ||||
|    <button v-if="hasWorkflow" | ||||
|       class="btn btn-primary" | ||||
|       @click="openModal"> | ||||
| @@ -7,39 +7,36 @@ | ||||
|       <template v-if="countWorkflows > 1">{{ $t('workflows') }}</template> | ||||
|       <template v-else>{{ $t('workflow') }}</template> | ||||
|    </button> | ||||
|  | ||||
|     | ||||
|    <pick-workflow v-else-if="allowCreate" | ||||
|       :relatedEntityClass="this.relatedEntityClass" | ||||
|       :relatedEntityId="this.relatedEntityId" | ||||
|       :workflowsAvailables="workflowsAvailables" | ||||
|       @go-to-generate-workflow="goToGenerateWorkflow" | ||||
|    ></pick-workflow> | ||||
|  | ||||
|     | ||||
|    <teleport to="body"> | ||||
|       <modal v-if="modal.showModal" | ||||
|          :modalDialogClass="modal.modalDialogClass" | ||||
|          @close="modal.showModal = false"> | ||||
|  | ||||
|           | ||||
|          <template v-slot:header> | ||||
|             <h2 class="modal-title">{{ $t('workflow_list') }}</h2> | ||||
|          </template> | ||||
|  | ||||
|           | ||||
|          <template v-slot:body> | ||||
|             <list-workflow-vue | ||||
|                :workflows="workflows" | ||||
|             ></list-workflow-vue> | ||||
|          </template> | ||||
|  | ||||
|           | ||||
|          <template v-slot:footer> | ||||
|             <pick-workflow v-if="allowCreate" | ||||
|                :relatedEntityClass="this.relatedEntityClass" | ||||
|                :relatedEntityId="this.relatedEntityId" | ||||
|                :workflowsAvailables="workflowsAvailables" | ||||
|                :preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate" | ||||
|                @go-to-generate-workflow="this.goToGenerateWorkflow" | ||||
|             ></pick-workflow> | ||||
|          </template> | ||||
|  | ||||
|           | ||||
|       </modal> | ||||
|    </teleport> | ||||
| </template> | ||||
| @@ -56,7 +53,6 @@ export default { | ||||
|       PickWorkflow, | ||||
|       ListWorkflowVue | ||||
|    }, | ||||
|    emits: ['goToGenerateWorkflow'], | ||||
|    props: { | ||||
|       workflows: { | ||||
|          type: Array, | ||||
| @@ -77,14 +73,9 @@ export default { | ||||
|       workflowsAvailables: { | ||||
|          type: Array, | ||||
|          required: true, | ||||
|       }, | ||||
|       preventDefaultMoveToGenerate: { | ||||
|           type: Boolean, | ||||
|           required: false, | ||||
|           default: false, | ||||
|       }, | ||||
|       } | ||||
|    }, | ||||
|     data() { | ||||
|    data() { | ||||
|       return { | ||||
|          modal: { | ||||
|             showModal: false, | ||||
| @@ -104,10 +95,6 @@ export default { | ||||
|       openModal() { | ||||
|          this.modal.showModal = true; | ||||
|       }, | ||||
|       goToGenerateWorkflow(data) { | ||||
|           console.log('go to generate workflow intercepted'); | ||||
|           this.$emit('goToGenerateWorkflow', data); | ||||
|       } | ||||
|    }, | ||||
|    i18n: { | ||||
|       messages: { | ||||
| @@ -121,4 +108,4 @@ export default { | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped></style> | ||||
| <style scoped></style> | ||||
| @@ -6,7 +6,7 @@ | ||||
|             </button> | ||||
|             <ul class="dropdown-menu" aria-labelledby="createWorkflowButton"> | ||||
|                 <li v-for="w in workflowsAvailables" :key="w.name"> | ||||
|                     <a class="dropdown-item" :href="makeLink(w.name)" @click.prevent="goToGenerateWorkflow($event, w.name)">{{ w.text }}</a> | ||||
|                     <a class="dropdown-item" :href="makeLink(w.name)" @click="goToGenerateWorkflow($event, w.name)">{{ w.text }}</a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
| @@ -31,12 +31,7 @@ export default { | ||||
|         workflowsAvailables: { | ||||
|             type: Array, | ||||
|             required: true, | ||||
|         }, | ||||
|         preventDefaultMoveToGenerate: { | ||||
|             type: Boolean, | ||||
|             required: false, | ||||
|             default: false, | ||||
|         }, | ||||
|         } | ||||
|     }, | ||||
|     emits: ['goToGenerateWorkflow'], | ||||
|     methods: { | ||||
| @@ -44,13 +39,6 @@ export default { | ||||
|             return buildLinkCreate(workflowName, this.relatedEntityClass, this.relatedEntityId); | ||||
|         }, | ||||
|         goToGenerateWorkflow(event, workflowName) { | ||||
|             console.log('goToGenerateWorkflow', event, workflowName); | ||||
|  | ||||
|             if (!this.$props.preventDefaultMoveToGenerate) { | ||||
|                 console.log('to go generate'); | ||||
|                 window.location.assign(this.makeLink(workflowName)); | ||||
|             } | ||||
|  | ||||
|             this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName)}); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,14 +1,12 @@ | ||||
| <template> | ||||
|    <a v-if="isOpenDocument" | ||||
|       class="btn" :class="[ | ||||
|          isChangeIcon ? 'change-icon' : '', | ||||
|          isChangeClass ? options.changeClass : 'btn-wopilink' ]" | ||||
|       class="btn change-icon" :class="[isChangeClass ? button.changeClass : 'btn-edit']" | ||||
|       @click="openModal"> | ||||
|        | ||||
|       <i v-if="isChangeIcon" class="fa me-2" :class="options.changeIcon"></i> | ||||
|       <i class="fa me-2" :class="[isChangeIcon ? button.changeIcon : 'fa-pencil']"></i> | ||||
|        | ||||
|       <span v-if="!noText"> | ||||
|          {{ $t('online_edit_document') }} | ||||
|          {{ $t('Update_document') }} | ||||
|       </span> | ||||
|    </a> | ||||
|     | ||||
| @@ -22,14 +20,12 @@ | ||||
|             <template v-slot:header> | ||||
|                <img class="logo" :src="logo" height="45"/> | ||||
|                   <span class="ms-auto me-3"> | ||||
|                      <span v-if="options.title">{{ options.title }}</span> | ||||
|                      {{ this.title }} | ||||
|                   </span> | ||||
|                   <!-- | ||||
|                   <a class="btn btn-outline-light"> | ||||
|                      <i class="fa fa-save fa-fw"></i> | ||||
|                      {{ $t('save_and_quit') }} | ||||
|                   </a> | ||||
|                   --> | ||||
|             </template> | ||||
|              | ||||
|             <template v-slot:body> | ||||
| @@ -75,11 +71,15 @@ export default { | ||||
|          type: String, | ||||
|          required: true | ||||
|       }, | ||||
|       title: { | ||||
|          type: String, | ||||
|          required: true | ||||
|       }, | ||||
|       type: { | ||||
|          type: String, | ||||
|          required: true | ||||
|       }, | ||||
|       options: { | ||||
|       button: { | ||||
|          type: Object, | ||||
|          required: false | ||||
|       } | ||||
| @@ -175,20 +175,20 @@ export default { | ||||
|          return false; | ||||
|       }, | ||||
|       noText() { | ||||
|          if (typeof this.options.noText !== 'undefined') { | ||||
|             return this.options.noText === true; | ||||
|          if (typeof this.button.noText !== 'undefined') { | ||||
|             return this.button.noText === true; | ||||
|          } | ||||
|          return false; | ||||
|       }, | ||||
|       isChangeIcon() { | ||||
|          if (typeof this.options.changeIcon !== 'undefined') { | ||||
|             return (!(this.options.changeIcon === null || this.options.changeIcon === '')) | ||||
|          if (typeof this.button.changeIcon !== 'undefined') { | ||||
|             return (!(this.button.changeIcon === null || this.button.changeIcon === '')) | ||||
|          } | ||||
|          return false; | ||||
|       }, | ||||
|       isChangeClass() { | ||||
|          if (typeof this.options.changeClass !== 'undefined') { | ||||
|             return (!(this.options.changeClass === null || this.options.changeClass === '')) | ||||
|          if (typeof this.button.changeClass !== 'undefined') { | ||||
|             return (!(this.button.changeClass === null || this.button.changeClass === '')) | ||||
|          } | ||||
|          return false; | ||||
|       } | ||||
| @@ -205,7 +205,7 @@ export default { | ||||
|    i18n: { | ||||
|       messages: { | ||||
|          fr: { | ||||
|             online_edit_document: "Éditer en ligne", | ||||
|             Update_document: "Modifier le document", | ||||
|             save_and_quit: "Enregistrer et quitter", | ||||
|             loading: "Chargement de l'éditeur en ligne", | ||||
|             invalid_title: "Format incompatible", | ||||
|   | ||||
| @@ -91,11 +91,11 @@ export const multiSelectMessages = { | ||||
|     multiselect: { | ||||
|       placeholder: 'Choisir', | ||||
|       tag_placeholder: 'Créer un nouvel élément', | ||||
|       select_label: '"Entrée" ou cliquez pour sélectionner', | ||||
|       deselect_label: '"Entrée" ou cliquez pour désélectionner', | ||||
|       select_label: 'Appuyer sur "Entrée" pour sélectionner', | ||||
|       deselect_label: 'Appuyer sur "Entrée" pour désélectionner', | ||||
|       select_group_label: 'Appuyer sur "Entrée" pour sélectionner ce groupe', | ||||
|       deselect_group_label: 'Appuyer sur "Entrée" pour désélectionner ce groupe', | ||||
|       selected_label: 'Sélectionné' | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| }; | ||||
| @@ -15,11 +15,11 @@ | ||||
|  | ||||
| <div class="notification-comment-list my-5"> | ||||
|     <h2 class="chill-blue">{{ 'notification.comments_list'|trans }}</h2> | ||||
|  | ||||
|      | ||||
|     {% if notification.comments|length > 0 %} | ||||
|         <div class="flex-table"> | ||||
|             {% for comment in notification.comments %} | ||||
|  | ||||
|                  | ||||
|                 {% if editedCommentForm is null or editedCommentId != comment.id %} | ||||
|                     {{ m.show_comment(comment, { | ||||
|                         'recordAction': _self.recordAction(comment) | ||||
| @@ -28,11 +28,10 @@ | ||||
|                     <div class="item-bloc"> | ||||
|                         <div class="item-row row"> | ||||
|                             <a id="comment-{{ comment.id }}"></a> | ||||
|  | ||||
|                              | ||||
|                             {{ form_start(editedCommentForm) }} | ||||
|                             {{ form_errors(editedCommentForm) }} | ||||
|                             {{ form_widget(editedCommentForm.content) }} | ||||
|                             {{ form_errors(editedCommentForm.content) }} | ||||
|                             <input type="hidden" name="form" value="edit" /> | ||||
|                             <ul class="record_actions"> | ||||
|                                 <li class="cancel"> | ||||
| @@ -47,25 +46,24 @@ | ||||
|                                 </li> | ||||
|                             </ul> | ||||
|                             {{ form_end(editedCommentForm) }} | ||||
|  | ||||
|                          | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {% endif %} | ||||
|  | ||||
|              | ||||
|             {% endfor %} | ||||
|         </div> | ||||
|     {% else %} | ||||
|         <span class="chill-no-data-statement">{{ 'No comments'|trans }}</span> | ||||
|     {% endif %} | ||||
|  | ||||
|      | ||||
|     {% if appendCommentForm is not null %} | ||||
|         <div class="new-comment my-5"> | ||||
|             <h2 class="chill-blue mb-4">{{ 'Write a new comment'|trans }}</h2> | ||||
|  | ||||
|              | ||||
|             {{ form_start(appendCommentForm) }} | ||||
|             {{ form_errors(appendCommentForm) }} | ||||
|             {{ form_widget(appendCommentForm.content) }} | ||||
|             {{ form_errors(appendCommentForm.content) }} | ||||
|             <input type="hidden" name="form" value="append" /> | ||||
|             <ul class="record_actions"> | ||||
|                 <li> | ||||
| @@ -73,7 +71,7 @@ | ||||
|                 </li> | ||||
|             </ul> | ||||
|             {{ form_end(appendCommentForm) }} | ||||
|  | ||||
|          | ||||
|         </div> | ||||
|     {% endif %} | ||||
| </div> | ||||
| </div> | ||||
| @@ -72,13 +72,10 @@ | ||||
|         <div class="item-row separator"> | ||||
|             <div class="item-col item-meta"> | ||||
|                  | ||||
|                 {% if c.notification.comments|length > 0 %} | ||||
|                     <div class="comment-counter"> | ||||
|                         <span class="counter"> | ||||
|                             {{ 'notification.counter comments'|trans({'nb': c.notification.comments|length }) }} | ||||
|                         </span> | ||||
|                     </div> | ||||
|                 {% endif %} | ||||
|                 {# TODO twig extension to count comments #} | ||||
|                 <div class="comment-counter visually-hidden"> | ||||
|                     <span>x commentaires</span> | ||||
|                 </div> | ||||
|                  | ||||
|             </div> | ||||
|             <div class="item-col"> | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| <div class="notification-counter"> | ||||
|     {% if counter.total > 0 %} | ||||
|         <span class="counter"> | ||||
|         <span> | ||||
|             {{ 'notification.counter total notifications'|trans({'total': counter.total }) }} | ||||
|         </span> | ||||
|     {% endif %} | ||||
|     {% if counter.unread > 0 %} | ||||
|         <span class="counter"> | ||||
|         <span> | ||||
|             {{ 'notification.counter unread notifications'|trans({'unread': counter.unread }) }} | ||||
|         </span> | ||||
|     {% endif %} | ||||
|   | ||||
| @@ -3,58 +3,7 @@ | ||||
| {% if transition_form is not null %} | ||||
|     {{ form_start(transition_form) }} | ||||
|  | ||||
|     {% set step = entity_workflow.currentStepChained %} | ||||
|     {% set labels = workflow_metadata(entity_workflow, 'label', step.currentStep) %} | ||||
|     {% set label = labels is null ? step.currentStep : labels|localize_translatable_string %} | ||||
|  | ||||
|     <div class="card"> | ||||
|         <div class="card-body"> | ||||
|             <div class="row"> | ||||
|                 <div class="col-sm-12"> | ||||
|                     {{ 'workflow.Current step'|trans }} : | ||||
|                     <span class="badge bg-primary">{{ label }}</span> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
|             {% if step.previous is not null %} | ||||
|                 {% if step.previous.comment is not empty %} | ||||
|                     <div class="row"> | ||||
|                         <div class="col-sm-12"> | ||||
|                             <blockquote class="chill-user-quote"> | ||||
|                                 {{ step.previous.comment|chill_markdown_to_html }} | ||||
|                             </blockquote> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {% endif %} | ||||
|                 <div class="row"> | ||||
|                     <div class="col-sm-12"> | ||||
|                         {{ 'By'|trans }} | ||||
|                         {{ step.previous.transitionBy|chill_entity_render_box }}, | ||||
|                         {{ step.previous.transitionAt|format_datetime('short', 'short') }} | ||||
|                     </div> | ||||
|                 </div> | ||||
|             {% else %} | ||||
|                 <div class="row"> | ||||
|                     <div class="col-sm-4">{{ 'workflow.Created by'|trans }}</div> | ||||
|                     <div class="col-sm-8">{{ step.entityWorkflow.createdBy|chill_entity_render_box }}</div> | ||||
|                 </div> | ||||
|                 <div class="row"> | ||||
|                     <div class="col-sm-4">{{ 'Le'|trans }}</div> | ||||
|                     <div class="col-sm-8">{{ step.entityWorkflow.createdAt|format_datetime('short', 'short') }}</div> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div id="transitionFilter"> | ||||
|         {% if transition_form.transitionFilter is defined %} | ||||
|             {{ form_row(transition_form.transitionFilter) }} | ||||
|         {% endif %} | ||||
|     </div> | ||||
|  | ||||
|     <div id="transitions"> | ||||
|         {{ form_row(transition_form.transition) }} | ||||
|     </div> | ||||
|     {{ form_row(transition_form.transition) }} | ||||
|  | ||||
|     {% if transition_form.freezeAfter is defined %} | ||||
|         {{ form_row(transition_form.freezeAfter) }} | ||||
|   | ||||
| @@ -69,18 +69,6 @@ | ||||
|                     </blockquote> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|             {% if loop.last and step.destUser|length > 0 %} | ||||
|                 <div class="item-row separator"> | ||||
|                     <div> | ||||
|                         <p><b>{{ 'workflow.Users allowed to apply transition'|trans }} : </b></p> | ||||
|                         <ul> | ||||
|                             {% for u in step.destUser %} | ||||
|                                 <li>{{ u|chill_entity_render_box }}</li> | ||||
|                             {% endfor %} | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|  | ||||
|     {% endfor %} | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|     <h2> | ||||
|         {{ 'workflow_'|trans }} | ||||
|     </h2> | ||||
|     {% include handler.templateTitle(entity_workflow) with handler.templateTitleData(entity_workflow)|merge({ | ||||
|     {% include handler.templateTitle(l.entity_workflow) with handler.templateTitleData(entity_workflow)|merge({ | ||||
|         'description': true, | ||||
|         'breadcrumb': true, | ||||
|         'add_classes': 'ms-3 h3' | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| {% block js %} | ||||
|     {{ parent() }} | ||||
|      | ||||
|     {{ encore_entry_script_tags('mod_async_upload') }} | ||||
|     {{ encore_entry_script_tags('mod_pickentity_type') }} | ||||
|     {{ encore_entry_script_tags('mod_entity_workflow_subscribe') }} | ||||
|   | ||||
| @@ -14,7 +14,6 @@ namespace Chill\MainBundle\Routing\MenuBuilder; | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Chill\MainBundle\Notification\Counter\NotificationByUserCounter; | ||||
| use Chill\MainBundle\Routing\LocalMenuBuilderInterface; | ||||
| use Chill\MainBundle\Workflow\Counter\WorkflowByUserCounter; | ||||
| use Symfony\Component\Security\Core\Security; | ||||
| use Symfony\Contracts\Translation\TranslatorInterface; | ||||
|  | ||||
| @@ -26,16 +25,12 @@ class UserMenuBuilder implements LocalMenuBuilderInterface | ||||
|  | ||||
|     private TranslatorInterface $translator; | ||||
|  | ||||
|     private WorkflowByUserCounter $workflowByUserCounter; | ||||
|  | ||||
|     public function __construct( | ||||
|         NotificationByUserCounter $notificationByUserCounter, | ||||
|         WorkflowByUserCounter $workflowByUserCounter, | ||||
|         Security $security, | ||||
|         TranslatorInterface $translator | ||||
|     ) { | ||||
|         $this->notificationByUserCounter = $notificationByUserCounter; | ||||
|         $this->workflowByUserCounter = $workflowByUserCounter; | ||||
|         $this->security = $security; | ||||
|         $this->translator = $translator; | ||||
|     } | ||||
| @@ -74,11 +69,9 @@ class UserMenuBuilder implements LocalMenuBuilderInterface | ||||
|                     'counter' => $nbNotifications, | ||||
|                 ]); | ||||
|  | ||||
|             $workflowCount = $this->workflowByUserCounter->getCountUnreadByUser($user); | ||||
|  | ||||
|             $menu | ||||
|                 ->addChild( | ||||
|                     $this->translator->trans('workflow.My workflows with counter', ['wc' => $workflowCount]), | ||||
|                     $this->translator->trans('workflow.My workflows'), | ||||
|                     ['route' => 'chill_main_workflow_list_dest'] | ||||
|                 ) | ||||
|                 ->setExtras([ | ||||
|   | ||||
| @@ -64,7 +64,7 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw | ||||
|         if ($address instanceof Address) { | ||||
|             $data = [ | ||||
|                 'address_id' => $address->getId(), | ||||
|                 'text' => $this->addressRender->renderString($address, []), | ||||
|                 'text' => $address->isNoAddress() ? null : $this->addressRender->renderStreetLine($address, []), | ||||
|                 'street' => $address->getStreet(), | ||||
|                 'streetNumber' => $address->getStreetNumber(), | ||||
|                 'postcode' => [ | ||||
|   | ||||
| @@ -12,7 +12,6 @@ declare(strict_types=1); | ||||
| namespace Chill\MainBundle\Serializer\Normalizer; | ||||
|  | ||||
| use LogicException; | ||||
| use Symfony\Component\Serializer\Exception\NotNormalizableValueException; | ||||
| use Symfony\Component\Serializer\Exception\RuntimeException; | ||||
| use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface; | ||||
| use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; | ||||
| @@ -45,7 +44,7 @@ class DiscriminatedObjectDenormalizer implements ContextAwareDenormalizerInterfa | ||||
|             if ($this->denormalizer->supportsDenormalization($data, $localType, $format)) { | ||||
|                 try { | ||||
|                     return $this->denormalizer->denormalize($data, $localType, $format, $context); | ||||
|                 } catch (RuntimeException|NotNormalizableValueException $e) { | ||||
|                 } catch (RuntimeException $e) { | ||||
|                     $lastException = $e; | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -44,6 +44,7 @@ class NotificationNormalizer implements NormalizerAwareInterface, NormalizerInte | ||||
|      */ | ||||
|     public function normalize($object, ?string $format = null, array $context = []) | ||||
|     { | ||||
|         dump($object); | ||||
|         $entity = $this->entityManager | ||||
|             ->getRepository($object->getRelatedEntityClass()) | ||||
|             ->find($object->getRelatedEntityId()); | ||||
|   | ||||
| @@ -124,7 +124,7 @@ class AddressRender implements ChillEntityRenderInterface | ||||
|      */ | ||||
|     public function renderString($addr, array $options): string | ||||
|     { | ||||
|         return implode(' — ', $this->renderLines($addr)); | ||||
|         return implode(' - ', $this->renderLines($addr)); | ||||
|     } | ||||
|  | ||||
|     public function supports($entity, array $options): bool | ||||
|   | ||||
| @@ -24,30 +24,30 @@ final class EntityWorkflowTest extends TestCase | ||||
|     { | ||||
|         $entityWorkflow = new EntityWorkflow(); | ||||
|  | ||||
|         $entityWorkflow->getCurrentStep()->setFinalizeAfter(true); | ||||
|         $entityWorkflow->setStep('final'); | ||||
|         $entityWorkflow->getCurrentStep()->setIsFinal(true); | ||||
|  | ||||
|         $this->assertTrue($entityWorkflow->isFinal()); | ||||
|         $this->assertTrue($entityWorkflow->isFinalize()); | ||||
|     } | ||||
|  | ||||
|     public function testIsFinalizeWith4Steps() | ||||
|     { | ||||
|         $entityWorkflow = new EntityWorkflow(); | ||||
|  | ||||
|         $this->assertFalse($entityWorkflow->isFinal()); | ||||
|         $this->assertFalse($entityWorkflow->isFinalize()); | ||||
|  | ||||
|         $entityWorkflow->setStep('two'); | ||||
|  | ||||
|         $this->assertFalse($entityWorkflow->isFinal()); | ||||
|         $this->assertFalse($entityWorkflow->isFinalize()); | ||||
|  | ||||
|         $entityWorkflow->setStep('previous_final'); | ||||
|  | ||||
|         $this->assertFalse($entityWorkflow->isFinal()); | ||||
|         $this->assertFalse($entityWorkflow->isFinalize()); | ||||
|  | ||||
|         $entityWorkflow->getCurrentStep()->setIsFinal(true); | ||||
|         $entityWorkflow->getCurrentStep()->setFinalizeAfter(true); | ||||
|         $entityWorkflow->setStep('final'); | ||||
|  | ||||
|         $this->assertTrue($entityWorkflow->isFinal()); | ||||
|         $this->assertTrue($entityWorkflow->isFinalize()); | ||||
|     } | ||||
|  | ||||
|     public function testIsFreeze() | ||||
| @@ -64,9 +64,12 @@ final class EntityWorkflowTest extends TestCase | ||||
|  | ||||
|         $this->assertFalse($entityWorkflow->isFreeze()); | ||||
|  | ||||
|         $entityWorkflow->setStep('freezed'); | ||||
|         $entityWorkflow->getCurrentStep()->setFreezeAfter(true); | ||||
|  | ||||
|         $this->assertFalse($entityWorkflow->isFreeze()); | ||||
|  | ||||
|         $entityWorkflow->setStep('freezed'); | ||||
|  | ||||
|         $this->assertTrue($entityWorkflow->isFreeze()); | ||||
|  | ||||
|         $entityWorkflow->setStep('after_freeze'); | ||||
|   | ||||
| @@ -48,7 +48,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|  | ||||
|         $addr->setBuildingName('Résidence "Les Bleuets"'); | ||||
|  | ||||
|         yield [$addr, 'Résidence "Les Bleuets" — Rue ABC, 5 — 012345 Locality — Belgium']; | ||||
|         yield [$addr, 'Résidence "Les Bleuets" - Rue ABC, 5 - 012345 Locality - Belgium']; | ||||
|     } | ||||
|  | ||||
|     public function addressDataProviderBEWithSteps(): Iterator | ||||
| @@ -68,7 +68,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|  | ||||
|         $addr->setSteps('4'); | ||||
|  | ||||
|         yield [$addr, 'esc 4 — Rue ABC, 5 — 012345 Locality — Belgium']; | ||||
|         yield [$addr, 'esc 4 - Rue ABC, 5 - 012345 Locality - Belgium']; | ||||
|     } | ||||
|  | ||||
|     public function addressDataProviderFRWithBuilding(): Iterator | ||||
| @@ -88,7 +88,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|  | ||||
|         $addr->setBuildingName('Résidence "Les Bleuets"'); | ||||
|  | ||||
|         yield [$addr, 'Résidence "Les Bleuets" — 5, Rue ABC — 012345 Locality — France']; | ||||
|         yield [$addr, 'Résidence "Les Bleuets" - 5, Rue ABC - 012345 Locality - France']; | ||||
|     } | ||||
|  | ||||
|     public function addressDataProviderFRWithSteps(): Iterator | ||||
| @@ -108,7 +108,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|  | ||||
|         $addr->setSteps('4'); | ||||
|  | ||||
|         yield [$addr, 'esc 4 — 5, Rue ABC — 012345 Locality — France']; | ||||
|         yield [$addr, 'esc 4 - 5, Rue ABC - 012345 Locality - France']; | ||||
|     } | ||||
|  | ||||
|     public function complexAddressDataProviderBE(): Iterator | ||||
| @@ -132,7 +132,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|         $addr->setCorridor('3'); | ||||
|         $addr->setSteps('4'); | ||||
|  | ||||
|         yield [$addr, 'Résidence "Les Bleuets" - appart 1 - ét 2 - coul 3 - esc 4 — Rue ABC, 5 — 012345 Locality — Belgium']; | ||||
|         yield [$addr, 'Résidence "Les Bleuets" - appart 1 - ét 2 - coul 3 - esc 4 - Rue ABC, 5 - 012345 Locality - Belgium']; | ||||
|     } | ||||
|  | ||||
|     public function complexAddressDataProviderFR(): Iterator | ||||
| @@ -158,7 +158,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|         $addr->setExtra('A droite de la porte'); | ||||
|         $addr->setDistribution('CEDEX'); | ||||
|  | ||||
|         yield [$addr, 'appart 1 - ét 2 - coul 3 - esc 4 — Résidence "Les Bleuets" — 5, Rue ABC — A droite de la porte — 012345 Locality CEDEX — France']; | ||||
|         yield [$addr, 'appart 1 - ét 2 - coul 3 - esc 4 - Résidence "Les Bleuets" - 5, Rue ABC - A droite de la porte - 012345 Locality CEDEX - France']; | ||||
|     } | ||||
|  | ||||
|     public function noFullAddressDataProviderBE(): Iterator | ||||
| @@ -175,7 +175,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|         $addr->setPostcode($postCode) | ||||
|             ->setIsNoAddress(true); | ||||
|  | ||||
|         yield [$addr, '012345 Locality — Belgium']; | ||||
|         yield [$addr, '012345 Locality - Belgium']; | ||||
|     } | ||||
|  | ||||
|     public function simpleAddressDataProviderBE(): Iterator | ||||
| @@ -193,7 +193,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|             ->setStreetNumber('5') | ||||
|             ->setPostcode($postCode); | ||||
|  | ||||
|         yield [$addr, 'Rue ABC, 5 — 012345 Locality — Belgium']; | ||||
|         yield [$addr, 'Rue ABC, 5 - 012345 Locality - Belgium']; | ||||
|     } | ||||
|  | ||||
|     public function simpleAddressDataProviderFR(): Iterator | ||||
| @@ -211,7 +211,7 @@ final class AddressRenderTest extends KernelTestCase | ||||
|             ->setStreetNumber('5') | ||||
|             ->setPostcode($postCode); | ||||
|  | ||||
|         yield [$addr, '5, Rue ABC — 012345 Locality — France']; | ||||
|         yield [$addr, '5, Rue ABC - 012345 Locality - France']; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -1,98 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\MainBundle\Workflow\Counter; | ||||
|  | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Chill\MainBundle\Entity\Workflow\EntityWorkflow; | ||||
| use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepRepository; | ||||
| use Chill\MainBundle\Templating\UI\NotificationCounterInterface; | ||||
| use Psr\Cache\CacheItemPoolInterface; | ||||
| use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||||
| use Symfony\Component\Security\Core\User\UserInterface; | ||||
| use Symfony\Component\Workflow\Event\Event; | ||||
|  | ||||
| final class WorkflowByUserCounter implements NotificationCounterInterface, EventSubscriberInterface | ||||
| { | ||||
|     private CacheItemPoolInterface $cacheItemPool; | ||||
|  | ||||
|     private EntityWorkflowStepRepository $workflowStepRepository; | ||||
|  | ||||
|     public function __construct(EntityWorkflowStepRepository $workflowStepRepository, CacheItemPoolInterface $cacheItemPool) | ||||
|     { | ||||
|         $this->workflowStepRepository = $workflowStepRepository; | ||||
|         $this->cacheItemPool = $cacheItemPool; | ||||
|     } | ||||
|  | ||||
|     public function addNotification(UserInterface $u): int | ||||
|     { | ||||
|         if (!$u instanceof User) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         $key = self::generateCacheKeyWorkflowByUser($u); | ||||
|         $item = $this->cacheItemPool->getItem($key); | ||||
|  | ||||
|         if ($item->isHit()) { | ||||
|             return $item->get(); | ||||
|         } | ||||
|  | ||||
|         $nb = $this->getCountUnreadByUser($u); | ||||
|  | ||||
|         $item->set($nb) | ||||
|             ->expiresAfter(60 * 15); | ||||
|         $this->cacheItemPool->save($item); | ||||
|  | ||||
|         return $nb; | ||||
|     } | ||||
|  | ||||
|     public static function generateCacheKeyWorkflowByUser(User $user): string | ||||
|     { | ||||
|         return 'chill_main_workflow_by_u_' . $user->getId(); | ||||
|     } | ||||
|  | ||||
|     public function getCountUnreadByUser(User $user): int | ||||
|     { | ||||
|         return $this->workflowStepRepository->countUnreadByUser($user); | ||||
|     } | ||||
|  | ||||
|     public static function getSubscribedEvents() | ||||
|     { | ||||
|         return [ | ||||
|             'workflow.leave' => 'resetWorkflowCache', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function resetWorkflowCache(Event $event): void | ||||
|     { | ||||
|         if (!$event->getSubject() instanceof EntityWorkflow) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /** @var EntityWorkflow $entityWorkflow */ | ||||
|         $entityWorkflow = $event->getSubject(); | ||||
|         $step = $entityWorkflow->getCurrentStep(); | ||||
|  | ||||
|         if (null === $step) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $keys = []; | ||||
|  | ||||
|         foreach ($step->getDestUser() as $user) { | ||||
|             $keys[] = self::generateCacheKeyWorkflowByUser($user); | ||||
|         } | ||||
|  | ||||
|         if ([] !== $keys) { | ||||
|             $this->cacheItemPool->deleteItems($keys); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -72,18 +72,17 @@ class MetadataExtractor | ||||
|  | ||||
|         foreach ($transitions as $transition) { | ||||
|             if ($transition->getName() === $transitionName) { | ||||
|                 $metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition); | ||||
|  | ||||
|                 return [ | ||||
|                     'name' => $transition->getName(), | ||||
|                     'text' => array_key_exists('label', $metadata) ? | ||||
|                         $this->translatableStringHelper->localize($metadata['label']) : $transition->getName(), | ||||
|                     'isForward' => $metadata['isForward'] ?? null, | ||||
|                 ]; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         $metadata = $workflow->getMetadataStore()->getTransitionMetadata($transition); | ||||
|  | ||||
|         return []; | ||||
|         return [ | ||||
|             'name' => $transition->getName(), | ||||
|             'text' => array_key_exists('label', $metadata) ? | ||||
|                 $this->translatableStringHelper->localize($metadata['label']) : $transition->getName(), | ||||
|             'isForward' => $metadata['isForward'] ?? null, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function buildArrayPresentationForWorkflow(WorkflowInterface $workflow): array | ||||
|   | ||||
| @@ -23,12 +23,6 @@ class WorkflowNotificationHandler implements NotificationHandlerInterface | ||||
|  | ||||
|     private EntityWorkflowRepository $entityWorkflowRepository; | ||||
|  | ||||
|     public function __construct(EntityWorkflowRepository $entityWorkflowRepository, EntityWorkflowManager $entityWorkflowManager) | ||||
|     { | ||||
|         $this->entityWorkflowRepository = $entityWorkflowRepository; | ||||
|         $this->entityWorkflowManager = $entityWorkflowManager; | ||||
|     } | ||||
|  | ||||
|     public function getTemplate(Notification $notification, array $options = []): string | ||||
|     { | ||||
|         return '@ChillMain/Workflow/_notification_include.html.twig'; | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\MainBundle\Workflow\Validator; | ||||
|  | ||||
| use Symfony\Component\Validator\Constraint; | ||||
|  | ||||
| class StepDestValid extends Constraint | ||||
| { | ||||
|     public string $messageDestNotAllowed = 'workflow.As the step is final, no dest are allowed'; | ||||
|  | ||||
|     public string $messageRequireDest = 'workflow.As the step is not final, dest are required'; | ||||
|  | ||||
|     public function getTargets() | ||||
|     { | ||||
|         return [self::CLASS_CONSTRAINT]; | ||||
|     } | ||||
| } | ||||
| @@ -1,49 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\MainBundle\Workflow\Validator; | ||||
|  | ||||
| use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep; | ||||
| use Symfony\Component\Validator\Constraint; | ||||
| use Symfony\Component\Validator\ConstraintValidator; | ||||
| use Symfony\Component\Validator\Exception\UnexpectedTypeException; | ||||
| use Symfony\Component\Validator\Exception\UnexpectedValueException; | ||||
| use function count; | ||||
|  | ||||
| class StepDestValidValidator extends ConstraintValidator | ||||
| { | ||||
|     /** | ||||
|      * @param EntityWorkflowStep $value | ||||
|      * @param Constraint|StepDestValid $constraint | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function validate($value, Constraint $constraint) | ||||
|     { | ||||
|         if (!$constraint instanceof StepDestValid) { | ||||
|             throw new UnexpectedTypeException($constraint, StepDestValid::class); | ||||
|         } | ||||
|  | ||||
|         if (!$value instanceof EntityWorkflowStep) { | ||||
|             throw new UnexpectedValueException($value, EntityWorkflowStep::class); | ||||
|         } | ||||
|  | ||||
|         if ($value->isFinal() && 0 < count($value->getDestUser())) { | ||||
|             $this->context->buildViolation($constraint->messageDestNotAllowed) | ||||
|                 ->addViolation(); | ||||
|         } | ||||
|  | ||||
|         if (!$value->isFinal() && 0 === count($value->getDestUser())) { | ||||
|             $this->context->buildViolation($constraint->messageRequireDest) | ||||
|                 ->addViolation(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user