Compare commits

...

36 Commits

Author SHA1 Message Date
1961bf36e2 Changie added 2024-11-18 15:06:46 +01:00
52f06e2142 Translator dependencies added to chill-bundles 2024-11-18 14:57:13 +01:00
6ded75119c Add developers documentation for the correct management of translation keys 2024-11-18 13:04:27 +01:00
89fa10cede Change namespace for detect translation duplicate command 2024-11-18 13:03:47 +01:00
b0b1a28f50 Implement trans() in activity vue components 2024-10-08 13:51:41 +02:00
760f74b386 Add translation keys from i18ns to yaml files 2024-10-08 13:25:09 +02:00
d82a3af063 Adjust more translations in mainbundle create component 2024-10-01 20:05:04 +02:00
7edd644963 wip: use script setup and new translator within vue components main bundle 2024-09-25 10:13:04 +02:00
077163a774 Transfer translations from i18n file to yaml file 2024-09-25 10:11:35 +02:00
9c963a2122 Implement translator in vue components and use script setup syntax 2024-09-24 11:04:17 +02:00
c1c632dcb0 Refactor use of translation key in vue template slightly 2024-09-20 14:02:36 +02:00
9732b80298 Implement translation with Symfony translator within vue component 2024-09-20 13:58:23 +02:00
c602c27b39 Remove empty translation key, causing errors 2024-09-20 13:57:56 +02:00
41f13e29e0 Expand detection of duplicates in translations command
Added functionality to specify key namespaces that should be exluded from duplicate detection
Added functionality to check the accidental creation of duplicates based on an md5 hash of translation duplicates.
2024-09-20 12:16:31 +02:00
611261c863 Example usage of translation keys from yaml files within vue component 2024-09-17 16:59:50 +02:00
5921404712 Remove unutilized script 2024-09-17 12:50:32 +02:00
23d882d4cd Improve script to find translation duplicates
Format the output using a table.
Use existing translator.default service to fetch translation files and extract keys and translations from the translation catalogue for a certain locale.
2024-09-17 12:49:41 +02:00
155066be13 Remove irrelevant command 2024-08-19 16:48:03 +02:00
a5329c5d69 Create commands to manage translations 2024-08-19 16:11:18 +02:00
547a9d1369 Downgrade bundles to v2.23.0 fix in v2.23.1 undone 2024-08-08 11:01:21 +02:00
288a02f5b7 Undo wrong fix in export filter parcours having activity between dates
Problem reported by users, wrong analysis and fix made. Behavior was in fact
correct.
2024-08-05 15:56:40 +02:00
5dfd8daf3a Remove dump causing export to fail 2024-07-30 14:58:48 +02:00
a46e987f81 Upgrade version of chill-bundles to v2.23.1 2024-07-25 16:56:43 +02:00
81220b5b22 Correct phpstan error: ParsingException is never thrown
The typing of the exception had to be changed, because \Datetime throws an \Exception
instead of a ParsingException.
2024-07-25 11:30:33 +02:00
5b0019cde7 Fix query in filter: period having activity between dates filter
Adding statement to ensure that subquery only considers records that are identical to the
ones being processed in the main query.
2024-07-25 11:23:36 +02:00
b42473b01d Upgrade bundles version with new features and fix 2024-07-23 16:54:08 +02:00
be19d09bad Fix typing error in ListActivityHelper: string expected got int 2024-07-23 16:43:54 +02:00
c82991674e Rector changes: repositories become final readonly classes 2024-07-18 09:49:05 +02:00
3fc3f32c5f Pipeline fixes 2024-07-17 16:21:19 +02:00
20af766cdf Fix the query to return the count of events
Left join was causing events to counted multiple times (once per participation). Using DISTINCT fixes this.
2024-07-17 15:38:55 +02:00
681f637d13 Merge branch 'add-module-emploi' into upgrade-sf5 2024-07-17 14:21:10 +02:00
fb8a6d960e Remove duplicate edit method in events controller 2024-07-17 14:20:15 +02:00
a2310a662f Fix delete request for event status controller 2024-07-17 14:17:19 +02:00
dd7d126bec Fix edit request for event bundle 2024-07-17 14:15:48 +02:00
29f6a43288 Fix translation in event bundle using new pluralization syntax 2024-07-17 13:35:47 +02:00
74be6460d4 Fix delete request for all entities 2024-07-17 13:33:05 +02:00
82 changed files with 1087 additions and 805 deletions

View File

@@ -1,5 +0,0 @@
kind: Feature
body: '[DX] move async-upload-bundle features into chill-bundles'
time: 2023-12-12T15:48:41.954970271+01:00
custom:
Issue: "221"

View File

@@ -1,5 +0,0 @@
kind: Feature
body: Add job bundle (module emploi)
time: 2024-05-22T16:49:33.730465146+02:00
custom:
Issue: ""

View File

@@ -1,6 +0,0 @@
kind: Feature
body: |
Upgrade import of address list to the last version of compiled addresses of belgian-best-address
time: 2024-05-30T16:00:03.440767606+02:00
custom:
Issue: ""

View File

@@ -1,6 +0,0 @@
kind: Feature
body: |
Upgrade CKEditor and refactor configuration with use of typescript
time: 2024-05-31T19:02:42.776662753+02:00
custom:
Issue: ""

View File

@@ -0,0 +1,7 @@
kind: Feature
body: "Implementation of new translation management with one source of truth for both
twig and vue component templates using YAML files. \nDuplicate translation keys
can also be detected with new command."
time: 2024-11-18T15:06:27.929549251+01:00
custom:
Issue: ""

View File

@@ -1,6 +0,0 @@
kind: Fixed
body: Fix resolving of centers for an household, which will fix in turn the access
control
time: 2024-04-10T10:37:36.462484988+02:00
custom:
Issue: ""

11
.changes/v2.23.0.md Normal file
View File

@@ -0,0 +1,11 @@
## v2.23.0 - 2024-07-23
### Feature
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
* Add job bundle (module emploi)
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
* Upgrade CKEditor and refactor configuration with use of typescript
### Fixed
* Fix resolving of centers for an household, which will fix in turn the access control
* Resolved type hinting error in activity list export

View File

@@ -6,6 +6,18 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.23.0 - 2024-07-23
### Feature
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
* Add job bundle (module emploi)
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
* Upgrade CKEditor and refactor configuration with use of typescript
### Fixed
* Fix resolving of centers for an household, which will fix in turn the access control
* Resolved type hinting error in activity list export
## v2.22.2 - 2024-07-03
### Fixed
* Remove scope required for event participation stats

View File

@@ -65,10 +65,12 @@
"symfony/security-guard": "^5.4",
"symfony/security-http": "^5.4",
"symfony/serializer": "^5.4",
"symfony/stimulus-bundle": "^2.19",
"symfony/string": "^5.4",
"symfony/templating": "^5.4",
"symfony/translation": "^5.4",
"symfony/twig-bundle": "^5.4",
"symfony/ux-translator": "^2.19",
"symfony/validator": "^5.4",
"symfony/webpack-encore-bundle": "^1.11",
"symfony/workflow": "^5.4",

View File

@@ -0,0 +1,31 @@
Translations
*************
Translator-UX: one source of truth
==================================
The Translator-ux integration streamlines the process of managing and using translation keys dynamically in our views, whether they be in twig or vue components. The goal is to have one source of truth
for all translations and avoid having to add translation keys in the YAML files as well as in i18ns files.
To add new translation keys, you can define them in your translation YAML files. Running `symfony console cache:clear` will subsequently update the compiled translation keys which can then also be imported and
used within any vue component. For use within a twig template they can be leveraged by using the |trans function.
Within vue components you will have to import the translation keys you require and then they can be used in the template with the trans() function.
It is advisable, before adding a translation key to do a search on the existing translation keys of the translation you require. An IDE will allow you to do so easily.
However to avoid the creation of duplicate translation keys a command also exists to detect them. We also strongly advise you to use this command as explained below.
Detect duplicates command
=========================
The DetectTranslationDuplicatesCommand `chill:detect-duplicate-translations` is a Symfony console command designed to identify duplicate translations across YAML files in a project.
It checks for repeated translation values linked to different keys within a specified locale.
The command accepts two main options:
1. `--locale`: to specify the language locale to check (defaulting to 'en')
2. `--exclude-namespaces`: to list namespaces to ignore during the check.
3. [optional] `--verify-hash`: can be used to ensure that the hash of current duplicates matches a given expected value,
aiding in maintaining translation integrity.
When duplicates are detected, they are displayed in a table format, listing the repeated translations alongside the keys where they are found.
If a mismatch occurs between the computed and expected hash values, an error message is displayed to signal a potential issue in translation consistency.
This command is useful for maintaining clean and consistent translations, avoiding redundancy in your YAML files.

View File

@@ -152,7 +152,7 @@ class ListActivityHelper
return '';
}
return $this->translator->trans($value);
return $this->translator->trans((string) $value);
},
};
}

View File

@@ -73,7 +73,7 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
$qb->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = acp"
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = activity.accompanyingPeriod"
)
);

View File

