Merge remote-tracking branch 'origin/master' into rector/rules-up-to-php74

This commit is contained in:
Julien Fastré 2023-04-27 23:32:31 +02:00
commit 47d0334b9e
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
64 changed files with 584 additions and 577 deletions

View File

@ -47,7 +47,6 @@
"symfony/monolog-bundle": "^3.5",
"symfony/security-bundle": "^4.4",
"symfony/serializer": "^5.3",
"symfony/swiftmailer-bundle": "^3.5",
"symfony/templating": "^4.4",
"symfony/translation": "^4.4",
"symfony/twig-bundle": "^4.4",

View File

@ -2631,11 +2631,6 @@ parameters:
count: 2
path: src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php
-
message: "#^Call to method getRoleScopes\\(\\) on an unknown class Chill\\\\MainBundle\\\\Entity\\\\PermissionGroup\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php
-
message: "#^Empty array passed to foreach\\.$#"
count: 1

View File

@ -41,7 +41,6 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
@ -213,7 +212,7 @@ final class ActivityController extends AbstractController
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $this->centerResolver->resolveCenters($entity)[0] ?? null,
'role' => new Role('CHILL_ACTIVITY_UPDATE'),
'role' => 'CHILL_ACTIVITY_UPDATE',
'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod,
]);
@ -442,7 +441,7 @@ final class ActivityController extends AbstractController
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $this->centerResolver->resolveCenters($entity)[0] ?? null,
'role' => new Role('CHILL_ACTIVITY_CREATE'),
'role' => 'CHILL_ACTIVITY_CREATE',
'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod,
]);

View File

