Merge branch 'features/thirdparty-improve-acl-collection'

This commit is contained in:
2021-10-11 14:31:47 +02:00
143 changed files with 4212 additions and 1864 deletions

View File

@@ -268,7 +268,7 @@ final class PersonController extends AbstractController
) {
$this->em->persist($person);
$this->em->flush();
// $this->em->flush();
$this->lastPostDataReset();
if ($form->get('createPeriod')->isClicked()) {

View File

@@ -84,6 +84,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/repository.yaml');
$loader->load('services/serializer.yaml');
$loader->load('services/security.yaml');
$loader->load('services/doctrineEventListener.yaml');
// load service advanced search only if configure
if ($config['search']['search_by_phone'] != 'never') {

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Chill\PersonBundle\EventListener;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonAltName;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use LogicException;
use Symfony\Component\Validator\Exception\LogicException as ExceptionLogicException;
class PersonEventListener
{
public function onPrePersist(LifecycleEventArgs $event): void
{
if($event->getObject() instanceof Person){
$person = $event->getObject();
$firstnameCaps = mb_convert_case(mb_strtolower($person->getFirstName()), MB_CASE_TITLE, 'UTF-8');
$firstnameCaps = ucwords(strtolower($firstnameCaps), " \t\r\n\f\v'-");
$person->setFirstName($firstnameCaps);
$lastnameCaps = mb_strtoupper($person->getLastName(), 'UTF-8');
$person->setLastName($lastnameCaps);
} elseif ($event->getObject() instanceof PersonAltName){
$altname = $event->getObject();
$altnameCaps = mb_strtoupper($altname->getLabel(), 'UTF-8');
$altname->setLabel($altnameCaps);
} else {
throw new LogicException('Entity must be a person or an altname');
}
}
}

View File

@@ -22,7 +22,9 @@
namespace Chill\PersonBundle\Form;
use Chill\MainBundle\Form\Event\CustomizeFormEvent;
use Chill\MainBundle\Repository\CenterRepository;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -30,12 +32,11 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\CenterType;
use Chill\MainBundle\Form\Type\PickCenterType;
use Chill\PersonBundle\Form\Type\GenderType;
use Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Form\Type\PersonAltNameType;
use Chill\MainBundle\Form\Type\Export\PickCenterType;
final class CreationPersonType extends AbstractType
{
@@ -43,11 +44,7 @@ final class CreationPersonType extends AbstractType
// TODO: See if this is still valid and update accordingly.
const NAME = 'chill_personbundle_person_creation';
/**
*
* @var CenterTransformer
*/
private $centerTransformer;
private CenterRepository $centerRepository;
/**
*
@@ -58,11 +55,11 @@ final class CreationPersonType extends AbstractType
private EventDispatcherInterface $dispatcher;
public function __construct(
CenterTransformer $centerTransformer,
CenterRepository $centerRepository,
ConfigPersonAltNamesHelper $configPersonAltNamesHelper,
EventDispatcherInterface $dispatcher
) {
$this->centerTransformer = $centerTransformer;
$this->centerTransformer = $centerRepository;
$this->configPersonAltNamesHelper = $configPersonAltNamesHelper;
$this->dispatcher = $dispatcher;
}
@@ -82,8 +79,9 @@ final class CreationPersonType extends AbstractType
->add('gender', GenderType::class, array(
'required' => true, 'placeholder' => null
))
->add('center', CenterType::class, [
'required' => false
->add('center', PickCenterType::class, [
'required' => false,
'role' => PersonVoter::CREATE,
])
;

View File

@@ -1,10 +1,13 @@
// Access to Bootstrap variables and mixins
@import '~ChillMainAssets/module/bootstrap/shared';
@import 'ChillMainAssets/chill/scss/chill_variables';
// Complete/override Main generic assets
@import './scss/mixins';
@import './scss/render_box.scss';
@import './scss/flex_table.scss';
@import './scss/badge.scss';
// Specific templates styles
@import './scss/accompanying_period_work.scss';
@@ -19,13 +22,13 @@
div.banner {
div#header-person-name {
background: none repeat scroll 0 0 $chill-green-dark;
background: none repeat scroll 0 0 shade-color($chill-person-context, 20%);
color: $white;
padding-top: 1em;
padding-bottom: 1em;
}
div#header-person-details {
background: none repeat scroll 0 0 $chill-green;
background: none repeat scroll 0 0 $chill-person-context;
color: $white;
padding-top: 1em;
padding-bottom: 1em;
@@ -92,7 +95,6 @@ div.person-view {
* Header custom for Accompanying Course
*/
$chill-accourse-context: #718596;
div.banner {
div#header-accompanying_course-name {
@@ -104,9 +106,9 @@ div.banner {
span {
a {
color: $white;
}
a:hover {
text-decoration: underline;
&:hover {
text-decoration: underline;
}
}
}
}
@@ -118,19 +120,11 @@ div.banner {
}
}
abbr.referrer {
font-size: 70%;
padding-right: 0.4em;
align-self: center; // in flex context
}
/*
* HOUSEHOLD CONTEXT
* Header custom for Household
*/
$chill-household-context: #929d69;
div.banner {
div#header-household-name {
background: none repeat scroll 0 0 $chill-household-context;
@@ -142,9 +136,9 @@ div.banner {
span {
a {
color: $white;
}
a:hover {
text-decoration: underline;
&:hover {
text-decoration: underline;
}
}
}
div.household-members {
@@ -185,6 +179,7 @@ div.banner {
}
}
///
div.household-resume {
display: flex;
flex-direction: row;
@@ -208,48 +203,10 @@ div.household-resume {
}
}
/*
* BADGES, MARKS, PINS
* for chill person theme
*/
// chill person badges
span.badge-person,
span.badge-thirdparty {
display: inline-block;
padding: 0 0.5em !important;
background-color: $white;
color: $dark;
border: 1px solid $chill-ll-gray;
border-bottom-width: 2px;
border-bottom-style: solid;
border-radius: 6px;
a {
text-decoration: none;
}
}
span.badge-person {
border-bottom-color: $chill-green;
}
// todo: move in thirdparty
span.badge-thirdparty {
border-bottom-color: shade-color($chill-pink, 10%);
}
// household holder mark
span.fa-holder {
width: 1em;
margin: -10px 0.3em -8px 0;
i:last-child {
font-weight: 900;
color: white;
font-size: 70%;
font-family: "Open Sans Extrabold";
}
}
/// Horizontal list of persons (Accourse resume page)
div.accompanyingcourse-resume {
div.associated-persons {
font-size: 110%;
span.household {
display: inline-block;
border-radius: 8px;
@@ -272,3 +229,16 @@ div.accompanyingcourse-resume {
}
}
}
///
abbr.referrer { // still used ?
font-size: 70%;
padding-right: 0.4em;
align-self: center; // in flex context
}
.updatedBy {
margin-top: 0.3rem;
font-size: 0.9rem;
font-style: italic;
}

View File

@@ -1,31 +1,6 @@
/// AccompanyingCourse Work list Page
div.accompanying_course_work-list {
h2.title {
display: flex;
flex-direction: row;
width: 100%;
color: $dark;
//height: 40px;
span.title_label {
border-radius: 0.35rem 0 0 0.35rem;
background-color: $social-action-label-color;
color: $white;
font-size: 80%;
padding: 0.5em;
padding-right: 0;
}
span.title_action {
flex-grow: 1;
margin: 0 0 0 auto;
border-radius: 0 0.35rem 0.35rem 0;
background-color: $light;
padding: 0.2em 0.7em;
@include badge_social_action;
}
}
div.timeline {
width: 100%;
ul {
@@ -125,7 +100,7 @@ div.accompanying_course_work-list {
}
}
&.goal_title li::marker {
color: $sky-blue;
color: $social-issue-color;
}
&.result_list li::marker {
color: $pink;
@@ -133,11 +108,4 @@ div.accompanying_course_work-list {
}
}
.updatedBy {
margin-top: 0.3rem;
font-size: 0.9rem;
font-style: italic;
}
}

View File

@@ -0,0 +1,119 @@
/*
* BADGES PERSON AND THIRDPARTY
*/
span.badge-person,
span.badge-thirdparty {
display: inline-block;
padding: 0 0.5em !important;
background-color: $white;
color: $dark;
border: 1px solid $chill-ll-gray;
border-bottom-width: 2px;
border-bottom-style: solid;
border-radius: 6px;
a {
text-decoration: none;
}
}
span.badge-person {
border-bottom-color: $chill-green;
}
span.badge-thirdparty {
border-bottom-color: shade-color($chill-pink, 10%);
}
/*
* HOUSEHOLD HOLDER MARK
*/
span.fa-holder {
width: 1em;
margin: -10px 0.3em -8px 0;
i:last-child {
font-family: "Open Sans Extrabold";
font-weight: 900;
font-size: 70%;
color: $white;
}
}
/*
* BADGE_TITLE
* Display Title like a badge (with background-colored label)
*/
h2.badge-title {
display: flex;
flex-direction: row;
width: 100%;
color: $dark;
a & { text-decoration: none; } // ?!? keep it ?
span.title_label {
border-radius: 0.35rem 0 0 0.35rem;
color: $white;
font-size: 80%;
padding: 0.5em;
padding-right: 0;
h3 {
margin-bottom: 0.5rem;
}
}
span.title_action {
flex-grow: 1;
margin: 0 0 0 auto;
border-radius: 0 0.35rem 0.35rem 0;
background-color: $light;
padding: 0.2em 1em;
ul.small_in_title {
margin-top: 0.5em;
font-size: 70%;
padding-left: 1rem;
}
}
}
/// badge_title in AccompanyingCourse Work list Page
div.accompanying_course_work-list {
h2.badge-title {
span.title_label {
// Calculate same color then border:groove
background-color: shade-color($social-action-color, 34%);
}
span.title_action {
@include badge_title($social-action-color);
}
}
}
/// badge_title in Activities on resume page
div.activity-list {
h2.badge-title {
span.title_label {
// Calculate same color then border:groove
background-color: shade-color($activity-color, 34%);
h3 {
color: $white;
}
}
span.title_action {
@include badge_title($activity-color);
}
span.title_label {
div.duration {
font-size: 70%;
font-weight: 500;
p {
margin-bottom: 0;
text-align: right;
abbr {
text-decoration: none;
}
}
}
}
}
}

View File

@@ -1,44 +1,39 @@
// Additionnal colors
$sky-blue: #4bafe8;
// mixins variables
$social-issue-color: $sky-blue;
$social-action-color: $orange;
// Calculate same color then border:groove
// origin: $orange, computed: #965028
$social-action-label-color: shade-color($orange, 34%);
///
/// Social Issue mixin
// define visual badge for all social issues
/// Chill badge mixin
// define chill visual badge
///
@mixin badge_social_issue {
@mixin chill_badge($color) {
text-transform: capitalize !important;
font-weight: 500 !important;
border-left: 20px groove $social-issue-color;
border-left: 20px groove $color;
&:before {
content: '\f04b';
font-family: ForkAwesome;
color: $social-issue-color;
content: '\f04b';
color: $color;
}
}
///
/// Social badge mixin
// define visual badge for social issues or social action
///
@mixin badge_social($color) {
@include chill_badge($color);
&:before {
margin: 0 0.3em 0 -0.75em;
}
}
///
/// Social Action mixin
// define visual badge for all social actions
/// Generic mixin for titles like badge
// define visual badge used in title area
///
@mixin badge_social_action {
text-transform: capitalize !important;
font-weight: 500 !important;
border-left: 20px groove $social-action-color;
@mixin badge_title($color) {
@include chill_badge($color);
&:before {
content: '\f04b';
font-family: ForkAwesome;
color: $social-action-color;
margin: 0 0.3em 0 -0.75em;
margin: 0 0.3em 0 -1.05em;
}
}

View File

@@ -8,21 +8,28 @@
span.altname {}
}
/// SOCIAL-ISSUE
&.entity-social-issue {
margin-right: 0.3em;
font-size: 120%;
span.badge {
@include badge_social_issue;
}
}
/// SOCIAL-ACTION
/// SOCIAL-ISSUE AND SOCIAL-ACTION
&.entity-social-issue,
&.entity-social-action {
margin-right: 0.3em;
font-size: 120%;
span.badge {
@include badge_social_action;
// for too long badge in a narrow inside block
text-align: initial;
margin-bottom: 0.2em;
> span {
white-space: normal;
}
}
}
&.entity-social-issue {
span.badge {
@include badge_social($social-issue-color);
}
}
&.entity-social-action {
span.badge {
@include badge_social($social-action-color);
}
}
}

View File

@@ -12,9 +12,9 @@ export default {
<style lang="scss" scoped>
@import 'ChillMainAssets/module/bootstrap/shared';
@import 'ChillPersonAssets/chill/scss/mixins';
@import 'ChillMainAssets/chill/scss/chill_variables';
span.badge {
@include badge_social_issue;
@include badge_social($social-issue-color);
font-size: 95%;
margin-bottom: 5px;
margin-right: 1em;

View File

@@ -84,10 +84,10 @@ export default {
<style lang="scss">
@import 'ChillMainAssets/module/bootstrap/shared';
@import 'ChillPersonAssets/chill/scss/mixins';
@import 'ChillMainAssets/chill/scss/chill_variables';
div#accompanying-course {
span.multiselect__tag {
@include badge_social_issue;
@include badge_social($social-issue-color);
background: $chill-l-gray;
color: $dark;
}

View File

@@ -1,37 +1,102 @@
<template>
<div class="container">
<span class="name">
{{ item.result.text }}
<div class="container tpartycontainer">
<div class="tparty-identification">
<span class="name">
{{ item.result.text }}
</span>
<span class="location">
<template v-if="hasAddress">
{{ getAddress.text }} -
{{ getAddress.postcode.name }}
</template>
</span>
</div>
<div class="tpartyparent" v-if="hasParent">
<span class="name">
{{ item.result.parent.text }}
</span>
</div>
</div>
<div class="right_actions">
<span class="badge bg-chill-red" v-if="item.result.kind == 'child'">
{{ $t('thirdparty.contact')}}
</span>
<span class="badge bg-info" v-else-if="item.result.kind == 'company'">
{{ $t('thirdparty.company')}}
</span>
<span class="location">
{{ item.result.address.text }} -
{{ item.result.address.postcode.name }}
<span class="badge bg-secondary" v-else="item.result.kind == 'contact'">
{{ $t('thirdparty.contact')}}
</span>
</div>
<div class="right_actions">
<span class="badge rounded-pill bg-secondary" :title="item.key">
{{ $t('item.type_thirdparty') }}
</span>
<on-the-fly
type="thirdparty"
v-bind:id="item.result.id"
action="show">
type="thirdparty"
v-bind:id="item.result.id"
action="show">
</on-the-fly>
</div>
</div>
</template>
<script>
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
const i18n = {
messages: {
fr: {
thirdparty: {
contact: "Contact",
company: "Institution",
child: "Personne de contact"
}
}
}
};
export default {
name: 'SuggestionThirdParty',
components: {
OnTheFly
},
props: ['item']
props: ['item'],
i18n,
computed: {
hasAddress() {
if (this.$props.item.result.address !== null) {
return true;
}
if (this.$props.item.result.parent !== null) {
this.$props.item.result.parent.address !== null;
}
},
hasParent() {
return this.$props.item.result.parent !== null;
},
getAddress() {
if (this.$props.item.result.address !== null) {
return this.$props.item.result.address;
}
if (this.$props.item.result.parent.address !== null) {
return this.$props.item.result.parent.address;
}
return null;
}
}
}
</script>
<style lang="scss" scoped>
.tpartycontainer {
.tpartyparent {
.name {
font-weight: bold;
font-variant: all-small-caps;
}
}
}
</style>

View File

@@ -71,7 +71,7 @@
<div class="col-md-6 location mb-5">
{% if accompanyingCourse.locationStatus == 'person' %}
<h5>{{ 'This course is located by'|trans }}</h5>
<h2>{{ 'This course is located by'|trans }}</h2>
<h4>{{ accompanyingCourse.personLocation|chill_entity_render_string }}</h4>
{% elseif accompanyingCourse.locationStatus == 'address' %}
<h4>{{ 'This course has a temporarily location'|trans }}</h4>
@@ -82,97 +82,9 @@
{% endif %}
</div>
{# DISABLED
<h1>{{ 'Resume Accompanying Course'|trans }}</h1>
<div class="associated-persons mb-5">
<h2 class="mb-3">{{ 'Associated peoples'|trans }}</h2>
<div class="flex-table mb-3">
{% for participation in accompanyingCourse.participations %}
{% if participation.enddate is null %}
<div class="item-bloc">
{{ participation.person|chill_entity_render_box({
'render': 'bloc', 'addLink': false, 'addInfo': true, 'addAltNames': false,
'customButtons': { 'before': _self.button_person(participation.person) }
}) }}
</div>
{% endif %}
{% endfor %}
</div>
</div>
<div class="location mb-5">
<h2 class="mb-3">{{ 'Accompanying course location'|trans }}</h2>
{% if accompanyingCourse.locationStatus == 'person' %}
<p>{{ 'This course is located by'|trans }}
<b>{{ accompanyingCourse.personLocation|chill_entity_render_string }}</b>
</p>
{% elseif accompanyingCourse.locationStatus == 'address' %}
<p>{{ 'This course has a temporarily location'|trans }}</p>
{% endif %}
{% if accompanyingCourse.locationStatus != 'none' %}
<div class="flex-table">
<div class="item-bloc">
{{ accompanyingCourse.location|chill_entity_render_box }}
</div>
</div>
{% endif %}
</div>
<div class="requestor mb-5">
<h2 class="mb-3">{{ 'Requestor'|trans }}</h2>
{% if accompanyingCourse.requestorPerson is not empty %}
{% set requestor = accompanyingCourse.requestorPerson %}
{% set info = true %}
{% elseif accompanyingCourse.requestor is not empty %}
{% set requestor = accompanyingCourse.requestorThirdParty %}
{% set info = false %}
{% endif %}
{% if accompanyingCourse.requestor == null %}
<p class="chill-no-data-statement">{{ 'Any requestor to this accompanying course'|trans }}</p>
{% else %}
<div class="flex-bloc row row-cols-1 g-0">
<div class="item-bloc col">
{{ requestor|chill_entity_render_box({
'render': 'bloc', 'addLink': false, 'addEntity': true, 'addInfo': info, 'addAltNames': false
}) }}
</div>
</div>
{% endif %}
</div>
<div class="resources mb-5">
<h2 class="mb-3">{{ 'Resources'|trans }}</h2>
{% if accompanyingCourse.resources|length == 0 %}
<p class="chill-no-data-statement">{{ 'Any resource for this accompanying course'|trans }}</p>
{% else %}
<div class="flex-bloc row row-cols-1 row-cols-sm-2 row-cols-xl-3 row-cols-xxl-4 g-0">
{% for r in accompanyingCourse.resources %}
<div class="item-bloc col">
{% if r.person %}
{{ r.person|chill_entity_render_box({
'render': 'bloc', 'addLink': false, 'addEntity': true, 'addInfo': true, 'addAltNames': false
}) }}
{% endif %}
{% if r.thirdParty %}
{{ r.thirdParty|chill_entity_render_box({
'render': 'bloc', 'addLink': false, 'addEntity': true, 'addInfo': false
}) }}
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
#}
<div class="mt-5"></div>
<div class="social-actions mb-5">
<h2 class="mb-3">{{ 'Social actions'|trans }}</h2>
{% include 'ChillPersonBundle:AccompanyingCourseWork:list_by_accompanying_period.html.twig' with {'buttonText': false } %}
<h2 class="mb-3">{{ 'Last social actions'|trans }}</h2>
{% include 'ChillPersonBundle:AccompanyingCourseWork:list_recent_by_accompanying_period.html.twig' with {'buttonText': false } %}
</div>
{% block contentActivity %}
@@ -189,9 +101,9 @@
{% set accompanying_course_id = accompanyingCourse.id %}
{% endif %}
<h2>{{ 'Activity list' |trans }}</h2>
<h2 class="mb-3">{{ 'Last activities' |trans }}</h2>
{% include 'ChillActivityBundle:Activity:list.html.twig' with { 'context': 'accompanyingCourse', 'no_action': true } %}
{% include 'ChillActivityBundle:Activity:list_recent.html.twig' with { 'context': 'accompanyingCourse', 'no_action': true } %}
</div>
{% endblock %}

View File

@@ -10,7 +10,7 @@
<div class="item-bloc">
<div class="item-row">
<h2 class="title">
<h2 class="badge-title">
<span class="title_label">{{ 'accompanying_course_work.action'|trans }}</span>
<span class="title_action">{{ w.socialAction|chill_entity_render_string }}</span>
</h2>

View File

@@ -0,0 +1,32 @@
{% if works|length == 0 %}
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}
<a class="btn btn-sm btn-create"
href="" title="TODO"></a>{# TODO link #}
</p>
{% endif %}
<div class="accompanying_course_work-list">
{% for w in works | slice(0,5) %}
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"></a>
<h2 class="badge-title">
<span class="title_label">{{ 'accompanying_course_work.action'|trans }}</span>
<span class="title_action">{{ w.socialAction|chill_entity_render_string }}
<ul class="small_in_title">
<li>
<abbr title="{{ 'accompanying_course_work.start_date'|trans }}">{{ 'accompanying_course_work.start_date'|trans ~ ' : ' }}</abbr>
{{ w.startDate|format_date('short') }}
</li>
<li>
<abbr title="{{ 'Last updated by'|trans }}">{{ 'Last updated by'|trans ~ ' : ' }}</abbr>
{{ w.updatedBy|chill_entity_render_box }}, {{ w.updatedAt|format_datetime('short', 'short') }}
</li>
</ul>
</span>
</h2>
{% endfor %}
</div>

View File

@@ -58,11 +58,10 @@
</div>
<div class="item-row separator">
<div class="wrap-list">
{% if accompanying_period.requestorPerson is not null or accompanying_period.requestorThirdParty is not null %}
<div class="wl-row">
<div class="wl-col title"><h3>{{ 'Requestor'|trans }}</h3></div>
<div class="wl-col title"><h3>{{ 'Requestor'|trans({'gender': null }) }}</h3></div>
<div class="wl-col list">
{% if accompanying_period.requestorPerson is not null %}
<span class="wl-item badge-person">{{ accompanying_period.requestorPerson|chill_entity_render_string }}</span>

View File

@@ -61,7 +61,7 @@
</table>
{% endif %}
{{ form_start(form) }}
{{ form_start(form, {'attr' : {'id' : 'create-form'}}) }}
{{ form_row(form.lastName, { 'label' : 'Last name'|trans }) }}
@@ -104,3 +104,7 @@
</div>
{% endblock content %}
{% block js %}
{{ encore_entry_script_tags('mod_disablebuttons') }}
{% endblock js %}

View File

@@ -0,0 +1,69 @@
<?php
use PHPUnit\Framework\TestCase;
use Chill\PersonBundle\EventListener\PersonEventListener;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonAltName;
use Doctrine\Persistence\Event\LifecycleEventArgs;
class PersonCreateEventTest extends TestCase
{
/**
* @dataProvider generateNames
*/
public function testOnPrePersist($firstname, $firstnameExpected, $lastname, $lastnameExpected)
{
$listener = new PersonEventListener();
$person = new Person();
$person->setFirstName($firstname);
$person->setLastName($lastname);
$args = $this->createMock(LifecycleEventArgs::class);
$args->method('getObject')
->willReturn($person);
$listener->onPrePersist($args);
$this->assertEquals($firstnameExpected, $person->getFirstName());
$this->assertEquals($lastnameExpected, $person->getLastName());
}
/**
* @dataProvider generateAltNames
*/
public function testAltNamesOnPrePersist($altname, $altnameExpected)
{
$listener = new PersonEventListener();
$personAltname = new PersonAltName();
$personAltname->setLabel($altname);
$args = $this->createMock(LifecycleEventArgs::class);
$args->method('getObject')
->willReturn($personAltname);
$listener->onPrePersist($args);
$this->assertEquals($altnameExpected, $personAltname->getLabel());
}
public function generateNames(): iterator
{
yield ['émelie-marie', 'Émelie-Marie', 'lenaerts', 'LENAERTS'];
yield ['jean-marie', 'Jean-Marie', 'lenaerts', 'LENAERTS'];
yield ['vinCENT', 'Vincent', 'fastré', 'FASTRÉ'];
yield ['Vincent', 'Vincent', 'van Gogh', 'VAN GOGH'];
yield ['André marie', 'André Marie', 'Bah', 'BAH'];
}
public function generateAltNames(): iterator
{
yield ['vinCENT', 'VINCENT'];
yield ['jean-marie', 'JEAN-MARIE'];
yield ['fastré', 'FASTRÉ'];
yield ['émile', 'ÉMILE'];
}
}

View File

@@ -0,0 +1,12 @@
services:
Chill\PersonBundle\EventListener\PersonEventListener:
autoconfigure: true
tags:
-
name: 'doctrine.orm.entity_listener'
event: 'onPrePersist'
entity: 'Chill\PersonBundle\Entity\Person'
-
name: 'doctrine.orm.entity_listener'
event: 'onPrePersist'
entity: 'Chill\PersonBundle\Entity\PersonAltName'

View File

@@ -191,6 +191,7 @@ Associated peoples: Usagers concernés
Resources: Interlocuteurs privilégiés
Any requestor to this accompanying course: Aucun demandeur pour ce parcours
Social actions: Actions d'accompagnement
Last social actions: Les dernières actions d'accompagnement
Social issues: Problématiques sociales
Last events on accompanying course: Dernières actions de suivi
Edit & activate accompanying course: Modifier et valider
@@ -380,7 +381,7 @@ Create Accompanying Course: Créer un nouveau parcours
Drop Accompanying Course: Supprimer le parcours
This course is located at a temporarily address. You should locate this course to an user: Le parcours est localisé à une adresse temporaire. Il devrait être localisé auprès d'une personne concernée.
Accompanying course location: Localisation du parcours
This course is located by: Parcours localisé auprès de
This course is located by: Localisé auprès de
This course has a temporarily location: Localisation temporaire
Choose a person to locate by: Localiser auprès d'un usager concerné
Associate at least one member with an household, and set an address to this household: Associez au moins un membre du parcours à un ménage, et indiquez une adresse à ce ménage.