@@ -15,9 +15,9 @@ use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
final readonly class ActivityTypeRepository implements ActivityTypeRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{

View File

@@ -22,8 +22,8 @@
<ul class="record_actions">
<li class="add-persons">
<add-persons
buttonTitle="activity.add_persons"
modalTitle="activity.add_persons"
:buttonTitle="trans(ACTIVITY_ADD_PERSONS)"
:modalTitle="trans(ACTIVITY_ADD_PERSONS)"
v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions"
@addNewPersons="addNewPersons"
@@ -40,6 +40,20 @@ import { mapState, mapGetters } from 'vuex';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
import {
ACTIVITY_BLOC_PERSONS,
ACTIVITY_BLOC_PERSONS_ASSOCIATED,
ACTIVITY_BLOC_PERSONS_NOT_ASSOCIATED,
ACTIVITY_BLOC_THIRDPARTY,
ACTIVITY_BLOC_USERS,
ACTIVITY_ADD_PERSONS,
ACTIVITY_LOCATION,
ACTIVITY_CHOOSE_LOCATION,
MULTISELECT_SELECT_LABEL,
MULTISELECT_DESELECT_LABEL,
MULTISELECT_SELECTED_LABEL,
trans,
} from "../../../../../../../../../../../assets/translator";
export default {
name: "ConcernedGroups",
@@ -48,16 +62,22 @@ export default {
PersonsBloc,
PersonText
},
setup() {
return {
trans,
ACTIVITY_ADD_PERSONS
};
},
data() {
return {
personsBlocs: [
{ key: 'persons',
title: 'activity.bloc_persons',
title: trans(ACTIVITY_BLOC_PERSONS),
persons: [],
included: false
},
{ key: 'personsAssociated',
title: 'activity.bloc_persons_associated',
title: trans(ACTIVITY_BLOC_PERSONS_ASSOCIATED),
persons: [],
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true
},
@@ -67,12 +87,12 @@ export default {
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true
},
{ key: 'thirdparty',
title: 'activity.bloc_thirdparty',
title: trans(ACTIVITY_BLOC_THIRDPARTY),
persons: [],
included: window.activity ? window.activity.activityType.thirdPartiesVisible !== 0 : true
},
{ key: 'users',
title: 'activity.bloc_users',
title: trans(ACTIVITY_BLOC_USERS),
persons: [],
included: window.activity ? window.activity.activityType.usersVisible !== 0 : true
},

View File

@@ -2,7 +2,7 @@
<teleport to="#location">
<div class="mb-3 row">
<label :class="locationClassList">
{{ $t("activity.location") }}
{{ trans(ACTIVITY_LOCATION) }}
</label>
<div class="col-sm-8">
<VueMultiselect
@@ -13,11 +13,11 @@
open-direction="top"
:multiple="false"
:searchable="true"
:placeholder="$t('activity.choose_location')"
:placeholder="trans(ACTIVITY_CHOOSE_LOCATION)"
:custom-label="customLabel"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')"
:select-label="trans(MULTISELECT_SELECT_LABEL)"
:deselect-label="trans(MULTISELECT_DESELECT_LABEL)"
:selected-label="trans(MULTISELECT_SELECTED_LABEL)"
:options="availableLocations"
group-values="locations"
group-label="locationGroup"
@@ -34,6 +34,14 @@
import { mapState, mapGetters } from "vuex";
import VueMultiselect from "vue-multiselect";
import NewLocation from "./Location/NewLocation.vue";
import {
trans,
ACTIVITY_LOCATION,
ACTIVITY_CHOOSE_LOCATION,
MULTISELECT_SELECT_LABEL,
MULTISELECT_DESELECT_LABEL,
MULTISELECT_SELECTED_LABEL
} from '../../../../../../../../../../../assets/translator'
export default {
name: "Location",
@@ -41,6 +49,16 @@ export default {
NewLocation,
VueMultiselect,
},
setup() {
return {
trans,
ACTIVITY_LOCATION,
ACTIVITY_CHOOSE_LOCATION,
MULTISELECT_SELECT_LABEL,
MULTISELECT_DESELECT_LABEL,
MULTISELECT_SELECTED_LABEL
};
},
data() {
return {
locationClassList:

View File

@@ -3,7 +3,7 @@
<ul class="record_actions">
<li>
<a class="btn btn-sm btn-create" @click="openModal">
{{ $t('activity.create_new_location') }}
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
</a>
</li>
</ul>
@@ -14,7 +14,7 @@
@close="modal.showModal = false">
<template v-slot:header>
<h3 class="modal-title">{{ $t('activity.create_new_location') }}</h3>
<h3 class="modal-title">{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}</h3>
</template>
<template v-slot:body>
<form>
@@ -26,17 +26,17 @@
<div class="form-floating mb-3">
<select class="form-select form-select-lg" id="type" required v-model="selectType">
<option selected disabled value="">{{ $t('activity.choose_location_type') }}</option>
<option selected disabled value="">{{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}</option>
<option v-for="t in locationTypes" :value="t" :key="t.id">
{{ t.title.fr }}
</option>
</select>
<label>{{ $t('activity.location_fields.type') }}</label>
<label>{{ trans(ACTIVITY_LOCATION_FIELDS_TYPE) }}</label>
</div>
<div class="form-floating mb-3">
<input class="form-control form-control-lg" id="name" v-model="inputName" placeholder />
<label for="name">{{ $t('activity.location_fields.name') }}</label>
<label for="name">{{ trans(ACTIVITY_LOCATION_FIELDS_NAME) }}</label>
</div>
<add-address
@@ -49,15 +49,15 @@
<div class="form-floating mb-3" v-if="showContactData">
<input class="form-control form-control-lg" id="phonenumber1" v-model="inputPhonenumber1" placeholder />
<label for="phonenumber1">{{ $t('activity.location_fields.phonenumber1') }}</label>
<label for="phonenumber1">{{ trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1) }}</label>
</div>
<div class="form-floating mb-3" v-if="hasPhonenumber1">
<input class="form-control form-control-lg" id="phonenumber2" v-model="inputPhonenumber2" placeholder />
<label for="phonenumber2">{{ $t('activity.location_fields.phonenumber2') }}</label>
<label for="phonenumber2">{{ trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2) }}</label>
</div>
<div class="form-floating mb-3" v-if="showContactData">
<input class="form-control form-control-lg" id="email" v-model="inputEmail" placeholder />
<label for="email">{{ $t('activity.location_fields.email') }}</label>
<label for="email">{{ trans(ACTIVITY_LOCATION_FIELDS_EMAIL) }}</label>
</div>
</form>
@@ -66,7 +66,7 @@
<button class="btn btn-save"
@click.prevent="saveNewLocation"
>
{{ $t('action.save') }}
{{ trans(SAVE) }}
</button>
</template>
@@ -81,6 +81,13 @@ import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue"
import { mapState } from "vuex";
import { getLocationTypes } from "../../api";
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
import {
SAVE,
ACTIVITY_LOCATION_FIELDS_EMAIL, ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2, ACTIVITY_LOCATION_FIELDS_NAME,
ACTIVITY_LOCATION_FIELDS_TYPE, ACTIVITY_CHOOSE_LOCATION_TYPE, ACTIVITY_CREATE_NEW_LOCATION,
trans
} from "../../../../../../../../../../../../assets/translator";
export default {
name: "NewLocation",
@@ -88,6 +95,19 @@ export default {
Modal,
AddAddress,
},
setup() {
return {
trans,
SAVE,
ACTIVITY_LOCATION_FIELDS_EMAIL,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
ACTIVITY_LOCATION_FIELDS_NAME,
ACTIVITY_LOCATION_FIELDS_TYPE,
ACTIVITY_CHOOSE_LOCATION_TYPE,
ACTIVITY_CREATE_NEW_LOCATION,
};
},
props: ['availableLocations'],
data() {
return {

View File

@@ -3,7 +3,7 @@
<div class="mb-3 row">
<div class="col-4">
<label :class="socialIssuesClassList">{{ $t('activity.social_issues') }}</label>
<label :class="socialIssuesClassList">{{ trans(ACTIVITY_SOCIAL_ISSUES) }}</label>
</div>
<div class="col-8">
@@ -31,7 +31,7 @@
:allow-empty="true"
:show-labels="false"
:loading="issueIsLoading"
:placeholder="$t('activity.choose_other_social_issue')"
:placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)"
:options="socialIssuesOther"
@select="addIssueInList">
</VueMultiselect>
@@ -42,7 +42,7 @@
<div class="mb-3 row">
<div class="col-4">
<label :class="socialActionsClassList">{{ $t('activity.social_actions') }}</label>
<label :class="socialActionsClassList">{{ trans(ACTIVITY_SOCIAL_ACTIONS) }}</label>
</div>
<div class="col-8">
@@ -51,7 +51,7 @@
</div>
<span v-else-if="socialIssuesSelected.length === 0" class="inline-choice chill-no-data-statement mt-3">
{{ $t('activity.select_first_a_social_issue') }}
{{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }}
</span>
<template v-else-if="socialActionsList.length > 0">
@@ -66,7 +66,7 @@
</template>
<span v-else-if="actionAreLoaded && socialActionsList.length === 0" class="inline-choice chill-no-data-statement mt-3">
{{ $t('activity.social_action_list_empty') }}
{{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
</span>
@@ -81,6 +81,11 @@ import VueMultiselect from 'vue-multiselect';
import CheckSocialIssue from './SocialIssuesAcc/CheckSocialIssue.vue';
import CheckSocialAction from './SocialIssuesAcc/CheckSocialAction.vue';
import { getSocialIssues, getSocialActionByIssue } from '../api.js';
import {
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE, ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES, ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE, trans
} from "../../../../../../../../../../../assets/translator";
export default {
name: "SocialIssuesAcc",
@@ -89,6 +94,16 @@ export default {
CheckSocialAction,
VueMultiselect
},
setup() {
return {
trans,
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE
};
},
data() {
return {
issueIsLoading: false,

View File

@@ -101,6 +101,31 @@ activity:
Insert a document: Insérer un document
Remove a document: Supprimer le document
comment: Commentaire
errors: Le formulaire contient des erreurs
social_issues: Problématiques sociales
choose_other_social_issue: Ajouter une autre problématique sociale...
social_actions: Actions d'accompagnement
select_first_a_social_issue: Sélectionnez d'abord une problématique sociale
social_action_list_empty: Aucune action sociale disponible
add_persons: Ajouter des personnes concernées
bloc_persons: Usagers
bloc_persons_associated: Usagers du parcours
bloc_persons_not_associated: Tiers non-pro.
bloc_thirdparty: Tiers professionnels
bloc_users: T(M)S
location: Localisation
choose_location: Choisissez une localisation
choose_location_type: Choisissez un type de localisation
create_new_location: Créer une nouvelle localisation
location_fields:
name: Nom
type: Type
phonenumber1: Téléphone
phonenumber2: Autre téléphone
email: Adresse courriel
create_address: Créer une adresse
edit_address: Modifier l'adresse
No documents: Aucun document
# activity filter in list page

View File

@@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ChargeKind;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class ChargeKindRepository implements ChargeKindRepositoryInterface
final readonly class ChargeKindRepository implements ChargeKindRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ResourceKind;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class ResourceKindRepository implements ResourceKindRepositoryInterface
final readonly class ResourceKindRepository implements ResourceKindRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -21,14 +21,14 @@ use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
final class RelatorioDriver implements DriverInterface
final readonly class RelatorioDriver implements DriverInterface
{
private readonly string $url;
private string $url;
public function __construct(
private readonly HttpClientInterface $client,
private HttpClientInterface $client,
ParameterBagInterface $parameterBag,
private readonly LoggerInterface $logger
private LoggerInterface $logger
) {
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
}

View File

@@ -16,11 +16,11 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\HttpFoundation\RequestStack;
final class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
final readonly class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager, private readonly RequestStack $requestStack)
public function __construct(EntityManagerInterface $entityManager, private RequestStack $requestStack)
{
$this->repository = $entityManager->getRepository(DocGeneratorTemplate::class);
}

View File

@@ -94,7 +94,7 @@ readonly class CountEvents implements ExportInterface, GroupedExportInterface
->leftJoin('event.participations', 'epart')
->leftJoin('epart.person', 'person');
$qb->select('COUNT(event.id) as export_count_event');
$qb->select('COUNT(DISTINCT event.id) as export_count_event');
if ($this->filterStatsByCenters) {
$qb

View File

@@ -34,7 +34,7 @@ class EventType extends AbstractType
{
$builder
->add('name', TextType::class, [
'required' => true
'required' => true,
])
->add('date', ChillDateTimeType::class, [
'required' => true,

View File

@@ -123,7 +123,6 @@ Role: Rôles
Role creation: Nouveau rôle
Role edit: Modifier un rôle
'': ''
xlsx: xlsx
ods: ods
csv: csv

View File

@@ -91,7 +91,7 @@ class PartenaireRomeAppellation
try {
$response = $this->httpClient->request('GET', sprintf(self::BASE.'appellation/%s', $code), [
'headers' => [
'Authorization' => 'Bearer ' . $bearer,
'Authorization' => 'Bearer '.$bearer,
'Accept' => 'application/json',
],
'query' => [
@@ -101,9 +101,9 @@ class PartenaireRomeAppellation
return $response->toArray();
} catch (HttpExceptionInterface $exception) {
if ($exception->getResponse()->getStatusCode() === 429) {
if (429 === $exception->getResponse()->getStatusCode()) {
$retryAfter = $exception->getResponse()->getHeaders(false)['retry-after'][0] ?? 1;
sleep((int)$retryAfter);
sleep((int) $retryAfter);
} else {
throw $exception;
}

View File

@@ -0,0 +1,180 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Console\Helper\Table;
class DetectTranslationDuplicatesCommand extends Command
{
protected static $defaultName = 'chill:detect-duplicate-translations';
public function __construct(private readonly TranslatorInterface $translator, private readonly KernelInterface $kernel)
{
parent::__construct();
}
protected function configure(): void
{
$this
->setDescription('Detects duplicate translations in YAML files.')
->addOption('locale', null, InputOption::VALUE_REQUIRED, 'Locale to check for duplicate translations', 'en')
->addOption('exclude-namespaces', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Namespaces to exclude from duplicate detection', [])
->addArgument('verify-hash', InputArgument::OPTIONAL, 'The expected hash to verify translation integrity');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$locale = $input->getOption('locale');
$excludedNamespaces = $input->getOption('exclude-namespaces');
$expectedHash = $input->getArgument('verify-hash');
// Loop through all bundles and get the translation directories
foreach ($this->kernel->getBundles() as $bundle) {
$bundlePath = $bundle->getPath();
$translationDir = $this->getTranslationDirectory($bundle->getName(), $bundlePath);
if ($translationDir && is_dir($translationDir)) {
foreach (glob($translationDir . '/*.yaml') as $file) {
$this->translator->addResource('yaml', $file, $locale);
}
}
}
$catalogue = $this->translator->getCatalogue($locale);
$allTranslations = [];
// Iterate through each domain in the catalogue
foreach ($catalogue->all() as $domain => $translations) {
foreach ($translations as $key => $value) {
if ($this->isExcludedNamespace("$domain.$key", $excludedNamespaces)) {
continue;
}
if (is_array($value)) {
$this->flattenTranslation($value, "$domain.$key", $allTranslations);
} else {
if (!isset($allTranslations[$value])) {
$allTranslations[$value] = [];
}
$allTranslations[$value][] = "$domain.$key";
}
}
}
// Detect values that appear in more than one key
$duplicates = array_filter($allTranslations, function ($keys) {
return count($keys) > 1;
});
$duplicatesHash = $this->generateDuplicatesHash($duplicates);
if ($expectedHash) {
if ($duplicatesHash === $expectedHash) {
$output->writeln('<info>Translations are consistent with the expected hash.</info>');
$output->writeln("<info>Current duplicate hash: $duplicatesHash</info>");
return Command::SUCCESS;
} else {
$output->writeln('<error>Translation hash mismatch! Potential duplicate added.</error>');
$this->renderDuplicatesTable($output, $duplicates, $locale);
$output->writeln("<info>Current duplicate hash: $duplicatesHash</info>");
return Command::FAILURE;
}
}
$this->renderDuplicatesTable($output, $duplicates, $locale);
$output->writeln("<info>Current duplicate hash: $duplicatesHash</info>");
return Command::SUCCESS;
}
private function flattenTranslation(array $translations, string $prefix, array &$allTranslations): void
{
foreach ($translations as $key => $value) {
$fullKey = "$prefix.$key";
if (is_array($value)) {
$this->flattenTranslation($value, $fullKey, $allTranslations);
} else {
if (!isset($allTranslations[$value])) {
$allTranslations[$value] = [];
}
$allTranslations[$value][] = $fullKey;
}
}
}
private function getTranslationDirectory(string $bundleName, string $bundlePath): ?string
{
$translationDir = $bundlePath . '/translations';
if ($bundleName === 'ChillAsideActivityBundle') {
$translationDir = $bundlePath . '/src/translations';
}
return is_dir($translationDir) ? $translationDir : null;
}
private function wrapText(string $text, int $width): string
{
return wordwrap($text, $width, "\n", true);
}
private function isExcludedNamespace(string $key, array $excludedNamespaces): bool
{
foreach ($excludedNamespaces as $namespace) {
if (str_starts_with($key, $namespace)) {
return true;
}
}
return false;
}
private function generateDuplicatesHash(array $duplicates): string
{
ksort($duplicates);
foreach ($duplicates as $translation => $keys) {
sort($keys);
}
return hash('md5', serialize($duplicates));
}
private function renderDuplicatesTable(OutputInterface $output, array $duplicates, string $locale): void
{
if (empty($duplicates)) {
$output->writeln("<info>No duplicate translations found for locale '$locale'.</info>");
return;
}
$output->writeln("<comment>Duplicate translations found for locale '$locale':</comment>");
$table = new Table($output);
$table->setHeaders(['Translation', 'Used in Keys']);
foreach ($duplicates as $translation => $keys) {
$wrappedTranslation = $this->wrapText($translation, 40);
$wrappedKeys = $this->wrapText(implode(', ', $keys), 80);
$table->addRow([$wrappedTranslation, $wrappedKeys]);
}
$table->render();
}
}

View File

@@ -20,11 +20,11 @@ use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\Persistence\ObjectRepository;
final class AddressReferenceRepository implements ObjectRepository
final readonly class AddressReferenceRepository implements ObjectRepository
{
private readonly EntityManagerInterface $entityManager;
private EntityManagerInterface $entityManager;
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -17,9 +17,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class AddressRepository implements ObjectRepository
final readonly class AddressRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\MainBundle\Entity\Center;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class CenterRepository implements CenterRepositoryInterface
final readonly class CenterRepository implements CenterRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -17,9 +17,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class CountryRepository implements ObjectRepository
final readonly class CountryRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\MainBundle\Entity\GeographicalUnitLayer;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class GeographicalUnitLayerLayerRepository implements GeographicalUnitLayerRepositoryInterface
final readonly class GeographicalUnitLayerLayerRepository implements GeographicalUnitLayerRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{

View File

@@ -18,9 +18,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
final class GeographicalUnitRepository implements GeographicalUnitRepositoryInterface
final readonly class GeographicalUnitRepository implements GeographicalUnitRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{

View File

@@ -16,9 +16,9 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
final class GroupCenterRepository implements ObjectRepository
final readonly class GroupCenterRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\MainBundle\Entity\Language;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class LanguageRepository implements LanguageRepositoryInterface
final readonly class LanguageRepository implements LanguageRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -16,9 +16,9 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
final class PermissionsGroupRepository implements ObjectRepository
final readonly class PermissionsGroupRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -19,11 +19,11 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
final class PostalCodeRepository implements PostalCodeRepositoryInterface
final readonly class PostalCodeRepository implements PostalCodeRepositoryInterface
{
private readonly EntityManagerInterface $entityManager;
private EntityManagerInterface $entityManager;
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -18,9 +18,9 @@ use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Doctrine\Persistence\ObjectRepository;
final class RegroupmentRepository implements ObjectRepository
final readonly class RegroupmentRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -16,9 +16,9 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
final class RoleScopeRepository implements ObjectRepository
final readonly class RoleScopeRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -17,11 +17,11 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
final class ScopeRepository implements ScopeRepositoryInterface
final readonly class ScopeRepository implements ScopeRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager, private readonly TranslatableStringHelperInterface $translatableStringHelper)
public function __construct(EntityManagerInterface $entityManager, private TranslatableStringHelperInterface $translatableStringHelper)
{
$this->repository = $entityManager->getRepository(Scope::class);
}

View File

@@ -1,5 +1,6 @@
import { multiSelectMessages } from 'ChillMainAssets/vuejs/_js/i18n'
// translations added to yaml file and used through symfony ux translator.
// these can be deleted once everything is verified to work.
const addressMessages = {
fr: {
add_an_address_title: 'Créer une adresse',

View File

@@ -4,7 +4,7 @@
<a class="nav-link" :class="{ active: isActive('person') }">
<label for="person">
<input type="radio" name="person" id="person" v-model="radioType" value="person">
{{ $t('onthefly.create.person') }}
{{ $trans('ONTHEFLY_CREATE_PERSON') }}
</label>
</a>
</li>
@@ -37,59 +37,61 @@
</div>
</template>
<script>
<script setup>
import { ref, computed, onMounted } from 'vue';
import OnTheFlyPerson from 'ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue';
import OnTheFlyThirdparty from 'ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue';
export default {
name: "OnTheFlyCreate",
props: ['action', 'allowedTypes', 'query'],
components: {
OnTheFlyPerson,
OnTheFlyThirdparty
},
data() {
return {
type: null
}
},
computed: {
radioType: {
set(type) {
this.type = type;
console.log('## type:', type, ', action:', this.action);
},
get() {
return this.type;
}
},
},
mounted() {
this.type = (this.allowedTypes.length === 1 && this.allowedTypes.includes('thirdparty')) ? 'thirdparty' : 'person'
},
methods: {
isActive(tab) {
return (this.type === tab) ? true : false;
},
castDataByType() {
switch (this.radioType) {
case 'person':
return this.$refs.castPerson.$data.person;
case 'thirdparty':
let data = this.$refs.castThirdparty.$data.thirdparty;
if (data.address !== undefined && data.address !== null) {
data.address = { id: data.address.address_id }
} else {
data.address = null;
}
// Define props
const props = defineProps(['action', 'allowedTypes', 'query']);
// Create a ref for type
const type = ref(null);
// Computed
const radioType = computed({
get: () => type.value,
set(value) {
type.value = value;
console.log('## type:', value, ', action:', props.action);
}
});
// Mounted
onMounted(() => {
type.value = (props.allowedTypes.length === 1 && props.allowedTypes.includes('thirdparty'))
? 'thirdparty'
: 'person';
});
// Methods
const isActive = (tab) => {
return type.value === tab;
};
const castDataByType = () => {
switch (radioType.value) {
case 'person':
return $refs.castPerson.$data.person;
case 'thirdparty':
let data = $refs.castThirdparty.$data.thirdparty;
if (data.address !== undefined && data.address !== null) {
data.address = { id: data.address.address_id };
} else {
data.address = null;
}
return data;
default:
throw Error('Invalid type of entity');
}
};
// Register components
defineExpose({
isActive,
castDataByType
});
return data;
default:
throw Error('Invalid type of entity')
}
}
}
}
</script>
<style lang="css" scoped>

View File

@@ -2,6 +2,7 @@ import { createApp } from "vue";
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
import { ontheflyMessages } from './i18n.js';
import App from "./App.vue";
import TranslatorPlugin from '../translatorPlugin'
const i18n = _createI18n( ontheflyMessages );
@@ -30,6 +31,7 @@ containers.forEach((container) => {
}
})
.use(i18n)
.use(TranslatorPlugin)
.component('app', App)
.mount(container);

View File

@@ -1,12 +1,12 @@
<template>
<span v-if="entity.type === 'person'" class="badge rounded-pill bg-person">
{{ $t('person') }}
{{ trans(PERSON) }}
</span>
<span v-if="entity.type === 'thirdparty'" class="badge rounded-pill bg-thirdparty">
<template v-if="options.displayLong !== true">
{{ $t('thirdparty.thirdparty')}}
{{ trans(THIRDPARTY)}}
</template>
<i class="fa fa-fw fa-user" v-if="entity.kind === 'child'"></i>
@@ -14,41 +14,36 @@
<i class="fa fa-fw fa-user-md" v-else></i>
<template v-if="options.displayLong === true">
<span v-if="entity.kind === 'child'">{{ $t('thirdparty.child')}}</span>
<span v-else-if="entity.kind === 'company'">{{ $t('thirdparty.company')}}</span>
<span v-else>{{ $t('thirdparty.contact')}}</span>
<span v-if="entity.kind === 'child'">{{ trans(THIRDPARTY_CONTACT_OF)}}</span>
<span v-else-if="entity.kind === 'company'">{{ trans(THIRDPARTY_COMPANY)}}</span>
<span v-else>{{ trans(THIRDPARTY_A_CONTACT)}}</span>
</template>
</span>
<span v-if="entity.type === 'user'" class="badge rounded-pill bg-user">
{{ $t('user')}}
{{ trans(ACCEPTED_USERS)}}
</span>
<span v-if="entity.type === 'household'" class="badge rounded-pill bg-user">
{{ $t('household')}}
{{ trans(HOUSEHOLD)}}
</span>
</template>
<script>
export default {
name: "BadgeEntity",
props: ['options', 'entity'],
i18n: {
messages: {
fr: {
person: "Usager",
thirdparty: {
thirdparty: "Tiers",
child: "Personne de contact",
company: "Personne morale",
contact: "Personne physique",
},
user: 'TMS',
household: 'Ménage',
}
}
}
}
<script setup>
import {
trans,
HOUSEHOLD,
ACCEPTED_USERS,
THIRDPARTY_A_CONTACT,
THIRDPARTY_COMPANY,
THIRDPARTY_CONTACT_OF,
PERSON, THIRDPARTY
} from '../../../../../../../../../../assets/translator'
const props = defineProps({
options: Object,
entity: Object
})
</script>

View File

@@ -9,23 +9,16 @@
</div>
</template>
<script>
export default {
name: "Confidential",
data() {
return {
isBlurred: true,
toggleIcon: 'fa-eye',
};
},
methods : {
toggleBlur() {
console.log(this.positionBtnFar);
this.isBlurred = !this.isBlurred;
this.toggleIcon = this.isBlurred ? 'fa-eye' : 'fa-eye-slash';
},
},
}
<script setup>
import { ref } from 'vue';
const isBlurred = ref(true);
const toggleIcon = ref('fa-eye');
const toggleBlur = () => {
isBlurred.value = !isBlurred.value;
toggleIcon.value = isBlurred.value ? 'fa-eye' : 'fa-eye-slash';
};
</script>
<style scoped lang='scss'>

View File

@@ -51,12 +51,12 @@
<div v-if="useDatePane === true" class="address-more">
<div v-if="address.validFrom">
<span class="validFrom">
<b>{{ $t('validFrom') }}</b>: {{ $d(address.validFrom.date) }}
<b>{{ trans(ADDRESS_VALID_FROM) }}</b>: {{ $d(address.validFrom.date) }}
</span>
</div>
<div v-if="address.validTo">
<span class="validTo">
<b>{{ $t('validTo') }}</b>: {{ $d(address.validTo.date) }}
<b>{{ trans(ADDRESS_VALID_TO) }}</b>: {{ $d(address.validTo.date) }}
</span>
</div>
</div>
@@ -64,48 +64,47 @@
</component>
</template>
<script>
<script setup>
import { computed } from 'vue';
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
import AddressDetailsButton from "ChillMainAssets/vuejs/_components/AddressDetails/AddressDetailsButton.vue";
import { trans, ADDRESS_VALID_FROM, ADDRESS_VALID_TO } from '../../../../../../../../../../../assets/translator'
export default {
name: 'AddressRenderBox',
components: {
Confidential,
AddressDetailsButton,
},
props: {
address: {
type: Object
},
isMultiline: {
default: true,
type: Boolean
},
useDatePane: {
default: false,
type: Boolean
},
showButtonDetails: {
// Props
const props = defineProps({
address: {
type: Object,
},
isMultiline: {
default: true,
type: Boolean
}
},
computed: {
component() {
return this.isMultiline === true ? "div" : "span";
},
multiline() {
return this.isMultiline === true ? "multiline" : "";
},
isConfidential() {
return this.address.confidential;
}
}
type: Boolean,
},
useDatePane: {
default: false,
type: Boolean,
},
showButtonDetails: {
default: true,
type: Boolean,
},
});
// Components
const components = {
Confidential,
AddressDetailsButton,
};
// Computed
const component = computed(() => (props.isMultiline ? 'div' : 'span'));
const multiline = computed(() => (props.isMultiline ? 'multiline' : ''));
const isConfidential = computed(() => props.address?.confidential);
</script>
<style lang="scss" scoped>
p {
&:after {

View File

@@ -5,9 +5,6 @@
</span>
</template>
<script>
export default {
name: "UserRenderBoxBadge",
props: ['user'],
}
<script setup>
const props = defineProps(['user'])
</script>

View File

@@ -2,39 +2,30 @@
<div class="d-grid gap-2 my-3">
<button class="btn btn-misc" type="button" v-if="!subscriberFinal" @click="subscribeTo('subscribe', 'final')">
<i class="fa fa-check fa-fw"></i>
{{ $t('subscribe_final') }}
{{ trans(WORKFLOW_SUBSCRIBE_FINAL) }}
</button>
<button class="btn btn-misc" type="button" v-if="subscriberFinal" @click="subscribeTo('unsubscribe', 'final')">
<i class="fa fa-times fa-fw"></i>
{{ $t('unsubscribe_final') }}
{{ trans(WORKFLOW_UNSUBSCRIBE_FINAL) }}
</button>
<button class="btn btn-misc" type="button" v-if="!subscriberStep" @click="subscribeTo('subscribe', 'step')">
<i class="fa fa-check fa-fw"></i>
{{ $t('subscribe_all_steps') }}
{{ trans(WORKFLOW_SUBSCRIBE_ALL_STEPS) }}
</button>
<button class="btn btn-misc" type="button" v-if="subscriberStep" @click="subscribeTo('unsubscribe', 'step')">
<i class="fa fa-times fa-fw"></i>
{{ $t('unsubscribe_all_steps') }}
{{ trans(WORKFLOW_UNSUBSCRIBE_ALL_STEPS) }}
</button>
</div>
</template>
<script>
import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods.ts';
<script setup>
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods.ts';
import { defineProps, defineEmits } from 'vue';
import { trans, WORKFLOW_SUBSCRIBE_FINAL, WORKFLOW_UNSUBSCRIBE_FINAL, WORKFLOW_SUBSCRIBE_ALL_STEPS, WORKFLOW_UNSUBSCRIBE_ALL_STEPS } from '../../../../../../../../../../../assets/translator'
export default {
name: "EntityWorkflowVueSubscriber",
i18n: {
messages: {
fr: {
subscribe_final: "Recevoir une notification à l'étape finale",
unsubscribe_final: "Ne plus recevoir de notification à l'étape finale",
subscribe_all_steps: "Recevoir une notification à chaque étape du suivi",
unsubscribe_all_steps: "Ne plus recevoir de notification à chaque étape du suivi",
}
}
},
props: {
// props
const props = defineProps({
entityWorkflowId: {
type: Number,
required: true,
@@ -47,55 +38,22 @@ export default {
type: Boolean,
required: true,
},
},
emits: ['subscriptionUpdated'],
methods: {
subscribeTo(step, to) {
let params = new URLSearchParams();
params.set('subscribe', to);
})
const url = `/api/1.0/main/workflow/${this.entityWorkflowId}/${step}?` + params.toString();
//methods
const subscribeTo = (step, to) => {
let params = new URLSearchParams();
params.set('subscribe', to);
makeFetch('POST', url).then(response => {
this.$emit('subscriptionUpdated', response);
});
}
},
}
/*
* ALTERNATIVES
*
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="laststep">
<label class="form-check-label" for="laststep">{{ $t('subscribe_final') }}</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="allsteps">
<label class="form-check-label" for="allsteps">{{ $t('subscribe_all_steps') }}</label>
</div>
const url = `/api/1.0/main/workflow/${props.entityWorkflowId}/${step}?` + params.toString();
<div class="list-group my-3">
<label class="list-group-item">
<input class="form-check-input me-1" type="checkbox" value="">
{{ $t('subscribe_final') }}
</label>
<label class="list-group-item">
<input class="form-check-input me-1" type="checkbox" value="">
{{ $t('subscribe_all_steps') }}
</label>
</div>
makeFetch('POST', url).then(response => {
emit('subscriptionUpdated', response);
});
}
<div class="btn-group-vertical my-3" role="group">
<button type="button" class="btn btn-outline-primary">
<i class="fa fa-check fa-fw"></i>
{{ $t('subscribe_final') }}
</button>
<button type="button" class="btn btn-outline-primary">
<i class="fa fa-check fa-fw"></i>
{{ $t('subscribe_all_steps') }}
</button>
</div>
*/
// emit
const emit = defineEmits(['subscriptionUpdated'])
</script>
<style scoped></style>

View File

@@ -44,17 +44,17 @@
<div class="item-col flex-grow-1">
<p v-if="isUserSubscribedToStep(w)">
<i class="fa fa-check fa-fw"></i>
{{ $t('you_subscribed_to_all_steps') }}
{{ trans(WORKFLOW_YOU_DESCRIBED_TO_ALL_STEPS) }}
</p>
<p v-if="isUserSubscribedToFinal(w)">
<i class="fa fa-check fa-fw"></i>
{{ $t('you_subscribed_to_final_step') }}
{{ trans(WORKFLOW_YOU_SUBSCRIBED_TO_FINAL_STEP) }}
</p>
</div>
<div class="item-col">
<ul class="record_actions">
<li>
<a :href="goToUrl(w)" class="btn btn-sm btn-show" :title="$t('action.show')"></a>
<a :href="goToUrl(w)" class="btn btn-sm btn-show" :title="trans(SEE)"></a>
</li>
</ul>
</div>
@@ -64,69 +64,47 @@
</div>
</template>
<script>
<script setup>
import Popover from 'bootstrap/js/src/popover';
import { onMounted } from 'vue';
import { trans, SEE, BY_USER, WORKFLOW_AT, WORKFLOW_YOU_SUBSCRIBED_TO_ALL_STEPS, WORKFLOW_YOU_SUBSCRIBED_TO_FINAL_STEP } from '../../../../../../../../../../../assets/translator'
const i18n = {
messages: {
fr: {
you_subscribed_to_all_steps: "Vous recevrez une notification à chaque étape",
you_subscribed_to_final_step: "Vous recevrez une notification à l'étape finale",
by: "Par",
at: "Le"
}
}
}
export default {
name: "ListWorkflow",
i18n: i18n,
props: {
// props
const props = defineProps({
workflows: {
type: Array,
required: true,
}
},
methods: {
goToUrl(w) {
return `/fr/main/workflow/${w.id}/show`;
},
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}`;
}
},
getPopContent(step) {
if (step.transitionPrevious != null) {
return `<ul class="small_in_title">
<li><span class="item-key">${i18n.messages.fr.by} : </span><b>${step.transitionPreviousBy.text}</b></li>
<li><span class="item-key">${i18n.messages.fr.at} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li>
</ul>`
;
}
},
formatDate(datetime) {
return datetime.split('T')[0] +' '+ datetime.split('T')[1].substring(0,5)
},
isUserSubscribedToStep(w) {
// todo
return false;
},
isUserSubscribedToFinal(w) {
// todo
return false;
},
},
mounted() {
})
// methods
const goToUrl = (w) => `/fr/main/workflow/${w.id}/show`;
const 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}`;
}
}
const getPopContent = (step) => {
if (step.transitionPrevious != null) {
return `<ul class="small_in_title">
<li><span class="item-key">{{ trans(BY_USER) }} : </span><b>${step.transitionPreviousBy.text}</b></li>
<li><span class="item-key">{{ trans(WORKFLOW_AT) }} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li>
</ul>`
;
}
}
const formatDate = (datetime) => datetime.split('T')[0] +' '+ datetime.split('T')[1].substring(0,5)
const isUserSubscribedToStep = (w) => false;
const isUserSubscribedToFinal = (w) => false;
onMounted(() => {
const triggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
const popoverList = triggerList.map(function (el) {
//console.log('popover', el)
return new Popover(el, {
html: true,
});
});
}
}
})
</script>

View File

@@ -4,26 +4,26 @@
class="btn btn-primary"
@click="openModal">
<b>{{ countWorkflows }}</b>
<template v-if="countWorkflows > 1">{{ $t('workflows') }}</template>
<template v-else>{{ $t('workflow') }}</template>
<template v-if="countWorkflows > 1">{{ trans(WORKFLOW_ASSOCIATED, { aw: 'other' }) }}</template>
<template v-else>{{ trans(WORKFLOW_ASSOCIATED, { aw: 1 }) }}</template>
</button>
<pick-workflow v-else-if="allowCreate"
<Pick-workflow v-else-if="allowCreate"
:relatedEntityClass="this.relatedEntityClass"
:relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables"
:preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate"
:goToGenerateWorkflowPayload="this.goToGenerateWorkflowPayload"
@go-to-generate-workflow="goToGenerateWorkflow"
></pick-workflow>
></Pick-workflow>
<teleport to="body">
<modal v-if="modal.showModal"
<Modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false">
<template v-slot:header>
<h2 class="modal-title">{{ $t('workflow_list') }}</h2>
<h2 class="modal-title">{{ trans(WORKFLOW_LIST) }}</h2>
</template>
<template v-slot:body>
@@ -43,89 +43,67 @@
></pick-workflow>
</template>
</modal>
</Modal>
</teleport>
</template>
<script>
<script setup>
import { ref, computed, defineProps, defineEmits } from 'vue';
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import ListWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue';
import { trans, WORKFLOW_ASSOCIATED, WORKFLOW_LIST } from '../../../../../../../../../../../assets/translator'
export default {
name: "ListWorkflowModal",
components: {
Modal,
PickWorkflow,
ListWorkflowVue
},
emits: ['goToGenerateWorkflow'],
props: {
workflows: {
type: Array,
required: true,
},
allowCreate: {
type: Boolean,
required: true,
},
relatedEntityClass: {
type: String,
required: true,
},
relatedEntityId: {
type: Number,
required: false,
},
workflowsAvailables: {
type: Array,
required: true,
},
preventDefaultMoveToGenerate: {
type: Boolean,
required: false,
default: false,
},
goToGenerateWorkflowPayload: {
required: false,
default: {}
},
},
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl"
},
}
},
computed: {
countWorkflows() {
return this.workflows.length;
},
hasWorkflow() {
return this.countWorkflows > 0;
}
},
methods: {
openModal() {
this.modal.showModal = true;
},
goToGenerateWorkflow(data) {
console.log('go to generate workflow intercepted', data);
this.$emit('goToGenerateWorkflow', data);
}
},
i18n: {
messages: {
fr: {
workflow_list: "Liste des workflows associés",
workflow: " workflow associé",
workflows: " workflows associés",
}
}
}
}
// Define props
const props = defineProps({
workflows: {
type: Array,
required: true,
},
allowCreate: {
type: Boolean,
required: true,
},
relatedEntityClass: {
type: String,
required: true,
},
relatedEntityId: {
type: Number,
required: false,
},
workflowsAvailables: {
type: Array,
required: true,
},
preventDefaultMoveToGenerate: {
type: Boolean,
required: false,
default: false,
},
goToGenerateWorkflowPayload: {
required: false,
default: () => ({}),
},
});
// Define emits
const emit = defineEmits(['goToGenerateWorkflow']);
// Reactive data
const modal = ref({
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl"
});
// Computed properties
const countWorkflows = computed(() => props.workflows.length);
const hasWorkflow = computed(() => countWorkflows.value > 0);
// Methods
const openModal = () => modal.value.showModal = true;
const goToGenerateWorkflow = (data) => emit('goToGenerateWorkflow', data);
</script>
<style scoped></style>

View File

@@ -13,51 +13,57 @@
</template>
</template>
<script>
<script setup>
import { defineProps, defineEmits } from 'vue';
import { buildLinkCreate } from 'ChillMainAssets/lib/entity-workflow/api.js';
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
export default {
name: "PickWorkflow",
props: {
relatedEntityClass: {
type: String,
required: true,
},
relatedEntityId: {
type: Number,
required: false,
},
workflowsAvailables: {
type: Array,
required: true,
},
preventDefaultMoveToGenerate: {
type: Boolean,
required: false,
default: false,
},
goToGenerateWorkflowPayload: {
required: false,
default: {}
},
// Define props
const props = defineProps({
relatedEntityClass: {
type: String,
required: true,
},
emits: ['goToGenerateWorkflow'],
methods: {
makeLink(workflowName) {
return buildLinkCreate(workflowName, this.relatedEntityClass, this.relatedEntityId);
},
goToGenerateWorkflow(event, workflowName) {
console.log('goToGenerateWorkflow', event, workflowName);
relatedEntityId: {
type: Number,
required: false,
},
workflowsAvailables: {
type: Array,
required: true,
},
preventDefaultMoveToGenerate: {
type: Boolean,
required: false,
default: false,
},
goToGenerateWorkflowPayload: {
required: false,
default: () => ({}), // Use a function to return an object
},
});
if (!this.$props.preventDefaultMoveToGenerate) {
console.log('to go generate');
window.location.assign(this.makeLink(workflowName));
}
// Define emits
const emit = defineEmits(['goToGenerateWorkflow']);
this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName), payload: this.goToGenerateWorkflowPayload});
}
// Methods
function makeLink(workflowName) {
return buildLinkCreate(workflowName, props.relatedEntityClass, props.relatedEntityId);
}
function goToGenerateWorkflow(event, workflowName) {
console.log('goToGenerateWorkflow', event, workflowName);
if (!props.preventDefaultMoveToGenerate) {
console.log('to go generate');
window.location.assign(makeLink(workflowName));
}
emit('goToGenerateWorkflow', {
event,
workflowName,
link: makeLink(workflowName),
payload: props.goToGenerateWorkflowPayload,
});
}
</script>

View File

@@ -17,7 +17,7 @@
<slot name="body"></slot>
</div>
<div class="modal-footer" v-if="!hideFooter">
<button class="btn btn-cancel" @click="$emit('close')">{{ $t('action.close') }}</button>
<button class="btn btn-cancel" @click="$emit('close')">{{ trans(MODAL_ACTION_CLOSE) }}</button>
<slot name="footer"></slot>
</div>
</div>
@@ -28,8 +28,7 @@
</transition>
</template>
<script lang="ts">
import {defineComponent} from "vue";
<script setup>
/*
* This Modal component is a mix between Vue3 modal implementation
* [+] with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden
@@ -39,22 +38,24 @@ import {defineComponent} from "vue";
* [+] using bootstrap css classes, the modal have a responsive behaviour,
* [+] modal design can be configured using css classes (size, scroll)
*/
export default defineComponent({
name: 'Modal',
props: {
modalDialogClass: {
type: Object,
required: false,
default: {},
},
hideFooter: {
type: Boolean,
required: false,
default: false
}
},
emits: ['close']
import { trans, MODAL_ACTION_CLOSE } from '../../../../../../../../../../assets/translator'
import { defineProps, defineEmits } from 'vue';
// Define the props
const props = defineProps({
modalDialogClass: {
type: Object,
required: false,
default: {},
},
hideFooter: {
type: Boolean,
required: false,
default: false,
},
});
const emit = defineEmits(['close']);
</script>
<style lang="scss">

View File

@@ -6,12 +6,12 @@
class="btn"
:class="overrideClass"
type="button"
:title="$t('markAsUnread')"
:title="trans(NOTIFICATION_MARK_AS_UNREAD)"
@click="markAsUnread"
>
<i class="fa fa-sm fa-envelope-o"></i>
<span v-if="!buttonNoText" class="ps-2">
{{ $t('markAsUnread') }}
{{ trans(NOTIFICATION_MARK_AS_UNREAD) }}
</span>
</button>
@@ -19,12 +19,12 @@
class="btn"
:class="overrideClass"
type="button"
:title="$t('markAsRead')"
:title="trans(NOTIFICATION_MARK_AS_READ)"
@click="markAsRead"
>
<i class="fa fa-sm fa-envelope-open-o"></i>
<span v-if="!buttonNoText" class="ps-2">
{{ $t('markAsRead') }}
{{ trans(NOTIFICATION_MARK_AS_READ) }}
</span>
</button>
@@ -32,7 +32,7 @@
type="button"
class="btn btn-outline-primary"
:href="showUrl"
:title="$t('action.show')"
:title="trans(SEE)"
>
<i class="fa fa-sm fa-comment-o"></i>
</a>
@@ -40,71 +40,65 @@
</div>
</template>
<script>
<script setup>
import { ref, computed } from 'vue';
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods.ts';
import { trans, NOTIFICATION_MARK_AS_READ, NOTIFICATION_MARK_AS_UNREAD, SEE } from '../../../../../../../../../../../assets/translator'
export default {
name: "NotificationReadToggle",
props: {
isRead: {
required: true,
type: Boolean,
},
notificationId: {
required: true,
type: Number,
},
// Optional
buttonClass: {
required: false,
type: String
},
buttonNoText: {
required: false,
type: Boolean,
},
showUrl: {
required: false,
type: String
}
// Props
const props = defineProps({
isRead: {
type: Boolean,
required: true,
},
emits: ['markRead', 'markUnread'],
computed: {
/// [Option] override default button appearance (btn-misc)
overrideClass() {
return this.buttonClass ? this.buttonClass : 'btn-misc'
},
/// [Option] don't display text on button
buttonHideText() {
return this.buttonNoText;
},
/// [Option] showUrl is href for show page second button.
// When passed, the component return a button-group with 2 buttons.
isButtonGroup() {
return this.showUrl;
}
notificationId: {
type: Number,
required: true,
},
methods: {
markAsUnread() {
makeFetch('POST', `/api/1.0/main/notification/${this.notificationId}/mark/unread`, []).then(response => {
this.$emit('markRead', { notificationId: this.notificationId });
})
},
markAsRead() {
makeFetch('POST', `/api/1.0/main/notification/${this.notificationId}/mark/read`, []).then(response => {
this.$emit('markUnread', { notificationId: this.notificationId });
})
buttonClass: {
type: String,
required: false,
},
buttonNoText: {
type: Boolean,
required: false,
},
showUrl: {
type: String,
required: false,
},
});
// Emits
const emit = defineEmits(['markRead', 'markUnread']);
// Computed
const overrideClass = computed(() => props.buttonClass || 'btn-misc');
const buttonHideText = computed(() => props.buttonNoText);
const isButtonGroup = computed(() => props.showUrl);
// Methods
const markAsUnread = () => {
makeFetch('POST', `/api/1.0/main/notification/${props.notificationId}/mark/unread`, []).then(() => {
emit('markRead', { notificationId: props.notificationId });
});
};
const markAsRead = () => {
makeFetch('POST', `/api/1.0/main/notification/${props.notificationId}/mark/read`, []).then(() => {
emit('markUnread', { notificationId: props.notificationId });
});
};
// i18n setup
/*const { t } = useI18n({
messages: {
fr: {
markAsUnread: 'Marquer comme non-lu',
markAsRead: 'Marquer comme lu',
},
},
i18n: {
messages: {
fr: {
markAsUnread: 'Marquer comme non-lu',
markAsRead: 'Marquer comme lu'
}
}
}
}
});*/
</script>
<style lang="scss">

View File

@@ -4,216 +4,182 @@
isChangeIcon ? 'change-icon' : '',
isChangeClass ? options.changeClass : 'btn-wopilink' ]"
@click="openModal">
<i v-if="isChangeIcon" class="fa me-2" :class="options.changeIcon"></i>
<span v-if="!noText">
{{ $t('online_edit_document') }}
{{ trans(WOPI_ONLINE_EDIT_DOCUMENT) }}
</span>
</a>
<teleport to="body">
<div class="wopi-frame" v-if="isOpenDocument">
<modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
:hideFooter=true
@close="modal.showModal = false">
<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>
</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>
<div v-if="loading" class="loading">
<i class="fa fa-circle-o-notch fa-spin fa-3x" :title="$t('loading')"></i>
<i class="fa fa-circle-o-notch fa-spin fa-3x" :title="trans(WOPI_LOADING)"></i>
</div>
<iframe
:src="this.wopiUrl"
@load="loaded"
></iframe>
</template>
</modal>
</div>
<div v-else>
<modal v-if="modal.showModal"
<Modal v-if="modal.showModal"
modalDialogClass="modal-sm"
@close="modal.showModal = false">
<template v-slot:header>
<h3>{{ $t('invalid_title') }}</h3>
<h3>{{ trans(WOPI_INVALID_TITLE) }}</h3>
</template>
<template v-slot:body>
<div class="alert alert-warning">{{ $t('invalid_message') }}</div>
<div class="alert alert-warning">{{ trans(WOPI_ONLINE_EDIT_DOCUMENT) }}</div>
</template>
</modal>
</Modal>
</div>
</teleport>
</template>
<script>
<script setup>
import { ref, computed } from 'vue';
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import logo from 'ChillMainAssets/chill/img/logo-chill-sans-slogan_white.png';
import { useI18n } from 'vue-i18n';
import { trans, WOPI_ONLINE_EDIT_DOCUMENT, WOPI_INVALID_TITLE, WOPI_LOADING } from '../../../../../../../../../../assets/translator'
export default {
name: "OpenWopiLink",
components: {
Modal
},
props: {
wopiUrl: {
type: String,
required: true
},
type: {
type: String,
required: true
},
options: {
type: Object,
required: false
}
},
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-fullscreen" //modal-dialog-scrollable
},
logo: logo,
loading: false,
mime: [
// TODO temporary hardcoded. to be replaced by twig extension or a collabora server query
'application/clarisworks',
'application/coreldraw',
'application/macwriteii',
'application/msword',
'application/pdf',
'application/vnd.lotus-1-2-3',
'application/vnd.ms-excel',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'application/vnd.ms-excel.sheet.macroEnabled.12',
'application/vnd.ms-excel.template.macroEnabled.12',
'application/vnd.ms-powerpoint',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'application/vnd.ms-powerpoint.template.macroEnabled.12',
'application/vnd.ms-visio.drawing',
'application/vnd.ms-word.document.macroEnabled.12',
'application/vnd.ms-word.template.macroEnabled.12',
'application/vnd.ms-works',
'application/vnd.oasis.opendocument.chart',
'application/vnd.oasis.opendocument.formula',
'application/vnd.oasis.opendocument.graphics',
'application/vnd.oasis.opendocument.graphics-flat-xml',
'application/vnd.oasis.opendocument.graphics-template',
'application/vnd.oasis.opendocument.presentation',
'application/vnd.oasis.opendocument.presentation-flat-xml',
'application/vnd.oasis.opendocument.presentation-template',
'application/vnd.oasis.opendocument.spreadsheet',
'application/vnd.oasis.opendocument.spreadsheet-flat-xml',
'application/vnd.oasis.opendocument.spreadsheet-template',
'application/vnd.oasis.opendocument.text',
'application/vnd.oasis.opendocument.text-flat-xml',
'application/vnd.oasis.opendocument.text-master',
'application/vnd.oasis.opendocument.text-master-template',
'application/vnd.oasis.opendocument.text-template',
'application/vnd.oasis.opendocument.text-web',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'application/vnd.openxmlformats-officedocument.presentationml.template',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'application/vnd.sun.xml.calc',
'application/vnd.sun.xml.calc.template',
'application/vnd.sun.xml.chart',
'application/vnd.sun.xml.draw',
'application/vnd.sun.xml.draw.template',
'application/vnd.sun.xml.impress',
'application/vnd.sun.xml.impress.template',
'application/vnd.sun.xml.math',
'application/vnd.sun.xml.writer',
'application/vnd.sun.xml.writer.global',
'application/vnd.sun.xml.writer.template',
'application/vnd.visio',
'application/vnd.visio2013',
'application/vnd.wordperfect',
'application/x-abiword',
'application/x-aportisdoc',
'application/x-dbase',
'application/x-dif-document',
'application/x-fictionbook+xml',
'application/x-gnumeric',
'application/x-hwp',
'application/x-iwork-keynote-sffkey',
'application/x-iwork-numbers-sffnumbers',
'application/x-iwork-pages-sffpages',
'application/x-mspublisher',
'application/x-mswrite',
'application/x-pagemaker',
'application/x-sony-bbeb',
'application/x-t602',
]
}
},
computed: {
isOpenDocument() {
if (this.mime.indexOf(this.type) !== -1) {
return true;
}
return false;
},
noText() {
if (typeof this.options.noText !== 'undefined') {
return this.options.noText === true;
}
return false;
},
isChangeIcon() {
if (typeof this.options.changeIcon !== 'undefined') {
return (!(this.options.changeIcon === null || this.options.changeIcon === ''))
}
return false;
},
isChangeClass() {
if (typeof this.options.changeClass !== 'undefined') {
return (!(this.options.changeClass === null || this.options.changeClass === ''))
}
return false;
}
},
methods: {
openModal() {
this.loading = true;
this.modal.showModal = true;
},
loaded() {
this.loading = false;
}
},
i18n: {
messages: {
fr: {
online_edit_document: "Éditer en ligne",
save_and_quit: "Enregistrer et quitter",
loading: "Chargement de l'éditeur en ligne",
invalid_title: "Format incompatible",
invalid_message: "Désolé, ce format de document n'est pas éditable en ligne.",
}
}
}
}
// Props
const props = defineProps({
wopiUrl: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
options: {
type: Object,
required: false,
},
});
// data
const modal = ref({
showModal: false,
modalDialogClass: "modal-fullscreen",
});
const logoImg = logo;
const loading = ref(false);
// MIME types
const mime = [
// TODO temporary hardcoded. to be replaced by twig extension or a collabora server query
'application/clarisworks',
'application/coreldraw',
'application/macwriteii',
'application/msword',
'application/pdf',
'application/vnd.lotus-1-2-3',
'application/vnd.ms-excel',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'application/vnd.ms-excel.sheet.macroEnabled.12',
'application/vnd.ms-excel.template.macroEnabled.12',
'application/vnd.ms-powerpoint',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'application/vnd.ms-powerpoint.template.macroEnabled.12',
'application/vnd.ms-visio.drawing',
'application/vnd.ms-word.document.macroEnabled.12',
'application/vnd.ms-word.template.macroEnabled.12',
'application/vnd.ms-works',
'application/vnd.oasis.opendocument.chart',
'application/vnd.oasis.opendocument.formula',
'application/vnd.oasis.opendocument.graphics',
'application/vnd.oasis.opendocument.graphics-flat-xml',
'application/vnd.oasis.opendocument.graphics-template',
'application/vnd.oasis.opendocument.presentation',
'application/vnd.oasis.opendocument.presentation-flat-xml',
'application/vnd.oasis.opendocument.presentation-template',
'application/vnd.oasis.opendocument.spreadsheet',
'application/vnd.oasis.opendocument.spreadsheet-flat-xml',
'application/vnd.oasis.opendocument.spreadsheet-template',
'application/vnd.oasis.opendocument.text',
'application/vnd.oasis.opendocument.text-flat-xml',
'application/vnd.oasis.opendocument.text-master',
'application/vnd.oasis.opendocument.text-master-template',
'application/vnd.oasis.opendocument.text-template',
'application/vnd.oasis.opendocument.text-web',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'application/vnd.openxmlformats-officedocument.presentationml.template',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'application/vnd.sun.xml.calc',
'application/vnd.sun.xml.calc.template',
'application/vnd.sun.xml.chart',
'application/vnd.sun.xml.draw',
'application/vnd.sun.xml.draw.template',
'application/vnd.sun.xml.impress',
'application/vnd.sun.xml.impress.template',
'application/vnd.sun.xml.math',
'application/vnd.sun.xml.writer',
'application/vnd.sun.xml.writer.global',
'application/vnd.sun.xml.writer.template',
'application/vnd.visio',
'application/vnd.visio2013',
'application/vnd.wordperfect',
'application/x-abiword',
'application/x-aportisdoc',
'application/x-dbase',
'application/x-dif-document',
'application/x-fictionbook+xml',
'application/x-gnumeric',
'application/x-hwp',
'application/x-iwork-keynote-sffkey',
'application/x-iwork-numbers-sffnumbers',
'application/x-iwork-pages-sffpages',
'application/x-mspublisher',
'application/x-mswrite',
'application/x-pagemaker',
'application/x-sony-bbeb',
'application/x-t602',
]
// Computed
const isOpenDocument = computed(() => mime.includes(props.type));
const noText = computed(() => props.options?.noText === true);
const isChangeIcon = computed(() => !!props.options?.changeIcon);
const isChangeClass = computed(() => !!props.options?.changeClass);
// Methods
const openModal = () => {
loading.value = true;
modal.value.showModal = true;
};
const loaded = () => {
loading.value = false;
};
</script>
<style lang="scss">

View File

@@ -32,7 +32,7 @@ abstract class AbstractSearch implements SearchInterface
{
try {
return new \DateTime($string);
} catch (ParsingException $ex) {
} catch (\Exception $ex) {
$exception = new ParsingException('The date is '
.'not parsable', 0, $ex);

View File

@@ -15,11 +15,11 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Translation\TranslatorInterface;
final class TranslatableStringHelper implements TranslatableStringHelperInterface
final readonly class TranslatableStringHelper implements TranslatableStringHelperInterface
{
private readonly string $defaultLocale;
private string $defaultLocale;
public function __construct(private readonly RequestStack $requestStack, private readonly TranslatorInterface $translator, ParameterBagInterface $parameterBag)
public function __construct(private RequestStack $requestStack, private TranslatorInterface $translator, ParameterBagInterface $parameterBag)
{
$this->defaultLocale = $parameterBag->get('kernel.default_locale');
}

View File

@@ -74,3 +74,7 @@ services:
Chill\MainBundle\Command\SynchronizeEntityInfoViewsCommand:
tags:
- {name: console.command}
Chill\MainBundle\Command\DetectTranslationDuplicatesCommand:
tags:
- { name: console.command }

View File

@@ -45,6 +45,12 @@ workflow:
few {# workflows}
other {# workflows}
}
associated: >-
{aw, plural,
=0 {Aucun workflow associé}
one {# Workflow associé}
other {# Workflows associés}
}
duration:
minute: >-

View File

@@ -1,3 +1,7 @@
modal:
action:
close: Fermer
"This program is free software: you can redistribute it and/or modify it under the terms of the <strong>GNU Affero General Public License</strong>": "Ce programme est un logiciel libre: vous pouvez le redistribuer et/ou le modifier selon les termes de la licence <strong>GNU Affero GPL</strong>"
User manual: Manuel d'utilisation
Search: Rechercher
@@ -102,6 +106,48 @@ address:
address_homeless: L'adresse est-elle celle d'un domicile fixe ?
real address: Adresse d'un domicile
consider homeless: Cette adresse est incomplète
add_an_address_title: Créer une adresse
edit_an_address_title: Modifier une adresse
create_a_new_address: Créer une nouvelle adresse
edit_address: Modifier l'adresse
select_an_address_title: Sélectionner une adresse
fill_an_address: Compléter l'adresse
select_country: Choisir le pays
country: Pays
select_city: Choisir une localité
city: Localité
other_city: Autre localité
select_address: Choisir une adresse
address: Adresse
other_address: Autre adresse
create_address: Adresse inconnue. Cliquez ici pour créer une nouvelle adresse
isNoAddress: Pas d'adresse complète
isConfidential: Adresse confidentielle
street: Nom de rue
streetNumber: Numéro
floor: Étage
corridor: Couloir
steps: Escalier
flat: Appartement
buildingName: Résidence
extra: Complément d'adresse
distribution: Cedex
create_postal_code: Localité inconnue. Cliquez ici pour créer une nouvelle localité
postalCode_name: Nom
postalCode_code: Code postal
date: Date de la nouvelle adresse
valid_from: L'adresse est valable à partir du
valid_to: L'adresse est valable jusqu'au
back_to_the_list: Retour à la liste
loading: chargement en cours...
address_suggestions: Suggestion d'adresses
address_new_success: La nouvelle adresse est enregistrée.
address_edit_success: L'adresse a été mise à jour.
wait_redirection: La page est redirigée.
not_yet_address: Il n'y a pas encore d'adresse. Cliquez sur '+ Créer une adresse'
use_this_address: Utiliser cette adresse
household:
move_date: Date du déménagement
address more:
floor: ét
corridor: coul
@@ -474,6 +520,8 @@ Follow workflow: Suivre la décision
Workflow history: Historique de la décision
workflow:
list: Liste des workflows associés
associated: workflow associé
Created by: Créé par
My decision: Ma décision
Next step: Prochaine étape
@@ -521,13 +569,17 @@ workflow:
Previous workflow transitionned help: Workflows où vous avez exécuté une action.
For: Pour
Cc: Cc
At: Le
You must select a next step, pick another decision if no next steps are available: Il faut une prochaine étape. Choissisez une autre décision si nécessaire.
An access key was also sent to those addresses: Un lien d'accès a été envoyé à ces adresses
Those users are also granted to apply a transition by using an access key: Ces utilisateurs ont obtenu l'accès grâce au lien reçu par email
Access link copied: Lien d'accès copié
This link grant any user to apply a transition: Le lien d'accès suivant permet d'appliquer une transition
The workflow may be accssed through this link: Une transition peut être appliquée sur ce workflow grâce au lien d'accès suivant
subscribe_final: Recevoir une notification à l'étape finale
unsubscribe_final: Ne plus recevoir de notification à l'étape finale
subscribe_all_steps: Recevoir une notification à chaque étape du suivi
unsubscribe_all_steps: Ne plus recevoir de notification à chaque étape du suivi
Subscribe final: Recevoir une notification à l'étape finale
Subscribe all steps: Recevoir une notification à chaque étape
@@ -571,6 +623,8 @@ notification:
dest by email help: Les adresses email mentionnées ici recevront un lien d'accès. Un compte utilisateur sera toujours nécessaire.
Remove an email: Supprimer l'adresse email
Email with access link: Adresse email ayant reçu un lien d'accès
mark_as_read: Marquer comme lu
mark_as_unread: Marquer comme non-lu
export:
address_helper:
@@ -704,4 +758,40 @@ news:
read_more: Lire la suite
show_details: Voir l'actualité
wopi:
online_edit_document: Éditer en ligne
save_and_quit: Enregistrer et quitter
loading: Chargement de l'éditeur en ligne
invalid_title: Format incompatible
invalid_message: Désolé, ce format de document n'est pas éditable en ligne.
onthefly:
show:
person: Détails de l'usager
thirdparty: Détails du tiers
file_person: Ouvrir la fiche de l'usager
file_thirdparty: Voir le Tiers
edit:
person: Modifier un usager
thirdparty: Modifier un tiers
create:
button: Créer {q}
title:
default: Création d'un nouvel usager ou d'un tiers professionnel
person: Création d'un nouvel usager
thirdparty: Création d'un nouveau tiers professionnel
person: un nouvel usager
thirdparty: un nouveau tiers professionnel
addContact:
title: Créer un contact pour {q}
resource_comment_title: Un commentaire est associé à cet interlocuteur
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_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é'

View File

@@ -20,13 +20,13 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final class OriginAggregator implements AggregatorInterface
final readonly class OriginAggregator implements AggregatorInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(
EntityManagerInterface $em,
private readonly TranslatableStringHelper $translatableStringHelper
private TranslatableStringHelper $translatableStringHelper
) {
$this->repository = $em->getRepository(Origin::class);
}

View File

@@ -21,9 +21,9 @@ use Doctrine\ORM\EntityRepository;
* @method AccompanyingPeriodWorkGoal[] findAll()
* @method AccompanyingPeriodWorkGoal[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
final class AccompanyingPeriodWorkGoalRepository
final readonly class AccompanyingPeriodWorkGoalRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class CommentRepository
final readonly class CommentRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class OriginRepository
final readonly class OriginRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class ResourceRepository
final readonly class ResourceRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class AccompanyingPeriodParticipationRepository
final readonly class AccompanyingPeriodParticipationRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -18,9 +18,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class AccompanyingPeriodRepository implements ObjectRepository
final readonly class AccompanyingPeriodRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -16,9 +16,9 @@ use Chill\PersonBundle\Entity\Household\HouseholdComposition;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class HouseholdCompositionRepository implements HouseholdCompositionRepositoryInterface
final readonly class HouseholdCompositionRepository implements HouseholdCompositionRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\PersonBundle\Entity\Household\HouseholdCompositionType;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class HouseholdCompositionTypeRepository implements HouseholdCompositionTypeRepositoryInterface
final readonly class HouseholdCompositionTypeRepository implements HouseholdCompositionTypeRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class HouseholdMembersRepository
final readonly class HouseholdMembersRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -18,7 +18,7 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\Persistence\ObjectRepository;
final class HouseholdRepository implements ObjectRepository
final readonly class HouseholdRepository implements ObjectRepository
{
private const SQL_BY_ACCOMPANYING_PERIOD_PARTICIPATION = <<<'SQL'
WITH participations AS (
@@ -40,9 +40,9 @@ final class HouseholdRepository implements ObjectRepository
SELECT {select} FROM households {limits}
SQL;
private readonly EntityManagerInterface $em;
private EntityManagerInterface $em;
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -16,9 +16,9 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
final class PersonHouseholdAddressRepository implements ObjectRepository
final readonly class PersonHouseholdAddressRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{

View File

@@ -18,9 +18,9 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
final class PositionRepository implements ObjectRepository
final readonly class PositionRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\PersonBundle\Entity\PersonAltName;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class PersonAltNameRepository
final readonly class PersonAltNameRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -16,9 +16,9 @@ use Chill\PersonBundle\Entity\PersonNotDuplicate;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class PersonNotDuplicateRepository
final readonly class PersonNotDuplicateRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -17,9 +17,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class PersonResourceRepository implements ObjectRepository
final readonly class PersonResourceRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class EvaluationRepository implements EvaluationRepositoryInterface
final readonly class EvaluationRepository implements EvaluationRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -18,9 +18,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class GoalRepository implements ObjectRepository
final readonly class GoalRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -19,9 +19,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class ResultRepository implements ObjectRepository
final readonly class ResultRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -17,9 +17,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class SocialActionRepository implements ObjectRepository
final readonly class SocialActionRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -17,9 +17,9 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
final class SocialIssueRepository implements ObjectRepository
final readonly class SocialIssueRepository implements ObjectRepository
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -47,25 +47,25 @@ use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @see PersonContextTest
*/
final class PersonContext implements PersonContextInterface
final readonly class PersonContext implements PersonContextInterface
{
private readonly bool $showScopes;
private bool $showScopes;
public function __construct(
private readonly AuthorizationHelperInterface $authorizationHelper,
private readonly BaseContextData $baseContextData,
private readonly CenterResolverManagerInterface $centerResolverManager,
private readonly DocumentCategoryRepository $documentCategoryRepository,
private readonly EntityManagerInterface $em,
private readonly NormalizerInterface $normalizer,
private AuthorizationHelperInterface $authorizationHelper,
private BaseContextData $baseContextData,
private CenterResolverManagerInterface $centerResolverManager,
private DocumentCategoryRepository $documentCategoryRepository,
private EntityManagerInterface $em,
private NormalizerInterface $normalizer,
ParameterBagInterface $parameterBag,
private readonly ScopeRepositoryInterface $scopeRepository,
private readonly Security $security,
private readonly TranslatorInterface $translator,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly ThirdPartyRender $thirdPartyRender,
private readonly ThirdPartyRepository $thirdPartyRepository,
private readonly ResidentialAddressRepository $residentialAddressRepository
private ScopeRepositoryInterface $scopeRepository,
private Security $security,
private TranslatorInterface $translator,
private TranslatableStringHelperInterface $translatableStringHelper,
private ThirdPartyRender $thirdPartyRender,
private ThirdPartyRepository $thirdPartyRepository,
private ResidentialAddressRepository $residentialAddressRepository
) {
$this->showScopes = $parameterBag->get('chill_main')['acl']['form_show_scopes'];
}

View File

@@ -27,18 +27,18 @@ use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Mime\MimeTypes;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
final class ChillDocumentManager implements DocumentManagerInterface
final readonly class ChillDocumentManager implements DocumentManagerInterface
{
private readonly RequestInterface $request;
private RequestInterface $request;
public function __construct(
private readonly DocumentLockManagerInterface $documentLockManager,
private readonly EntityManagerInterface $entityManager,
private DocumentLockManagerInterface $documentLockManager,
private EntityManagerInterface $entityManager,
HttpMessageFactoryInterface $httpMessageFactory,
private readonly Psr17Interface $psr17,
private Psr17Interface $psr17,
RequestStack $requestStack,
private readonly StoredObjectManagerInterface $storedObjectManager,
private readonly StoredObjectRepository $storedObjectRepository
private StoredObjectManagerInterface $storedObjectManager,
private StoredObjectRepository $storedObjectRepository
) {
$this->request = $httpMessageFactory->createRequest($requestStack->getCurrentRequest());
}