@ -120,7 +120,6 @@ class ListActivitiesByAccompanyingPeriodContext implements
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$denormalized = $this->accompanyingPeriodContext->contextGenerationDataDenormalize($template, $entity, $data);
foreach (['myActivitiesOnly', 'myWorksOnly'] as $k) {

View File

@ -106,6 +106,9 @@ CHILL_ACTIVITY_SEE_DETAILS: Voir le détail des échanges
CHILL_ACTIVITY_DELETE: Supprimer un échange
CHILL_ACTIVITY_STATS: Statistique des échanges
CHILL_ACTIVITY_LIST: Liste des échanges
CHILL_ACTIVITY_CREATE_PERSON: Créer un échange lié à un usager
CHILL_ACTIVITY_CREATE_ACCOMPANYING_COURSE: Créer un échange lié à un parcours
CHILL_ACTIVITY_FULL: Voir les détails, créer, supprimer et mettre à jour un échange
# admin
Activities: Échanges
@ -116,7 +119,7 @@ Activity type configuration: Configuration des catégories d'échanges
Activity Reasons: Sujets d'un échange
Activity Reasons Category: Catégories de sujet d'échanges
Activity Types Categories: Catégories des types d'échange
Activity Presences: Presences aux échanges
Activity Presences: Présences aux échanges
Associated activity reason category is inactive: La catégorie de sujet attachée est inactive
@ -124,13 +127,13 @@ Associated activity reason category is inactive: La catégorie de sujet attaché
crud:
activity_type:
title_new: Nouveau type d'échange
title_edit: Edition d'un type d'activité
title_edit: Édition d'un type d'échange
activity_type_category:
title_new: Nouvelle catégorie de type d'activité
title_edit: Edition d'une catégorie de type d'activité
title_new: Nouvelle catégorie de type d'échange
title_edit: Édition d'une catégorie de type d'échange
activity_presence:
title_new: Nouvelle Présence aux activités
title_edit: Edition d'une Présence aux activités
title_new: Nouvelle présence aux échanges
title_edit: Édition d'une présence aux échanges
# activity reason admin
ActivityReason list: Liste des sujets
@ -139,7 +142,7 @@ Active: Actif
Category: Catégorie
ActivityReason creation: Nouveau sujet
ActivityReason edit: Modification d'un sujet
ActivityReason: Sujet d'activité
ActivityReason: Sujet d'échange
The entity is inactive and won't be proposed: Le sujet est inactif et ne sera pas proposé
The entity is active and will be proposed: Le sujet est actif et sera proposé
@ -148,17 +151,17 @@ ActivityReasonCategory list: Catégories de sujets
Create a new activity category reason: Créer une nouvelle catégorie
ActivityReasonCategory creation: Nouvelle catégorie de sujet
ActivityReasonCategory edit: Modification d'une catégorie de sujet
ActivityReasonCategory: Catégorie de sujet d'activité
ActivityReasonCategory: Catégorie de sujet d'échange
ActivityReasonCategory is active and will be proposed: La catégorie est active et sera proposée
ActivityReasonCategory is inactive and won't be proposed: La catégorie est inactive et ne sera pas proposée
#activity presence admin
ActivityPresence list: Liste des Présences aux activités
Create a new activity presence: Créer une nouvelle "Présence aux activités"
ActivityPresence list: Liste des présences aux échanges
Create a new activity presence: Créer une nouvelle "Présence aux échanges"
# activity type type admin
ActivityType list: Types d'activités
Create a new activity type: Créer un nouveau type d'activité
ActivityType list: Types d'échanges
Create a new activity type: Créer un nouveau type d'échange
Persons visible: Visibilité du champ Usagers
Persons label: Libellé du champ Usagers
User visible: Visibilité du champ Utilisateur
@ -197,135 +200,135 @@ Documents visible: Visibilité du champ Documents
Documents label: Libellé du champ Documents
# activity type category admin
ActivityTypeCategory list: Liste des catégories des types d'activité
Create a new activity type category: Créer une nouvelle catégorie de type d'activité
ActivityTypeCategory list: Liste des catégories des types d'échange
Create a new activity type category: Créer une nouvelle catégorie de type d'échange
# activity delete
Remove activity: Supprimer une activité
Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr de vouloir supprimer une activité qui concerne "%name%" ?
The activity has been successfully removed.: L'activité a été supprimée.
Remove activity: Supprimer un échange
Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr de vouloir supprimer un échange qui concerne "%name%" ?
The activity has been successfully removed.: L'échange a été supprimé.
# exports
Exports of activities linked to a person: Exports des activités liées à un usager
Number of activities linked to a person: Nombre d'activités liées à un usager
Count activities linked to a person: Nombre d'activités
Count activities linked to a person by various parameters.: Compte le nombre d'activités enregistrées et liées à un usager en fonction de différents paramètres.
Sum activity linked to a person duration: Durée des activités
Sum activities linked to a person duration: Durée des activités liés à un usager
Sum activities linked to a person duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
List activity linked to a person: Liste les activités
List activities linked to a person: Liste des activités liés à un usager
List activities linked to a person description: Crée la liste des activités en fonction de différents paramètres.
Exports of activities linked to a person: Exports des échanges liés à un usager
Number of activities linked to a person: Nombre d'échanges liés à un usager
Count activities linked to a person: Nombre d'échanges
Count activities linked to a person by various parameters.: Compte le nombre d'échanges enregistrés et liés à un usager en fonction de différents paramètres.
Sum activity linked to a person duration: Durée des échanges
Sum activities linked to a person duration: Durée des échanges liés à un usager
Sum activities linked to a person duration by various parameters.: Additionne la durée des échanges en fonction de différents paramètres.
List activity linked to a person: Liste les échanges
List activities linked to a person: Liste des échanges liés à un usager
List activities linked to a person description: Crée la liste des échanges en fonction de différents paramètres.
Exports of activities linked to an accompanying period: Exports des activités liées à un parcours
Number of activities linked to an accompanying period: Nombre d'activités liées à un parcours
Count activities linked to an accompanying period: Nombre d'activités
Count activities linked to an accompanying period by various parameters.: Compte le nombre d'activités enregistrées et liées à un parcours en fonction de différents paramètres.
Sum activity linked to an accompanying period duration: Somme de la durée des activités
Sum activities linked to an accompanying period duration: Somme de la durée des activités liées à un parcours
Sum activities linked to an accompanying period duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
Sum activity linked to an accompanying period visit duration: Somme de la durée de déplacement des activités
Sum activities linked to an accompanying period visit duration: Somme de la durée de déplacement des activités liées à un parcours
Sum activities linked to an accompanying period visit duration by various parameters.: Additionne la durée de déplacement des activités en fonction de différents paramètres.
Average activity linked to an accompanying period duration: Moyenne de la durée des activités
Average activities linked to an accompanying period duration: Moyenne de la durée des activités liées à un parcours
Average activities linked to an accompanying period duration by various parameters.: Moyenne de la durée des activités en fonction de différents paramètres.
Average activity linked to an accompanying period visit duration: Moyenne de la durée de déplacement des activités
Average activities linked to an accompanying period visit duration: Moyenne de la durée de déplacement des activités liées à un parcours
Average activities linked to an accompanying period visit duration by various parameters.: Moyenne de la durée de déplacement des activités en fonction de différents paramètres.
Exports of activities linked to an accompanying period: Exports des échanges liés à un parcours
Number of activities linked to an accompanying period: Nombre d'échanges liés à un parcours
Count activities linked to an accompanying period: Nombre d'échanges
Count activities linked to an accompanying period by various parameters.: Compte le nombre d'échanges enregistrés et liées à un parcours en fonction de différents paramètres.
Sum activity linked to an accompanying period duration: Somme de la durée des échanges
Sum activities linked to an accompanying period duration: Somme de la durée des échanges liés à un parcours
Sum activities linked to an accompanying period duration by various parameters.: Additionne la durée des échanges en fonction de différents paramètres.
Sum activity linked to an accompanying period visit duration: Somme de la durée de déplacement des échanges
Sum activities linked to an accompanying period visit duration: Somme de la durée de déplacement des échanges liés à un parcours
Sum activities linked to an accompanying period visit duration by various parameters.: Additionne la durée de déplacement des échanges en fonction de différents paramètres.
Average activity linked to an accompanying period duration: Moyenne de la durée des échanges
Average activities linked to an accompanying period duration: Moyenne de la durée des échanges liés à un parcours
Average activities linked to an accompanying period duration by various parameters.: Moyenne de la durée des échanges en fonction de différents paramètres.
Average activity linked to an accompanying period visit duration: Moyenne de la durée de déplacement des échanges
Average activities linked to an accompanying period visit duration: Moyenne de la durée de déplacement des échanges liés à un parcours
Average activities linked to an accompanying period visit duration by various parameters.: Moyenne de la durée de déplacement des échanges en fonction de différents paramètres.
#filters
Filter by reason: Filtrer les activités par sujet
Filter by reason: Filtrer les échanges par sujet
'Filtered by reasons: only %list%': 'Filtré par sujet: seulement %list%'
'Filtered by activity type: only %list%': "Filtré par type d'activité: uniquement %list%"
Filtered by date activity: Filtrer les activités par date
Activities after this date: Activités après cette date
Activities before this date: Activités avant cette date
"Filtered by date of activity: only between %date_from% and %date_to%": "Filtré par date de l'activité: uniquement entre %date_from% et %date_to%"
This date should be after the date given in "Implied in an activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités après cette date"
'Filtered by activity type: only %list%': "Filtré par type d'échange: uniquement %list%"
Filtered by date activity: Filtrer les échanges par date
Activities after this date: Échanges après cette date
Activities before this date: Échanges avant cette date
"Filtered by date of activity: only between %date_from% and %date_to%": "Filtré par date de l'échange: uniquement entre %date_from% et %date_to%"
This date should be after the date given in "Implied in an activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "échanges après cette date"
Filtered by person having an activity in a period: Uniquement les usagers ayant eu une activité dans la période donnée
Implied in an activity after this date: Impliqué dans une activité après cette date
Implied in an activity before this date: Impliqué dans une activité avant cette date
Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Filtré par usager associées à une activité entre %date_from% et %date_to% avec les sujets %reasons_name%
Activity reasons for those activities: Sujets de ces activités
Filtered by person having an activity in a period: Uniquement les usagers ayant eu un échange dans la période donnée
Implied in an activity after this date: Impliqué dans un échange après cette date
Implied in an activity before this date: Impliqué dans un échange avant cette date
Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Filtré par usager associées à un échange entre %date_from% et %date_to% avec les sujets %reasons_name%
Activity reasons for those activities: Sujets de ces échanges
Filter by activity type: Filtrer les activités par type
Filter by activity type: Filtrer les échanges par type
Filter activity by location: Filtrer les activités par localisation
Filter activity by location: Filtrer les échanges par localisation
'Filtered activity by location: only %locations%': "Filtré par localisation: uniquement %locations%"
Filter activity by locationtype: Filtrer les activités par type de localisation
Filter activity by locationtype: Filtrer les échanges par type de localisation
'Filtered activity by locationtype: only %types%': "Filtré par type de localisation: uniquement %types%"
Accepted locationtype: Types de localisation
Accepted users: TMS(s)
Filter activity by emergency: Filtrer les activités par urgence
Filter activity by emergency: Filtrer les échanges par urgence
'Filtered activity by emergency: only %emergency%': "Filtré par urgence: uniquement si %emergency%"
activity is emergency: l'activité est urgente
activity is not emergency: l'activité n'est pas urgente
Filter activity by sentreceived: Filtrer les activités par envoyé/reçu
activity is emergency: l'échange est urgent
activity is not emergency: l'échange n'est pas urgent
Filter activity by sentreceived: Filtrer les échanges par envoyé/reçu
'Filtered activity by sentreceived: only %sentreceived%': "Filtré par envoyé/reçu: uniquement %sentreceived%"
Accepted sentreceived: ''
Filter activity by linked socialaction: Filtrer les activités par action liée
Filter activity by linked socialaction: Filtrer les échanges par action liée
'Filtered activity by linked socialaction: only %actions%': "Filtré par action liée: uniquement %actions%"
Filter activity by linked socialissue: Filtrer les activités par problématique liée
Filter activity by linked socialissue: Filtrer les échanges par problématique liée
'Filtered activity by linked socialissue: only %issues%': "Filtré par problématique liée: uniquement %issues%"
Filter activity by user: Filtrer les activités par créateur
Filter activity by users: Filtrer les activités par utilisateur participant
Filter activity by creator: Filtrer les activités par créateur de l'échange
Filter activity by user: Filtrer les échanges par créateur
Filter activity by users: Filtrer les échanges par utilisateur participant
Filter activity by creator: Filtrer les échanges par créateur de l'échange
'Filtered activity by user: only %users%': "Filtré par référent: uniquement %users%"
'Filtered activity by users: only %users%': "Filtré par utilisateurs participants: uniquement %users%"
'Filtered activity by creator: only %users%': "Filtré par créateur: uniquement %users%"
Creators: Créateurs
Filter activity by userscope: Filtrer les activités par service du créateur
Filter activity by userscope: Filtrer les échanges par service du créateur
'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%"
Accepted userscope: Services
Filter acp which has no activity: Filtrer les parcours qui nont pas dactivité
Filtered acp which has no activities: Filtrer les parcours sans activité associée
Group acp by activity number: Grouper les parcours par nombre dactivité
Filter acp which has no activity: Filtrer les parcours qui nont pas déchange
Filtered acp which has no activities: Filtrer les parcours sans échange associé
Group acp by activity number: Grouper les parcours par nombre déchange
#aggregators
Activity type: Type d'activité
Activity user: Utilisateur lié à l'activité
Activity type: Type d'échange
Activity user: Utilisateur lié à l'échange
By reason: Par sujet
By category of reason: Par catégorie de sujet
Reason's level: Niveau du sujet
Group by reasons: Sujet d'activité
Aggregate by activity user: Grouper les activités par référent
Aggregate by activity users: Grouper les activités par utilisateurs participants
Aggregate by activity type: Grouper les activités par type
Aggregate by activity reason: Grouper les activités par sujet
Aggregate by users scope: Grouper les activités par service principal de l'utilisateur
Users 's scope: Service principal des utilisateurs participants à l'activité
Aggregate by users job: Grouper les activités par métier des utilisateurs participants
Users 's job: Métier des utilisateurs participants à l'activité
Group by reasons: Sujet d'échange
Aggregate by activity user: Grouper les échanges par référent
Aggregate by activity users: Grouper les échanges par utilisateurs participants
Aggregate by activity type: Grouper les échanges par type
Aggregate by activity reason: Grouper les échanges par sujet
Aggregate by users scope: Grouper les échanges par service principal de l'utilisateur
Users 's scope: Service principal des utilisateurs participants à l'échange
Aggregate by users job: Grouper les échanges par métier des utilisateurs participants
Users 's job: Métier des utilisateurs participants à l'échange
Group activity by locationtype: Grouper les activités par type de localisation
Group activity by date: Grouper les activités par date
Group activity by locationtype: Grouper les échanges par type de localisation
Group activity by date: Grouper les échanges par date
Frequency: Fréquence
by month: Par mois
by week: Par semaine
for week: Semaine
by year: Par année
in year: En
Group activity by creator: Grouper les activités par créateur de l'échange
Group activity by creator scope: Grouper les activités par service du créateur de l'échange
Group activity by linked thirdparties: Grouper les activités par tiers impliqué
Group activity by creator: Grouper les échanges par créateur de l'échange
Group activity by creator scope: Grouper les échanges par service du créateur de l'échange
Group activity by linked thirdparties: Grouper les échanges par tiers impliqué
Accepted thirdparty: Tiers impliqué
Group activity by linked socialaction: Grouper les activités par action liée
Group activity by linked socialissue: Grouper les activités par problématique liée
Group activity by userscope: Grouper les activités par service du créateur
Group activity by linked socialaction: Grouper les échanges par action liée
Group activity by linked socialissue: Grouper les échanges par problématique liée
Group activity by userscope: Grouper les échanges par service du créateur
Last activities: Les dernières activités
Last activities: Les derniers échanges
See activity in accompanying course context: Voir l'activité dans le contexte du parcours d'accompagnement
See activity in accompanying course context: Voir l'échange dans le contexte du parcours d'accompagnement
You get notified of an activity which does not exists any more: Cette notification ne correspond pas à une activité valide.
you are not allowed to see it details: La notification fait référence à une activité à laquelle vous n'avez pas accès.
This is the minimal activity data: Activité
You get notified of an activity which does not exists any more: Cette notification ne correspond pas à un échange valide.
you are not allowed to see it details: La notification fait référence à un échange auquel vous n'avez pas accès.
This is the minimal activity data: Échange
docgen:
Activity basic: Echange
Activity basic: Échange
A basic context for activity: Contexte pour les échanges
Accompanying period with a list of activities: Parcours d'accompagnement avec liste des échanges
Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les échanges pour un parcours. Les échanges ne sont pas filtrés.
@ -341,7 +344,7 @@ export:
persons ids: Identifiant des usagers
persons name: Nom des usagers
thirds parties: Tiers
date: Date de l'activité
date: Date de l'échange
locationName: Localisation
sent received: Envoyé ou reçu
emergency: Urgence
@ -350,17 +353,17 @@ export:
travelTime: Durée de déplacement
durationTime: Durée
id: Identifiant
List activities linked to an accompanying course: Liste les activités liées à un parcours en fonction de différents filtres.
List activity linked to a course: Liste des activités liées à un parcours
List activities linked to an accompanying course: Liste les échanges liés à un parcours en fonction de différents filtres.
List activity linked to a course: Liste des échanges liés à un parcours
filter:
activity:
by_usersjob:
Filter by users job: Filtrer les activités par métier d'au moins un utilisateur participant
Filter by users job: Filtrer les échanges par métier d'au moins un utilisateur participant
'Filtered activity by users job: only %jobs%': 'Filtré par métier d''au moins un utilisateur participant: seulement %jobs%'
by_usersscope:
Filter by users scope: Filtrer les activités par services d'au moins un utilisateur participant
Filter by users scope: Filtrer les échanges par services d'au moins un utilisateur participant
'Filtered activity by users scope: only %scopes%': 'Filtré par service d''au moins un utilisateur participant: seulement %scopes%'
aggregator:
activity:
@ -368,4 +371,4 @@ export:
Sent or received: Envoyé ou reçu
is sent: envoyé
is received: reçu
Group activity by sentreceived: Grouper les activités par envoyé / reçu
Group activity by sentreceived: Grouper les échanges par envoyé / reçu

View File

@ -1,39 +1,42 @@
.subtitle {
h3.subtitle {
margin-top: 1rem;
margin-bottom: 1rem;
padding: 1rem;
&::before {
font: normal normal normal 20px/1 ForkAwesome;
margin-right: 0.5em;
content: "\f061";
}
}
.family-title {
$col_charge: #e03851d7;
$col_resource: #6d9e63d8;
h4.family-title {
margin-top: 1.5rem;
margin-bottom: 1rem !important;
padding-left: 0.7em;
i {
margin-right: 0.4em;
}
&.charge i { color: $col_charge; }
&.resource i { color: $col_resource; }
}
.budget-table th {
th {
color: white;
}
}
.budget-table {
th.charge {
background-color: #e03851d7;
}
}
.budget-table {
th.resource {
background-color: #6d9e63d8;
}
}
.budget-table {
table.budget-table {
th, td {
padding: 10px;
text-align: right;
}
td.column-wide {
width: 20%;
}
td.column-small {
width: 15%;
&.right {
align-items: right;
}
th.charge { background-color: $col_charge; }
th.resource { background-color: $col_resource; }
td.column-fixed {
width: 9.5em;
}
}
@ -58,4 +61,4 @@
button[aria-expanded="true"] > span.folded,
button[aria-expanded="false"] > span.unfolded { display: none; }
button[aria-expanded="false"] > span.folded,
button[aria-expanded="true"] > span.unfolded { display: inline; }
button[aria-expanded="true"] > span.unfolded { display: inline; }

View File

@ -32,28 +32,21 @@
{% endif %}
{% endfor %}
<h3 class="subtitle">{{ 'Actual budget'|trans }}</h3>
{% if actualCharges|length > 0 or actualResources|length > 0 %}
{% include 'ChillBudgetBundle:Budget:_current_budget.html.twig' with {
{% include '@ChillBudget/Budget/_current_budget.html.twig' with {
'actualResources': actualResources,
'actualCharges': actualCharges,
'results': results,
'entity': entity
} %}
{% else %}
<div class="flex-table">
<div class="item-bloc">
<p><span class="chill-no-data-statement">{{ "There isn't any element recorded"|trans }}</span></p>
</div>
</div>
<p><span class="chill-no-data-statement">{{ "There isn't any element recorded"|trans }}</span></p>
{% endif %}
{% if pastCharges|length > 0 or pastResources|length > 0 %}
<h2 class="subtitle">{{ 'Past budget'|trans }}</h2>
{% include 'ChillBudgetBundle:Budget:_past_budget.html.twig' with {
<h3 class="subtitle">{{ 'Past budget'|trans }}</h3>
{% include '@ChillBudget/Budget/_past_budget.html.twig' with {
'pastCharges': pastCharges,
'pastResources': pastResources,
'entity': entity
@ -61,9 +54,8 @@
{% endif %}
{% if futureCharges|length > 0 or futureResources|length > 0 %}
<h2 class="subtitle">{{ 'Future budget'|trans }}</h2>
{% include 'ChillBudgetBundle:Budget:_future_budget.html.twig' with {
<h3 class="subtitle">{{ 'Future budget'|trans }}</h3>
{% include '@ChillBudget/Budget/_future_budget.html.twig' with {
'futureResources': futureResources,
'futureCharges': futureCharges,
'entity': entity

View File

@ -1,30 +1,17 @@
{% from 'ChillBudgetBundle:Budget:_macros.html.twig' import table_elements, table_results %}
{# <h2 class="subtitle">{{ 'Actual budget'|trans }}</h2> #}
<div class="flex-table">
<h4 class="family-title">{{ 'Actual resources'|trans }}</h4>
{% from '@ChillBudget/Budget/_macros.html.twig' import table_elements, table_results %}
<div class="my-4">
<h4 class="family-title resource"><i class="fa fa-fw fa-plus-square"></i>{{ 'Actual resources'|trans }}</h4>
{% if actualResources|length > 0 %}
<div class="item-bloc">
{{ table_elements(actualResources, 'resource') }}
</div>
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No resources registered'|trans }}</span>
</div>
{% endif %}
</div>
<div class="flex-table">
<h4 class="family-title">{{ 'Actual charges'|trans }}</h4>
<h4 class="family-title charge"><i class="fa fa-fw fa-minus-square"></i>{{ 'Actual charges'|trans }}</h4>
{% if actualCharges|length > 0 %}
<div class="item-bloc">
{{ table_elements(actualCharges, 'charge') }}
</div>
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No charges registered'|trans }}</span>
</div>
{% endif %}
</div>

View File

@ -20,32 +20,23 @@
aria-labelledby="heading_future_{{ entity.id }}"
data-bs-parent="#future_{{ entity.id }}">
<div class="flex-table">
<h3 class="family-title">{{ 'Future resources'|trans }}</h3>
<div class="my-4">
<h4 class="family-title resource"><i class="fa fa-fw fa-plus-square"></i>{{ 'Future resources'|trans }}</h4>
{% if futureResources|length > 0 %}
<div class="item-bloc">
{{ table_elements(futureResources, 'resource') }}
</div>
{{ table_elements(futureResources, 'resource') }}
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No future resources registered'|trans }}</span>
</div>
<span class="chill-no-data-statement">{{ 'No future resources registered'|trans }}</span>
{% endif %}
</div>
<div class="flex-table">
<h3 class="family-title">{{ 'Future charges'|trans }}</h3>
<h4 class="family-title charge"><i class="fa fa-fw fa-minus-square"></i>{{ 'Future charges'|trans }}</h4>
{% if futureCharges|length > 0 %}
<div class="item-bloc">
{{ table_elements(futureCharges, 'charge') }}
</div>
{{ table_elements(futureCharges, 'charge') }}
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No future charges registered'|trans }}</span>
</div>
<span class="chill-no-data-statement">{{ 'No future charges registered'|trans }}</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
{% macro table_elements(elements, family) %}
<table class="budget-table">
<table class="table table-bordered border-dark budget-table">
<thead>
<tr>
<th class="{{ family }} el-type">{{ 'Budget element type'|trans }}</th>
@ -13,25 +13,22 @@
{% for f in elements %}
{% set total = total + f.amount %}
<tr>
<td class="column-wide el-type">
<span class="badge-title">
<span class="title_label title_label_{{ family }}"></span>
{% if f.isResource %}
<span class="title_action">{{ f.resource.name|localize_translatable_string }}<span>
{% else %}
<span class="title_action">{{ f.charge.name|localize_translatable_string }}<span>
{% endif %}
</span>
<td class="el-type">
{% if f.isResource %}
{{ f.resource.name|localize_translatable_string }}
{% else %}
{{ f.charge.name|localize_translatable_string }}
{% endif %}
</td>
<td class="column-small">{{ f.amount|format_currency('EUR') }}</td>
<td class="column-wide">
<td>{{ f.amount|format_currency('EUR') }}</td>
<td>
{% if f.endDate is not null %}
{{ f.startDate|format_date('short') ~ ' - ' ~ f.endDate|format_date('short') }}
{% else %}
{{ f.startDate|format_date('short') ~ ' - ...' }}
{{ 'depuis le ' ~ f.startDate|format_date('short') }}
{% endif %}
</td>
<td class="column-small">
<td class="column-fixed">
<ul class="record_actions">
{% if is_granted('CHILL_BUDGET_ELEMENT_SEE', f) %}
<li>
@ -80,10 +77,9 @@
{% set result = (totalResources - totalCharges) %}
<table>
<table class="table table-bordered border-dark">
<thead>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>{{ 'Budget calculator result'|trans }}</th>
</tr>
@ -91,7 +87,6 @@
<tbody>
<tr>
<td>{{ 'The balance'|trans }}</td>
<td>&nbsp;</td>
<td>
{{ result|format_currency('EUR') }}
</td>

View File

@ -20,34 +20,24 @@
aria-labelledby="heading_past_{{ entity.id }}"
data-bs-parent="#past_{{ entity.id }}">
<div class="flex-table">
<h3 class="family-title">{{ 'Past resources'|trans }}</h3>
<div class="my-4">
<h4 class="family-title resource"><i class="fa fa-fw fa-plus-square"></i>{{ 'Past resources'|trans }}</h4>
{% if pastResources|length > 0 %}
<div class="item-bloc">
{{ table_elements(pastResources, 'resource') }}
</div>
{{ table_elements(pastResources, 'resource') }}
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No past resources registered'|trans }}</span>
</div>
<span class="chill-no-data-statement">{{ 'No past resources registered'|trans }}</span>
{% endif %}
</div>
<div class="flex-table">
<h3 class="family-title">{{ 'Past charges'|trans }}</h3>
<h4 class="family-title charge"><i class="fa fa-fw fa-minus-square"></i>{{ 'Past charges'|trans }}</h4>
{% if pastCharges|length > 0 %}
<div class="item-bloc">
{{ table_elements(pastCharges, 'charge') }}
</div>
{{ table_elements(pastCharges, 'charge') }}
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No past charges registered'|trans }}</span>
</div>
<span class="chill-no-data-statement">{{ 'No past charges registered'|trans }}</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>

View File

@ -24,16 +24,14 @@
} %}
{#
<div class="flex-table">
<h3 class="family-title">{{ 'Budget calculator'|trans }}</h3>
<div class="item-bloc">
{{ table_results(wholeCharges, wholeResources) }}
</div>
<div class="my-4">
<h4 class="family-title">{{ 'Budget calculator'|trans }}</h4>
{{ table_results(wholeCharges, wholeResources) }}
</div>
#}
{% if household.getCurrentMembers|length > 0 %}
<h2 class="subtitle">{{ 'Current budget household members'|trans }}</h2>
<h1 class="my-5">{{ 'Budget household members'|trans }}</h1>
{% for hm in household.getCurrentMembers %}
{% set member = hm.person %}
@ -57,6 +55,8 @@
aria-labelledby="heading_{{ member.id }}"
data-bs-parent="#nonCurrent">
<h2 class="mt-4">{{ 'Budget for %name%'|trans({'%name%': member.firstName ~ " " ~ member.lastName }) }}</h2>
{% include 'ChillBudgetBundle:Budget:_budget.html.twig' with {
'resources': member.getBudgetResources,
'charges': member.getBudgetCharges,

View File

@ -17,17 +17,15 @@
{% block content %}
<h1>{{ title }}</h1>
{% include 'ChillBudgetBundle:Budget:_budget.html.twig' with {
{% include '@ChillBudget/Budget/_budget.html.twig' with {
'resources': resources,
'charges': charges,
'person': person
} %}
<div class="flex-table">
<h3 class="family-title">{{ 'Budget calculator'|trans }}</h2>
<div class="item-bloc">
{{ table_results(charges, resources) }}
</div>
<div class="mt-5">
<h3 class="subtitle">{{ 'Budget calculator'|trans }}</h3>
{{ table_results(charges, resources) }}
</div>
{% if is_granted('CHILL_BUDGET_ELEMENT_CREATE', person) %}

View File

@ -9,7 +9,7 @@
{% set indexPage = 'chill_budget_elements_household_index' %}
{% set activeRouteKey = '' %}
{% set household = element.household %}
{% set confirm_question = 'Are you sure you want to remove the ressource "%type%" associated to household "%household%" ?'|trans({ '%household%' : household.id, '%type%': element.resource.getName | localize_translatable_string} ) %}
{% set confirm_question = 'Are you sure you want to remove the resource "%type%" associated to household "%household%" ?'|trans({ '%household%' : household.id, '%type%': element.resource.getName | localize_translatable_string} ) %}
{% endif %}
{% extends template %}

View File

@ -3,23 +3,23 @@ Resource: Ressource
Charge: Charge
Budget for %name%: Budget de %name%
Budget for household %household%: Budget du ménage
Current budget household members: Budget actuel des membres du ménage
Budget household members: Budget des membres du ménage
Show budget of %name%: Montrer budget de %name%
See complete budget: Voir budget complet
Hide budget: Masquer
Hide budget of %name%: Masquer budget de %name%
Resource element type: Nature de la ressource
Actual budget: Éléments actuels du budget
Actual budget: Éléments actuels
Actual resources: Ressources actuelles
Actual resources for %name%: Ressources actuelles de %name%
Actual charges for %name%: Charges actuelles de %name%
Actual charges: Charges actuelles
Past budget: Éléments du budget passé
Past budget: Éléments passés
Show past budget: Montrer budget passé
Show future budget: Montrer budget future
Past resources: Ressources passées
Past charges: Charges passées
Future budget: Futurs éléments du budget
Future budget: Éléments futurs
Future resources: Ressources futures
Future charges: Charges futures
Budget element type: Nature
@ -49,6 +49,8 @@ Remove resource: Supprimer la ressource
Remove charge: Supprimer la charge
Are you sure you want to remove the ressource "%type%" associated to "%name%" ?: Êtes-vous sûr·e de vouloir supprimer la ressource de nature "%type%" associée à %name% ?
Are you sure you want to remove the charge "%type%" associated to "%name%" ?: Êtes-vous sûr·e de vouloir supprimer la charge de nature "%type%" associée à %name% ?
Are you sure you want to remove the charge "%type%" associated to household "%household%" ?: Êtes-vous sur·e de vouloir supprimer la charge "%type%" associée au ménage ?
Are you sure you want to remove the resource "%type%" associated to household "%household%" ?: Êtes-vous sur·e de vouloir supprimer la ressource "%type%" associée au ménage ?
Resource deleted: Ressource supprimée
Charge deleted: Charge supprimée
Charge created: Charge créée

View File

@ -76,7 +76,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
'expiration' => $expiration->format(DateTimeImmutable::ATOM),
]);
while ($offset < ($total - 1)) {
while ($offset < $total) {
$users = $this->userRepository->findByMostOldSubscriptionOrWithoutSubscriptionOrData(
$interval,
$limit,

View File

@ -107,8 +107,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
'users/' . $userId . '/calendarView',
[
'query' => [
'startDateTime' => $startDate->format(DateTimeImmutable::ATOM),
'endDateTime' => $endDate->format(DateTimeImmutable::ATOM),
'startDateTime' => $startDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
'endDateTime' => $endDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
'$count' => 'true',
'$top' => 0,
],
@ -181,8 +181,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
'users/' . $userId . '/calendarView',
[
'query' => [
'startDateTime' => $startDate->format(DateTimeImmutable::ATOM),
'endDateTime' => $endDate->format(DateTimeImmutable::ATOM),
'startDateTime' => $startDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
'endDateTime' => $endDate->setTimezone(RemoteEventConverter::getRemoteTimeZone())->format(RemoteEventConverter::getRemoteDateTimeSimpleFormat()),
'$select' => 'id,subject,start,end,isAllDay',
'$top' => $limit,
'$skip' => $offset,
@ -533,10 +533,13 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$userId = $this->mapCalendarToUser->getUserId($user);
if (null === $userId) {
throw new Exception('no remote calendar for this user', [
'user' => $user->getId(),
'remoteId' => $remoteId,
]);
throw new Exception(
sprintf(
'no remote calendar for this user: %s, remoteid: %s',
$user->getId(),
$remoteId
)
);
}
try {

View File

@ -23,7 +23,7 @@ ordering: ordre
label_field: label du champ
active: actif
No value defined for this option: Pas de valeur pour cette option
CustomFieldsGroup edit: Edition d'un groupe de champs personnalisé
CustomFieldsGroup edit: Édition d'un groupe de champs personnalisé
type: type
The custom fields group has been created: Le groupe de champs personnalisés a été créé
The custom fields group has been updated: Le groupe de champs personnalisés a été mis à jour

View File

@ -27,9 +27,7 @@
</form>
</td>
<td>
<a href="{{ chill_path_add_return_path('chill_crud_docgen_template_edit', { 'id': entity.id }) }}" class="btn btn-edit">
{{ 'Edit'|trans }}
</a>
<a href="{{ chill_path_add_return_path('chill_crud_docgen_template_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'Edit'|trans }}"></a>
</td>
</tr>
{% endfor %}

View File

@ -1,6 +1,6 @@
docgen:
Generate a document: Génerer un document
Generate: Génerer
Generate a document: Générer un document
Generate: Générer
Document generation: Génération de documents
Manage templates and document generation: Gestion des documents générés et de leurs modèles
Pick template context: Choisir un contexte

View File

@ -5,14 +5,14 @@
{% block admin_content %}
<h1>{{ 'Document category list' | trans }}</h1>
<table class="table">
<table class="table table-bordered border-dark align-middle">
<thead>
<tr>
<th>{{ 'Creator bundle id' | trans }}</th>
<th>{{ 'Internal id inside creator bundle' | trans }}</th>
<th>{{ 'Document class' | trans }}</th>
<th>{{ 'Name' | trans }}</th>
<th>{{ 'Actions' | trans }}</th>
<th class="w-25">{{ 'Actions' | trans }}</th>
</tr>
</thead>
<tbody>
@ -23,7 +23,7 @@
<td>{{ document_category.documentClass }}</td>
<td>{{ document_category.name | localize_translatable_string}}</td>
<td>
<td class="text-end">
<a href="{{ path('document_category_show', {'bundleId': document_category.bundleId, 'idInsideBundle': document_category.idInsideBundle}) }}"
class="btn btn-show" title="{{ 'show' | trans }}"></a>
<a href="{{ path('document_category_edit', {'bundleId': document_category.bundleId, 'idInsideBundle': document_category.idInsideBundle}) }}"

View File

@ -274,7 +274,7 @@ class ApiController extends AbstractCRUDController
$postedData = $this->getSerializer()->deserialize($request->getContent(), $postedDataType, $_format, $postedDataContext);
} catch (\Symfony\Component\Serializer\Exception\UnexpectedValueException $e) {
throw new BadRequestHttpException(sprintf('Unable to deserialize posted ' .
'data: %s', $e->getMessage()), 0, $e);
'data: %s', $e->getMessage()), $e, 0);
}
switch ($request->getMethod()) {

View File

@ -107,5 +107,4 @@ class AddressToReferenceMatcherController
true
);
}
}

View File

@ -31,7 +31,7 @@ class GroupCenter
* )
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
*/
private $center;
private ?Center $center = null;
/**
* @var int
@ -40,83 +40,64 @@ class GroupCenter
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private ?int $id = null;
/**
* @var PermissionsGroup
*
* @ORM\ManyToOne(
* targetEntity="Chill\MainBundle\Entity\PermissionsGroup",
* inversedBy="groupCenters")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
*/
private $permissionsGroup;
private ?PermissionsGroup $permissionsGroup = null;
/**
* @var Collection
*
* @ORM\ManyToMany(
* targetEntity="Chill\MainBundle\Entity\User",
* mappedBy="groupCenters"
* )
* @var Collection<User::class>
*/
private $users;
private Collection $users;
/**
* GroupCenter constructor.
*/
public function __construct()
{
$this->permissionsGroup = new ArrayCollection();
$this->users = new ArrayCollection();
}
/**
* @return Center
*/
public function getCenter()
public function getCenter(): ?Center
{
return $this->center;
}
/**
* @return int
*/
public function getId()
public function getId(): ?int
{
return $this->id;
}
/**
* @return PermissionGroup
*/
public function getPermissionsGroup()
public function getPermissionsGroup(): ?PermissionsGroup
{
return $this->permissionsGroup;
}
/**
* @return ArrayCollection|Collection
* @return Collection<User::class>
*/
public function getUsers()
public function getUsers(): Collection
{
return $this->users;
}
/**
* @return \Chill\MainBundle\Entity\GroupCenter
*/
public function setCenter(Center $center)
public function setCenter(Center $center): self
{
$this->center = $center;
return $this;
}
/**
* @return \Chill\MainBundle\Entity\GroupCenter
*/
public function setPermissionsGroup(PermissionsGroup $permissionsGroup)
public function setPermissionsGroup(PermissionsGroup $permissionsGroup): self
{
$this->permissionsGroup = $permissionsGroup;

View File

@ -87,6 +87,7 @@ class ComposedRoleScopeType extends AbstractType
->add('scope', EntityType::class, [
'class' => Scope::class,
'choice_label' => static fn (Scope $scope) => $translatableStringHelper->localize($scope->getName()),
'placeholder' => 'Choose amongst scopes',
'required' => false,
'data' => null,
]);

View File

@ -60,13 +60,15 @@ class ScopePickerType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$items = array_filter(
$this->authorizationHelper->getReachableScopes(
$this->security->getUser(),
$options['role'] instanceof Role ? $options['role']->getRole() : $options['role'],
$options['center']
),
static fn (Scope $s) => $s->isActive()
$items = array_values(
array_filter(
$this->authorizationHelper->getReachableScopes(
$this->security->getUser(),
$options['role'] instanceof Role ? $options['role']->getRole() : $options['role'],
$options['center']
),
static fn (Scope $s) => $s->isActive()
)
);
if (0 === count($items)) {

View File

@ -241,7 +241,7 @@ class WorkflowStepType extends AbstractType
function ($step, ExecutionContextInterface $context, $payload) {
$form = $context->getObject();
foreach($form->get('future_dest_users')->getData() as $u) {
foreach ($form->get('future_dest_users')->getData() as $u) {
if (in_array($u, $form->get('future_cc_users')->getData(), true)) {
$context
->buildViolation('workflow.The user in cc cannot be a dest user in the same workflow step')

View File

@ -15,7 +15,10 @@ use Chill\MainBundle\Entity\User;
use Psr\Log\LoggerInterface;
use Swift_Mailer;
use Swift_Message;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;
@ -26,43 +29,34 @@ use function call_user_func;
* Classe d'aide pour l'envoi de notification.
*
* Héberge toutes les méthodes pour -écrire les URL en fonction de la langue de l'utilisateur.
*
* @deprecated use the MailerInterface
*/
class Mailer
{
/**
* @var Swift_Mailer
*/
protected $forcedMailer;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var Swift_Mailer
*/
protected $mailer;
private $logger;
/**
* @var array
*/
protected $routeParameters;
private $routeParameters;
/**
* @var RouterInterface
*/
protected $router;
private $router;
/**
* @var TranslatorInterface
*/
protected $translator;
private $translator;
/**
* @var \Twig\Environment
*/
protected $twig;
private EngineInterface $twig;
private MailerInterface $mailer;
/**
* Mailer constructor.
@ -70,11 +64,9 @@ class Mailer
* @param $routeParameters
*/
public function __construct(
MailerInterface $mailer,
LoggerInterface $logger,
Environment $twig,
Swift_Mailer $mailer,
// due to bug https://github.com/symfony/swiftmailer-bundle/issues/127
// \Swift_Transport $mailerTransporter,
EngineInterface $twig,
RouterInterface $router,
TranslatorInterface $translator,
$routeParameters
@ -82,7 +74,6 @@ class Mailer
$this->logger = $logger;
$this->twig = $twig;
$this->mailer = $mailer;
//$this->forcedMailer = new \Swift_Mailer($mailerTransporter);
$this->router = $router;
$this->translator = $translator;
$this->routeParameters = $routeParameters;
@ -115,20 +106,6 @@ class Mailer
return $content;
}
/**
* @param $force
*
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
*/
public function sendMessage(Swift_Message $message, $force)
{
if ($force) {
$this->forcedMailer->send($message);
} else {
$this->mailer->send($message);
}
}
/**
* Envoie une notification à un utilisateur.
*
@ -155,23 +132,25 @@ class Mailer
$subject[2] ?? null
);
$message = (new Swift_Message($subjectI18n))
->setFrom($fromEmail, $fromName)
->setTo($to);
$email = new Email();
$email->addTo($to)->subject($subjectI18n);
foreach ($bodies as $contentType => $content) {
$message->setBody($content, $contentType);
match ($contentType) {
'text/plain' => $email->text($content),
default => $email->text($content),
};
}
if (null !== $callback) {
call_user_func($callback, $message);
call_user_func($callback, $email);
}
$this->logger->info('[notification] Sending notification', [
'to' => $message->getTo(),
'subject' => $message->getSubject(),
'to' => $email->getTo(),
'subject' => $email->getSubject()
]);
$this->sendMessage($message, $force);
$this->mailer->send($email);
}
}

View File

@ -375,6 +375,12 @@ span.dt {
font-weight: bolder;
background-color: var(--bs-chill-light-gray);
}
/// help text
.help-text {
margin-top: 0.25rem;
font-size: 0.875em;
color: var(--bs-gray);
}
/*

View File

@ -1,3 +1,11 @@
/// mixin to set sticky area on bottom when scrolling
@mixin sticky-bottom {
position: sticky;
bottom: 0;
margin-top: 4em;
z-index: 1000;
}
ul.record_actions {
display: flex;
flex-direction: row;
@ -53,16 +61,30 @@ ul.record_actions {
}
}
}
.sticky-form {
background-color: $white;
padding-top: 1.25em;
margin: -1em;
box-shadow: 0 -20px 20px -20px rgba($chill-gray, .5);
@include sticky-bottom;
.sticky-form-buttons {
position: initial;
bottom: unset;
margin-top: unset;
z-index: unset;
}
}
.sticky-form-buttons {
margin-top: 4em;
background-color: $beige;
position: sticky;
bottom: 0.3em;
text-align: center;
display: flex;
padding: 0.8em 1.6em;
border-radius: 0;
z-index: 1000;
display: flex;
background-color: $beige;
text-align: center;
padding: 0.8em 1.6em;
border-radius: 0;
@include sticky-bottom;
bottom: 0.3em;
}
/// EXCEPTIONS

View File

@ -64,6 +64,7 @@ section.chill-entity {
margin: 0.7em 0;
p {
display: block;
margin-bottom: 0;
}
}
&.delimiter {

View File

@ -1,7 +1,7 @@
<template>
<span v-if="data.working_ref_status === 'to_review'" class="badge bg-danger address-details-button-warning">L'adresse de référence a été modifiée</span>
<a v-if="data.loading === false" @click.prevent="clickOrOpen" class="btn btn-sm btn-misc">
<span class="fa fa-map address-details-button"></span>
<a v-if="data.loading === false" @click.prevent="clickOrOpen" class="btn btn-misc address-details-button">
<span class="fa fa-map"></span> <!-- button -->
</a>
<span v-if="data.loading" class="fa fa-spin fa-spinner "></span>
<AddressModal :address="data.working_address" @update-address="onUpdateAddress" ref="address_modal"></AddressModal>

View File

@ -1,7 +1,7 @@
{% set formId = crudMainFormId|default('crud_main_form') %}
{% block crud_content_header %}
<h1>{{ ('crud.'~crud_name~'.title_edit')|trans }}</h1>
<h1 class="mb-5">{{ ('crud.'~crud_name~'.title_edit')|trans }}</h1>
{% endblock crud_content_header %}
{% block crud_content_form %}

View File

@ -14,7 +14,7 @@
{% endblock %}
{% else %}
{% block table_entities %}
<table class="table table-bordered border-dark">
<table class="table table-bordered border-dark align-middle">
<thead>
<tr>
{% block table_entities_thead_tr %}

View File

@ -10,6 +10,7 @@
* has_no_address bool
* multiline bool multiline display
* extended_infos bool add extra informations (step, floor, etc.) DEPRECATED
* details_button bool add an address details button
#}
@ -79,7 +80,12 @@
<i class="fa fa-fw fa-map-marker"></i>
{% endif %}
{{ _self.inline(address, options, streetLine, lines) }}
<span data-address-details="1" data-address-id="{{ address.id|escape('html_attr') }}" data-address-ref-status="{{ address.refStatus|escape('html_attr') }}"></span>
{% if options['details_button'] is defined and options['details_button'] == true %}
<span data-address-details="1"
data-address-id="{{ address.id|escape('html_attr') }}"
data-address-ref-status="{{ address.refStatus|escape('html_attr') }}">
</span>
{% endif %}
</span>
{%- endif -%}
@ -112,7 +118,12 @@
<i class="fa fa-fw fa-map-marker"></i>
{% endif %}
{{ _self.raw(lines) }}
<p><span data-address-details="1" data-address-id="{{ address.id|escape('html_attr') }}" data-address-ref-status="{{ address.refStatus|escape('html_attr') }}"></span></p>
{% if options['details_button'] is defined and options['details_button'] == true %}<p class="mt-3">
<span data-address-details="1"
data-address-id="{{ address.id|escape('html_attr') }}"
data-address-ref-status="{{ address.refStatus|escape('html_attr') }}">
</span></p>
{% endif %}
</div>
{% endif %}
{{ _self.validity(address, options) }}

View File

@ -1,8 +1,8 @@
<footer class="footer">
<p>
{{ '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>'|trans|raw }}
<br/>
<a name="bottom" class="btn text-white" href="https://{{ app.request.locale }}.wikibooks.org/wiki/Chill" target="_blank">
<br/>
<a name="bottom" class="btn text-white" href="https://gitea.champs-libres.be/Chill-project/manuals/releases" target="_blank">
{{ 'User manual'|trans }}
</a>
</p>

View File

@ -3,93 +3,130 @@
{% block title %}{{ 'PermissionsGroup "%name%" edit'|trans( { '%name%': entity.name } ) }}{% endblock %}
{% block admin_content -%}
<h1>{{ 'PermissionsGroup "%name%" edit'|trans( { '%name%': entity.name } ) }}</h1>
<div class="container-xxl">
<div class="row">
<h2>{{ 'Details'|trans }}</h2>
<h1 class="mb-4">{{ 'PermissionsGroup "%name%" edit'|trans( { '%name%': entity.name } ) }}</h1>
{{ form_start(edit_form) }}
{{ form_row(edit_form.name) }}
{% if edit_form.flags is defined %}
{{ form_row(edit_form.flags) }}
{% endif %}
{{ form_row(edit_form.submit, { 'attr': { 'class': 'btn btn-chill-green' } } ) }}
{{ form_end(edit_form) }}
<h2>{{ 'Details'|trans }}</h2>
<h2>{{ 'Grant those permissions'|trans }} :</h2>
{{ form_start(edit_form) }}
{{ form_row(edit_form.name) }}
{% if edit_form.flags is defined %}
{{ form_row(edit_form.flags) }}
{% endif %}
{{ form_row(edit_form.submit, { 'attr': { 'class': 'btn btn-save float-end' } } ) }}
{{ form_end(edit_form) }}
{%- if entity.getRoleScopes|length > 0 -%}
{% for title, role_scopes in role_scopes_sorted %}
<h2 class="mb-5">{{ 'Grant those permissions'|trans }} :</h2>
<h3>{{ title|default("Unclassified")|trans }}</h3>
{%- if entity.getRoleScopes|length > 0 -%}
{% for title, role_scopes in role_scopes_sorted %}
<table class="striped rounded">
<thead>
<h3>{{ title|default("Unclassified")|trans }}</h3>
<table class="table table-bordered border-dark align-middle mb-5">
<thead>
<tr>
<th>{{ 'Circle'|trans }}</th>
<th class="w-75">{{ 'Role'|trans }}</th>
<th>{{ 'Actions'|trans }}</th>
</tr>
</thead>
<tbody>
{% for role_scope in role_scopes %}
<tr>
<th>{{ 'Role'|trans }}</th>
<th>{{ 'Circle'|trans }}</th>
<th>{{ 'Actions'|trans }}</th>
</tr>
</thead>
<tbody>
{% for role_scope in role_scopes %}
<tr>
<td>
<span class="role_scope role">{{ role_scope.role|trans }}</span>
{% if expanded_roles[role_scope.role]|length > 1 %}
<br/>
<small>{{ 'Which implies'|trans }}&nbsp;: {% for role in expanded_roles[role_scope.role] %}{{ role|trans }}{% if not loop.last %}, {% endif %}{% endfor %}</small>
<td style="width: 7em">
{%- if role_scope.scope is not null -%}
<span class="role_scope scope">
{{ role_scope.scope.name|localize_translatable_string }}
</span>
{%- else -%}
<small><i>N/A</i></small>
{%- endif -%}
</td>
<td>
<span class="role_scope role">{{ role_scope.role|trans }}</span>
{% if expanded_roles[role_scope.role]|length > 1 %}
<div class="help-text">
<span style="text-decoration: underline dotted;">{{ 'Which implies'|trans }}&nbsp;:</span>
{% for role in expanded_roles[role_scope.role] %}
{% if role != role_scope.role %}
{{ role|trans }}
{% if not loop.last %}, {% endif %}
{% endif %}
{% endfor %}
</div>
{% endif %}
</td>
<td>
{%- if role_scope.scope is not null -%}
<span class="role_scope scope">
{{ role_scope.scope.name|localize_translatable_string }}
</span>
{%- else -%}
<em>N/A</em>
{%- endif -%}
</td>
<td>
{{ form_start(delete_role_scopes_form[role_scope.id]) }}
{{ form_widget(delete_role_scopes_form[role_scope.id].submit, { 'attr': { 'class': 'btn btn-chill-red' } } ) }}
{{ form_end(delete_role_scopes_form[role_scope.id]) }}
</td>
</tr>
</td>
<td style="width: 7em">
{{ form_start(delete_role_scopes_form[role_scope.id]) }}
{{ form_widget(delete_role_scopes_form[role_scope.id].submit, { 'attr': { 'class': 'btn btn-remove' } } ) }}
{{ form_end(delete_role_scopes_form[role_scope.id]) }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}
{% endfor %}
</tbody>
</table>
{% endfor %}
{%- else -%}
<p>{{ 'This group does not provide any permission'|trans }}</p>
{%- endif -%}
{%- else -%}
<p>{{ 'This group does not provide any permission'|trans }}</p>
{%- endif -%}
</div>
<div class="row sticky-form">
<div class="mt-5">
<h2>{{ 'Grant new permissions'|trans }}</h2>
{{ form_start(add_role_scopes_form) }}
{{ form_errors(add_role_scopes_form) }}
{{ form_row(add_role_scopes_form.composed_role_scope.role) }}
{{ form_row(add_role_scopes_form.composed_role_scope.scope) }}
<div class="input-group">
{{ form_widget(add_role_scopes_form.composed_role_scope.role, { 'attr': { 'class': 'w-50' }}) }}
{{ form_widget(add_role_scopes_form.composed_role_scope.scope) }}
</div>
<div id="role_scope_legend" class="help-text mb-3">{{ 'Help to pick role and scope'|trans }}</div>
<ul class="record_actions sticky-form-buttons">
<li>
{{ form_row(add_role_scopes_form.submit, { 'attr' : { 'class': 'btn btn-create' } } ) }}
</li>
<li>
<a href="{{ path('admin_permissionsgroup_show', { 'id': entity.id }) }}" class="btn btn-cancel">{{ 'Cancel'|trans }}</a>
</li>
<li>
<li class="cancel">
<a href="{{ path('admin_permissionsgroup') }}" class="btn btn-cancel">
{{ 'Back to the list'|trans }}
</a>
<a href="{{ path('admin_permissionsgroup_show', { 'id': entity.id }) }}" class="btn btn-misc">{{ 'Cancel'|trans }}</a>
</li>
<li>
{{ form_widget(add_role_scopes_form.submit, { 'attr' : { 'class': 'btn btn-create' } } ) }}
</li>
</ul>
{{ form_end(add_role_scopes_form) }}
</div>
</div>
{% endblock %}
{% block js %}
<script>
// An event listener give contextual legend when choosing an option.
const select = document.getElementById('form_composed_role_scope_role');
const legend = document.getElementById('role_scope_legend');
select.addEventListener('change', function() {
const option = this.options[this.selectedIndex];
const hasScope = option.getAttribute('data-has-scope');
legend.style.display = 'block';
if (hasScope === '0') {
legend.innerText = '{{ 'The role does not need scope'|trans }}';
} else if (hasScope === '1') {
legend.innerText = '{{ 'The role need scope'|trans }}';
} else {
legend.innerText = '{{ 'Help to pick role and scope'|trans }}';
}
});
</script>
{% endblock %}

View File

@ -5,7 +5,7 @@
{% block admin_content -%}
<h1>{{ 'Permissions group list'|trans }}</h1>
<table class="records_list">
<table class="table table-bordered border-dark align-middle">
<thead>
<tr>
<th>{{ 'Name'|trans }}</th>

View File

@ -10,8 +10,6 @@
{% if form.flags is defined %}
{{ form_row(form.flags) }}
{% endif %}
{{ form_row(form.submit, { 'attr': { 'class': 'btn btn-chill-green' } } ) }}
{{ form_end(form) }}
<ul class="record_actions sticky-form-buttons">
<li class='cancel'>
@ -19,5 +17,10 @@
{{ 'Back to the list'|trans }}
</a>
</li>
<li>
{{ form_widget(form.submit, { 'attr': { 'class': 'btn btn-create' } } ) }}
</li>
</ul>
{{ form_end(form) }}
{% endblock %}

View File

@ -3,44 +3,52 @@
{% block title %}{{ 'Permission group "%name%"'|trans({ '%name%': entity.name }) }}{% endblock %}
{% block admin_content -%}
<h1>{{ 'Permission group "%name%"'|trans({ '%name%': entity.name }) }}</h1>
<h1 class="mb-4">{{ 'Permission group "%name%"'|trans({ '%name%': entity.name }) }}</h1>
<table class="record_properties">
<table class="table table-bordered border-dark align-middle">
<tbody>
<tr>
<th>{{ 'Name'|trans }}</th>
<td>{{ entity.name }}</td>
<td class="w-25">{{ entity.name }}</td>
</tr>
</tbody>
</table>
{% if role_scopes_sorted|length > 0 %}
<h2>{{ 'Grant those permissions'|trans }}&nbsp;:</h2>
<h2 class="mb-5">{{ 'Grant those permissions'|trans }}&nbsp;:</h2>
{% for title, role_scopes in role_scopes_sorted %}
<h3>{{ title|default('Unclassified')|trans }}</h3>
<table class="striped rounded">
<table class="table table-bordered border-dark align-middle mb-5">
<thead>
<tr>
<th class="w-25">{{ 'Circle'|trans }}</th>
<th>{{ 'Role'|trans }}</th>
<th>{{ 'Circle'|trans }}</th>
</tr>
</thead>
<tbody>
{% for role_scope in role_scopes %}
<tr>
<td>
{{ role_scope.role|trans }}
{% if expanded_roles[role_scope.role]|length > 1 %}
<br/>
<small>{{ 'Which implies'|trans }}&nbsp;: {% for role in expanded_roles[role_scope.role] %}{{ role|trans }}{% if not loop.last %}, {% endif %}{% endfor %}</small>
{% endif %}
</td>
<td>{%- if role_scope.scope is not null -%}
{{ role_scope.scope.name|localize_translatable_string }}
<td style="width: 7em">{%- if role_scope.scope is not null -%}
{{ role_scope.scope.name|localize_translatable_string }}
{%- else -%}
<em>N/A</em>
{%- endif -%}
<small><i>N/A</i></small>
{%- endif -%}
</td>
<td>
<span class="role_scope role">{{ role_scope.role|trans }}</span>
{% if expanded_roles[role_scope.role]|length > 1 %}
<div class="help-text">
<span style="text-decoration: underline dotted;">{{ 'Which implies'|trans }}&nbsp;:</span>
{% for role in expanded_roles[role_scope.role] %}
{% if role != role_scope.role %}
{{ role|trans }}
{% if not loop.last %}, {% endif %}
{% endif %}
{% endfor %}
</div>
{% endif %}
</td>
</tr>
{% endfor %}
@ -57,17 +65,15 @@
{% endif %}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a href="{{ path('admin_permissionsgroup') }}" class="btn btn-cancel">
{{ 'Back to the list'|trans }}
</a>
</li>
<li>
<a href="{{ path('admin_permissionsgroup_edit', { 'id': entity.id }) }}" class="btn btn-edit">
{{ 'Edit'|trans }}
</a>
</li>
<li>
<a href="{{ path('admin_permissionsgroup') }}" class="btn btn-cancel">
{{ 'Back to the list'|trans }}
</a>
</li>
</ul>
{% endblock %}

View File

@ -4,10 +4,10 @@
{% embed '@ChillMain/CRUD/_edit_content.html.twig' %}
{% block crud_content_after_form %}
{% if access_permissions_group_list %}
<h2>{{ 'Permissions granted'|trans }}</h2>
<h2 class="mt-5">{{ 'Permissions granted'|trans }}</h2>
{% if entity.groupcenters|length > 0 %}
<table>
<table class="table table-bordered border-dark align-middle mb-5">
<thead>
<tr>
<th>{{ 'Permission group'|trans }}</th>
@ -19,18 +19,18 @@
{% for groupcenter in entity.groupcenters %}
<tr>
<td>
<span class="user_group permissionsgroup">
{{ groupcenter.permissionsgroup.name }}
</span>
<span class="user_group permissionsgroup">
{{ groupcenter.permissionsgroup.name }}
</span>
</td>
<td>
<span class="user_group center">
{{ groupcenter.center.name }}
</span>
<span class="user_group center">
{{ groupcenter.center.name }}
</span>
</td>
<td>
<td class="w-25 text-end">
{{ form_start(delete_groupcenter_form[groupcenter.id]) }}
{{ form_row(delete_groupcenter_form[groupcenter.id].submit, { 'attr': { 'class': 'btn btn-chill-red' } } ) }}
{{ form_widget(delete_groupcenter_form[groupcenter.id].submit, { 'attr': { 'class': 'btn btn-remove' } } ) }}
{{ form_rest(delete_groupcenter_form[groupcenter.id]) }}
{{ form_end(delete_groupcenter_form[groupcenter.id]) }}
</td>
@ -47,7 +47,7 @@
{{ form_start(add_groupcenter_form) }}
{{ form_row(add_groupcenter_form.composed_groupcenter.center) }}
{{ form_row(add_groupcenter_form.composed_groupcenter.permissionsgroup) }}
{{ form_row(add_groupcenter_form.submit, { 'attr' : { 'class': 'btn btn-chill-green' } } ) }}
{{ form_row(add_groupcenter_form.submit, { 'attr' : { 'class': 'btn btn-create' } } ) }}
{{ form_end(add_groupcenter_form) }}
{% endif %}

View File

@ -69,19 +69,20 @@
</td>
<td>
<ul class="record_actions">
{% if is_granted('ROLE_ALLOWED_TO_SWITCH') %}
<li>
<a class="btn btn-chill-blue" href="{{ path('chill_main_homepage', {'_switch_user': entity.username }) }}" title="{{ "Impersonate"|trans|e('html_attr') }}"><i class="fa fa-user-secret"></i></a>
</li>
{% endif %}
<li>
<a class="btn btn-edit" title="{{ 'Edit'|trans }}" href="{{ path('chill_crud_admin_user_edit', { 'id': entity.id }) }}"></a>
</li>
{% if allow_change_password is same as(true) %}
<li>
<a class="btn btn-chill-red" href="{{ path('admin_user_edit_password', { 'id' : entity.id }) }}" title="{{ 'Edit password'|trans|e('html_attr') }}"><i class="fa fa-ellipsis-h"></i></a>
</li>
{% endif %}
{% if is_granted('ROLE_ALLOWED_TO_SWITCH') %}
<li>
<a class="btn btn-chill-blue" href="{{ path('chill_main_homepage', {'_switch_user': entity.username }) }}" title="{{ "Impersonate"|trans|e('html_attr') }}"><i class="fa fa-user-secret"></i></a>
<a class="btn btn-reset" href="{{ path('admin_user_edit_password', { 'id' : entity.id }) }}" title="{{ 'Edit password'|trans|e('html_attr') }}"><i class="fa fa-key-modern"></i></a>
</li>
{% endif %}
</ul>

View File

@ -2,14 +2,14 @@
{% block admin_content %}
{% embed '@ChillMain/CRUD/_index.html.twig' %}
{% block table_entities_thead_tr %}
<th>id</th>
<th>{{ 'Label'|trans }}</th>
<th>{{ 'Active'|trans }}</th>
<th>{{ 'Actions'|trans }}</th>
{% endblock %}
{% block table_entities_tbody %}
{% for entity in entities %}
<tr>
@ -25,19 +25,19 @@
<td>
<ul class="record_actions">
<li>
<a href="{{ chill_path_add_return_path('chill_crud_admin_user_job_edit', { 'id': entity.id}) }}" class="btn btn-sm btn-edit btn-mini"></a>
<a href="{{ chill_path_add_return_path('chill_crud_admin_user_job_edit', { 'id': entity.id}) }}" class="btn btn-edit"></a>
</li>
</ul>
</td>
</tr>
{% endfor %}
{% endblock %}
{% block actions_before %}
<li class='cancel'>
<a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans}}</a>
</li>
{% endblock %}
{% endembed %}
{% endblock %}

View File

@ -120,6 +120,11 @@ class AuthorizationHelper implements AuthorizationHelperInterface
if ($role instanceof Role) {
$role = $role->getRole();
}
if (!$user instanceof User) {
return [];
}
/** @var array<string, Center> $centers */
$centers = [];

View File

@ -14,6 +14,8 @@ namespace Chill\MainBundle\Security\PasswordRecover;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Notification\Mailer;
use DateTimeInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use function array_merge;
@ -22,33 +24,26 @@ class RecoverPasswordHelper
{
public const RECOVER_PASSWORD_ROUTE = 'password_recover';
/**
* @var Mailer
*/
protected $mailer;
protected $routeParameters;
private MailerInterface $mailer;
/**
* @var TokenManager
*/
protected $tokenManager;
private $tokenManager;
/**
* @var UrlGeneratorInterface
*/
protected $urlGenerator;
private $urlGenerator;
public function __construct(
TokenManager $tokenManager,
UrlGeneratorInterface $urlGenerator,
Mailer $mailer,
array $routeParameters
MailerInterface $mailer,
) {
$this->tokenManager = $tokenManager;
$this->urlGenerator = $urlGenerator;
$this->mailer = $mailer;
$this->routeParameters = $routeParameters;
}
/**
@ -59,27 +54,14 @@ class RecoverPasswordHelper
*/
public function generateUrl(User $user, DateTimeInterface $expiration, $absolute = true, array $parameters = [])
{
$context = $this->urlGenerator->getContext();
$previousHost = $context->getHost();
$previousScheme = $context->getScheme();
$context->setHost($this->routeParameters['host']);
$context->setScheme($this->routeParameters['scheme']);
$url = $this->urlGenerator->generate(
return $this->urlGenerator->generate(
self::RECOVER_PASSWORD_ROUTE,
array_merge(
$this->tokenManager->generate($user, $expiration),
$parameters
),
$absolute ? UrlGeneratorInterface::ABSOLUTE_URL : UrlGeneratorInterface::ABSOLUTE_PATH
UrlGeneratorInterface::ABSOLUTE_URL
);
// reset the host
$context->setHost($previousHost);
$context->setScheme($previousScheme);
return $url;
}
public function sendRecoverEmail(
@ -91,26 +73,20 @@ class RecoverPasswordHelper
array $additionalUrlParameters = [],
$emailSubject = 'Recover your password'
) {
$content = $this->mailer->renderContentToUser(
$user,
$template,
array_merge(
[
'user' => $user,
'url' => $this->generateUrl($user, $expiration, true, $additionalUrlParameters),
],
$templateParameters
)
);
if (null === $user->getEmail() || '' === trim($user->getEmail())) {
throw new \UnexpectedValueException("No emaail associated to the user");
}
$this->mailer->sendNotification(
$user,
[$emailSubject],
[
'text/plain' => $content,
],
null,
$force
);
$email = (new TemplatedEmail())
->subject($emailSubject)
->to($user->getEmail())
->textTemplate($template)
->context([
'user' => $user,
'url' => $this->generateUrl($user, $expiration, true, $additionalUrlParameters),
...$templateParameters
]);
$this->mailer->send($email);
}
}

View File

@ -207,7 +207,7 @@ final class GeographicalUnitBaseImporter
(id, geom, unitname, layer_id, unitrefid)
SELECT
nextval('chill_main_geographical_unit_id_seq'),
geom,
st_makevalid(geom),
unitName,
layer.id,
unitKey

View File

@ -78,8 +78,8 @@ class PostalCodeFRFromOpenData
private function handleRecord(array $record): void
{
if ('' !== trim($record['coordonnees_gps'])) {
[$lat, $lon] = array_map(static fn ($el) => (float) trim($el), explode(',', $record['coordonnees_gps']));
if ('' !== trim($record['coordonnees_geographiques'] ?? $record['coordonnees_gps'])) {
[$lat, $lon] = array_map(static fn ($el) => (float) trim($el), explode(',', $record['coordonnees_geographiques'] ?? $record['coordonnees_gps']));
} else {
$lat = $lon = 0.0;
}

View File

@ -10,12 +10,6 @@ services:
Chill\MainBundle\Notification\Mailer:
arguments:
$logger: '@Psr\Log\LoggerInterface'
$twig: '@Twig\Environment'
$mailer: '@swiftmailer.mailer.default'
# $mailerTransporter: '@swiftmailer.transport'
$router: '@Symfony\Component\Routing\RouterInterface'
$translator: '@Symfony\Contracts\Translation\TranslatorInterface'
$routeParameters: '%chill_main.notifications%'
Chill\MainBundle\Notification\NotificationHandlerManager:

View File

@ -61,11 +61,7 @@ services:
arguments:
$secret: '%kernel.secret%'
Chill\MainBundle\Security\PasswordRecover\RecoverPasswordHelper:
arguments:
$tokenManager: '@Chill\MainBundle\Security\PasswordRecover\TokenManager'
$mailer: '@Chill\MainBundle\Notification\Mailer'
$routeParameters: "%chill_main.notifications%"
Chill\MainBundle\Security\PasswordRecover\RecoverPasswordHelper: ~
Chill\MainBundle\Security\PasswordRecover\PasswordRecoverEventSubscriber:
arguments:

View File

@ -69,7 +69,6 @@ final class Version20230306145728 extends AbstractMigration
$this->addSql('CREATE INDEX IDX_165051F63174800F ON chill_main_address (createdBy_id)');
$this->addSql('CREATE INDEX IDX_165051F665FF1AEC ON chill_main_address (updatedBy_id)');
$this->addSql('COMMENT ON COLUMN chill_main_address_reference.point IS \'(DC2Type:point)\'');
}
public function down(Schema $schema): void

View File

@ -167,12 +167,16 @@ Permissionsgroup: Groupe de permissions
New permission group: Nouveau groupe de permissions
PermissionsGroup "%name%" edit: Modification du groupe de permission '%name%'
Role: Rôle
Choose amongst roles: Choisir parmi les rôles
Choose amongst roles: Choisir un rôle
Choose amongst scopes: Choisir un cercle
Add permission: Ajouter les permissions
This group does not provide any permission: Ce groupe n'attribue aucune permission
The role '%role%' has been removed: Le rôle "%role%" a été enlevé de ce groupe de permission
The role '%role%' on circle '%scope%' has been removed: Le rôle "%role%" sur le cercle "%scope%" a été enlevé de ce groupe de permission
Unclassified: Non classifié
Help to pick role and scope: Certains rôles ne nécessitent pas de cercle.
The role need scope: Ce rôle nécessite un cercle.
The role does not need scope: Ce rôle ne nécessite pas de cercle !
#admin section for users
User configuration: Gestion des utilisateurs
@ -423,6 +427,7 @@ No entities: Aucun élément
CHILL_FOO_SEE: Voir un élément
CHILL_FOO_EDIT: Modifier un élément
chill_export: Exports (statistiques)
#Show templates
Date: Date

View File

@ -64,7 +64,7 @@ class ReferrerScopeAggregator implements AggregatorInterface
$qb->expr()->lte($userHistory . '.startDate', ':' . $dateCalc),
$qb->expr()->orX(
$qb->expr()->isNull($userHistory . '.endDate'),
$qb->expr()->lt($userHistory . '.endDate', ':' . $dateCalc)
$qb->expr()->gt($userHistory . '.endDate', ':' . $dateCalc)
)
)
)

View File

@ -93,7 +93,7 @@ class ReferrerFilter implements FilterInterface
return [
'Filtered by referrer: only %referrers%', [
'%referrers' => implode(', ', $users),
'%referrers%' => implode(', ', $users),
], ];
}

View File

@ -174,7 +174,10 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
->andWhere(
$qb->expr()->orX(
$qb->expr()->neq('ap.step', ':draft'),
$qb->expr()->eq('ap.createdBy', ':creator')
$qb->expr()->orX(
$qb->expr()->eq('ap.createdBy', ':creator'),
$qb->expr()->isNull('ap.createdBy')
)
)
)
->setParameter('draft', AccompanyingPeriod::STEP_DRAFT)

View File

@ -14,6 +14,31 @@
@import './scss/person_by_phonenumber.scss';
@import './scss/address_history.scss';
/*
* Mixins
*/
@mixin context_buttons ($context) {
.chill-entity.entity-address .address {
padding-right: 1em;
}
.address-details-button,
.household-link {
border: 1px solid white;
background-color: transparent;
width: 1.85rem;
height: 1.85rem;
font-size: 75%;
padding: .38rem;
border-radius: 5px;
color: white;
cursor: pointer;
&:hover {
background-color: white;
color: $context
}
}
}
/*
* PERSON CONTEXT
@ -39,17 +64,7 @@ div.banner {
margin-right: 1em;
}
}
.household-link {
border: 1px solid white;
padding: .05rem .3rem;
border-radius: 5px;
color: white;
cursor: pointer;
&:hover {
background-color: white;
color: $chill-person-context
}
}
@include context_buttons($chill-person-context);
}
}
@ -219,6 +234,8 @@ div.banner {
font-weight: 700;
font-weight: bold;
}
@include context_buttons($chill-household-context);
}
}

View File

@ -22,6 +22,7 @@
<div class="col-md-10">
<h1>{{ 'My accompanying periods in draft'|trans }}</h1>
<p class="help-text">{{ 'Display draft periods created by me'|trans }}</p>
<div class="flex-table accompanyingcourse-list">
{% for period in accompanyingPeriods %}
@ -31,7 +32,9 @@
{% endfor %}
</div>
{{ chill_pagination(pagination) }}
{% if accompanyingPeriods|length > 0 %}
{{ chill_pagination(pagination) }}
{% endif %}
</div>

View File

@ -58,7 +58,8 @@
{% else %}
<span class=" d-block d-sm-inline-block">
{{ address|chill_entity_render_box({
'render': 'inline', 'multiline': false, 'with_picto': true, 'with_delimiter': true
'render': 'inline', 'multiline': false, 'with_picto': true, 'with_delimiter': true,
'details_button': false
}) }}
</span>
{% endif %}

View File

@ -23,7 +23,11 @@
{% if address is empty %}
<p class="chill-no-data-statement">{{ 'household.Household does not have any address currently'|trans }}</p>
{% else %}
{{ address|chill_entity_render_box({'multiline': true, 'extended_infos': true }) }}
{{ address|chill_entity_render_box({
'multiline': true,
'extended_infos': true,
'details_button': true
}) }}
{% endif %}
<ul class="list-inline text-right mt-2">

View File

@ -19,25 +19,25 @@
<div id="header-person-details" class="header-details">
<div class="container-xxl">
<div class="row justify-content-between">
<div class="col-md-12 ps-md-5 ps-xxl-0 container">
<div class="row contact">
<div class="col-md-12 ps-md-5 ps-xxl-0">
<div class="contact">
{% if person.phonenumber %}
<span class="phonenumber d-block d-sm-inline-block">
<span class="col-auto phonenumber">
<i class="fa fa-fw fa-phone"></i>
<a href="{{ 'tel:' ~ person.phonenumber|phone_number_format('E164') }}" class="phone mr-3" title="{{ 'Phonenumber'|trans }}">
{{ person.phonenumber|chill_format_phonenumber }}</a>
</span>
{% endif %}
{% if person.mobilenumber %}
<span class="mobilenumber d-block d-sm-inline-block">
<span class="col-auto mobilenumber">
<i class="fa fa-fw fa-mobile"></i>
<a href="{{ 'tel:' ~ person.mobilenumber|phone_number_format('E164') }}" class="phone mr-3" title="{{ 'Mobilenumber'|trans }}">
{{ person.mobilenumber|chill_format_phonenumber }}</a>
</span>
{% endif %}
{% if person.email %}
<span class="email d-block d-sm-inline-block">
<span class="col-auto email">
<i class="fa fa-fw fa-envelope-o"></i>
<a href="{{ 'mailto:' ~ person.email }}" class="email" title="{{ 'Email'|trans }}">
{{ person.email }}
@ -51,23 +51,25 @@
{%- elseif person.lastAddress is not empty -%}
{% set address = person.lastAddress %}
{%- endif -%}
{%- if address is not null -%}
<span class=" d-block d-sm-inline-block">
<span class="col-md-auto address">
{%- if address is not null -%}
{{ address|chill_entity_render_box({
'render': 'inline', 'multiline': false, 'with_picto': true, 'with_delimiter': true
'render': 'inline', 'multiline': false, 'with_picto': true, 'with_delimiter': true,
'details_button': false
}) }}
</span>
{%- endif -%}
{% if person.getCurrentHousehold is not null %}
<span>
<a class="household-link" href="{{ chill_path_add_return_path('chill_person_household_summary', { 'household_id' : person.getCurrentHousehold.id } ) }}">
<i class="fa fa-home"></i>
{%- endif -%}
{% if person.getCurrentHousehold is not null %}
<a class="btn household-link text-end"
href="{{ chill_path_add_return_path('chill_person_household_summary', { 'household_id' : person.getCurrentHousehold.id } ) }}"
title="{{ 'Show household'|trans }}">
<i class="fa fa-lg fa-home"></i>
</a>
</span>
{% endif %}
{% endif %}
</span>
</div>
</div>
</div>
</div>
</div>

View File

@ -287,7 +287,7 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface
$return['socialActionChild'] = $previousSocialActionChild;
} else {
$return['socialActionChild'] = $child = (new SocialAction())->setTitle(['fr' => $socialActionChildTitle]);
$parent->addChild($child);
$child->setParent($parent);
$child->setIssue($socialIssue)->setOrdering($orderingChild);
$this->entityManager->persist($child);
}
@ -332,7 +332,7 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface
} elseif (null !== $socialIssueChildTitle) {
$return['socialIssueChild'] = $child = (new SocialIssue())->setTitle(['fr' => $socialIssueChildTitle])
->setOrdering($orderingChild);
$parent->addChild($child);
$child->setParent($parent);
$this->entityManager->persist($child);
} else {
$return['socialIssueChild'] = null;

View File

@ -966,6 +966,7 @@ Linked evaluations: Évaluations associées
# Accompanying period per user
My accompanying periods: Mes parcours
My accompanying periods in draft: Mes parcours brouillons
Display draft periods created by me: Affiche les parcours en attente de confirmation. Ils ne sont visibles que par moi-même. Ces parcours restent disponibles 15 jours après leur création, avant d'être supprimés automatiquement.
Number of periods: Nombre de parcours
workflow:

View File

@ -1,4 +1,4 @@
'Report edit': "Edition d'un rapport"
'Report edit': "Édition d'un rapport"
'Save report': "Enregistrer le rapport"
'Reset report': "Remise à zéro"
'Add a report': "Ajout d'un rapport"

View File

@ -33,6 +33,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@ -205,7 +206,7 @@ final class SingleTaskController extends AbstractController
. 'allowed to edit this task');
$event = (new UIEvent('single-task', $task))
->setForm($this->setCreateForm($task, new Role(TaskVoter::UPDATE)));
->setForm($this->setCreateForm($task, TaskVoter::UPDATE));
$this->eventDispatcher->dispatch(UIEvent::EDIT_FORM, $event);
$form = $event->getForm();
@ -557,7 +558,7 @@ final class SingleTaskController extends AbstractController
$this->denyAccessUnlessGranted($role, $task, 'You are not '
. 'allowed to create this task');
$form = $this->setCreateForm($task, new Role($role));
$form = $this->setCreateForm($task, $role);
$form->handleRequest($request);
@ -650,7 +651,7 @@ final class SingleTaskController extends AbstractController
/**
* @return \Symfony\Component\Form\FormInterface
*/
protected function setCreateForm(SingleTask $task, Role $role)
protected function setCreateForm(SingleTask $task, string $role)
{
$form = $this->createForm(SingleTaskType::class, $task, [
'role' => $role,
@ -684,12 +685,9 @@ final class SingleTaskController extends AbstractController
/**
* Creates a form to delete a Task entity by id.
*
* @param mixed $id The entity id
*
* @return \Symfony\Component\Form\Form The form
* @param mixed $id
*/
private function createDeleteForm($id)
private function createDeleteForm($id): FormInterface
{
return $this->createFormBuilder()
->setAction($this->generateUrl(

View File

@ -81,7 +81,7 @@ class SingleTaskType extends AbstractType
->add('circle', ScopePickerType::class, [
'center' => $center,
'role' => $options['role'],
'required' => false,
'required' => true,
]);
}
}