mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge remote-tracking branch 'origin/master' into issue464_document_on_person
This commit is contained in:
commit
3df2335cf4
@ -29,6 +29,7 @@ variables:
|
||||
REDIS_URL: redis://redis:6379
|
||||
# change vendor dir to make the app install into tests/apps
|
||||
COMPOSER_VENDOR_DIR: tests/app/vendor
|
||||
DEFAULT_CARRIER_CODE: BE
|
||||
|
||||
stages:
|
||||
- Composer install
|
||||
@ -78,6 +79,7 @@ psalm_tests:
|
||||
image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4
|
||||
script:
|
||||
- bin/grumphp run --tasks=psalm
|
||||
allow_failure: true
|
||||
artifacts:
|
||||
expire_in: 30 min
|
||||
paths:
|
||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -12,7 +12,8 @@ and this project adheres to
|
||||
|
||||
<!-- write down unreleased development here -->
|
||||
* [person] Add document generation in admin and in person/{id}/document (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/464)
|
||||
|
||||
* [activity] do not override location if already exist (when validating new activity) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/470)
|
||||
* [parcours] Toggle emergency/intensity only by referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442)
|
||||
* [docstore] Add an API entrypoint for StoredObject (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
|
||||
* [person] Add the possibility of uploading existing documents to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
|
||||
* [person] Add title to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
|
||||
@ -20,6 +21,13 @@ and this project adheres to
|
||||
* [Person/Household list] when listing other simultaneous members of an household, exclude the members on person, not on members (avoid to show two membersship with the same person)
|
||||
* [draft periods] add a delete button (if acl granted) on each draft period listed on draft period page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/463)
|
||||
* [Person] Display suffixText in RenderPerson, PersonText.vue, RenderPersonBox.vue (was made for displaying "enfant confie") (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/441)
|
||||
* [person] residential address: show residential address or info in PersonRenderBox, refactor Residential Address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/439)
|
||||
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
|
||||
* [documents] Improve flex-table item-col placement when long buttons and long metadata
|
||||
* [thirdparty] Fix display of multiple contact badges so they wrap onto next line (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/482)
|
||||
* [confidential] Fix position of toggle button so it does not cover text nor fall outside of box (no issue)
|
||||
* [parcours] Fix edit of both thirdparty and contact name (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/474)
|
||||
* [template] do not list inactive templates (for doc generator)
|
||||
|
||||
## Test releases
|
||||
|
||||
@ -35,7 +43,6 @@ and this project adheres to
|
||||
* [person]: AddPersons: add suggestion of name when creating new person or thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
|
||||
* [main] Address: fix small bug: when modifying an address without street (isNoAddress), also check errors if street is an empty string as back-end change null value to empty string for street (and streetNumber)
|
||||
* [main] Address: stronger client-side validation of addresses (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/449)
|
||||
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
|
||||
* [person] accompanying course: filter suggested entities by open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/415)
|
||||
[activity] can click through the cross icon for removing person in concerned group (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
|
||||
[activity] correct associated persons by considering only open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
|
||||
@ -49,10 +56,9 @@ and this project adheres to
|
||||
* [thirdparty_contact]: in search results the 'qualité' is displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/465)
|
||||
* [bug]: fix confidential toggle of address in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/460)
|
||||
|
||||
|
||||
## Test releases
|
||||
* Creation of PickCivilityType, and implementation in PersonType and ThirdpartyType
|
||||
* [renderbox]: Fix display of address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/462)
|
||||
* [renderbox]: Add email in personRenderBox, this was not yet displayed.
|
||||
|
||||
### test release 2022-02-14
|
||||
|
||||
@ -69,6 +75,7 @@ and this project adheres to
|
||||
* [parcours]: Mes parcours brouillon added to user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/440)
|
||||
* [Documents]: List view adapted to display more information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/414)
|
||||
* [person]: style fix in parcours listing per person. (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/432)
|
||||
* [parcours]: Only the referrer can toggle the intensity of the parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442)
|
||||
* [household]: display address of current household (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/415)
|
||||
* ajoute un ordre dans les localisation (api)
|
||||
* [pick entity]: fix translations in modal (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/419)
|
||||
@ -317,6 +324,7 @@ and this project adheres to
|
||||
* add an endpoint for checking permissions. See https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/232
|
||||
* [activity] for a new activity: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties
|
||||
* [calendar] for a new rdv: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties
|
||||
* [period] Validation added when period is confidential and confirmed -> user cannot be null.
|
||||
|
||||
|
||||
## Test releases
|
||||
|
@ -22,6 +22,7 @@
|
||||
"league/csv": "^9.7.1",
|
||||
"nyholm/psr7": "^1.4",
|
||||
"ocramius/package-versions": "^1.10",
|
||||
"odolbeau/phone-number-bundle": "^3.6",
|
||||
"phpoffice/phpspreadsheet": "^1.16",
|
||||
"ramsey/uuid-doctrine": "^1.7",
|
||||
"sensio/framework-extra-bundle": "^5.5",
|
||||
|
@ -295,11 +295,6 @@ parameters:
|
||||
count: 3
|
||||
path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/DateIntervalTransformer.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
|
||||
count: 1
|
||||
path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/ObjectToIdTransformer.php
|
||||
|
||||
-
|
||||
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
|
||||
count: 2
|
||||
@ -325,11 +320,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
|
||||
count: 1
|
||||
path: src/Bundle/ChillMainBundle/Validation/Validator/ValidPhonenumber.php
|
||||
|
||||
-
|
||||
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
|
||||
count: 1
|
||||
|
@ -110,10 +110,8 @@ export default function prepareLocations(store) {
|
||||
console.log('default loation id', window.default_location_id);
|
||||
if (window.default_location_id) {
|
||||
for (let group of store.state.availableLocations) {
|
||||
console.log(group);
|
||||
let location = group.locations.find((l) => l.id === window.default_location_id);
|
||||
console.log(location);
|
||||
if (location !== undefined) {
|
||||
if (location !== undefined & store.state.activity.location === null) {
|
||||
store.dispatch('updateLocation', location);
|
||||
break;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ activity:
|
||||
Insert a document: Insérer un document
|
||||
Remove a document: Supprimer le document
|
||||
comment: Commentaire
|
||||
No documents: Pas de documents
|
||||
No documents: Aucun document
|
||||
|
||||
#timeline
|
||||
'%user% has done an %activity_type%': '%user% a effectué une activité de type "%activity_type%"'
|
||||
|
@ -70,6 +70,7 @@ final class DocGeneratorTemplateRepository implements ObjectRepository
|
||||
|
||||
$builder
|
||||
->where('t.entity LIKE :entity')
|
||||
->andWhere($builder->expr()->eq('t.active', "'TRUE'"))
|
||||
->setParameter('entity', addslashes($entity));
|
||||
|
||||
return $builder
|
||||
|
@ -21,6 +21,8 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use DateTime;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
@ -59,21 +61,37 @@ class DocumentAccompanyingCourseController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}", name="accompanying_course_document_delete", methods="DELETE")
|
||||
* @Route("/{id}/delete", name="chill_docstore_accompanying_course_document_delete")
|
||||
*/
|
||||
public function delete(Request $request, AccompanyingPeriod $course, AccompanyingCourseDocument $document): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(AccompanyingCourseDocumentVoter::DELETE, $document);
|
||||
|
||||
if ($this->isCsrfTokenValid('delete' . $document->getId(), $request->request->get('_token'))) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->remove($document);
|
||||
$em->flush();
|
||||
$form = $this->createForm(FormType::class);
|
||||
$form->add('submit', SubmitType::class, ['label' => 'Delete']);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->getDoctrine()->getManager()->remove($document);
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator->trans('The document is successfully removed'));
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $this->redirect($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'accompanying_course_document_index',
|
||||
['accompanyingCourse' => $course->getId()]
|
||||
return $this->redirectToRoute('accompanying_course_document_index', ['course' => $course->getId()]);
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'ChillDocStoreBundle:AccompanyingCourseDocument:delete.html.twig',
|
||||
[
|
||||
'document' => $document,
|
||||
'delete_form' => $form->createView(),
|
||||
'accompanyingCourse' => $course,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace Chill\DocStoreBundle\Controller;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\DocStoreBundle\Form\PersonDocumentType;
|
||||
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
@ -22,6 +23,8 @@ use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use DateTime;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
@ -64,22 +67,37 @@ class DocumentPersonController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{id}", name="person_document_delete", methods="DELETE")
|
||||
* @Route("/{id}/delete", name="chill_docstore_person_document_delete")
|
||||
*/
|
||||
public function delete(Request $request, Person $person, PersonDocument $document): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||
$this->denyAccessUnlessGranted('CHILL_PERSON_DOCUMENT_DELETE', $document);
|
||||
$this->denyAccessUnlessGranted(PersonDocumentVoter::DELETE, $document);
|
||||
|
||||
if ($this->isCsrfTokenValid('delete' . $document->getId(), $request->request->get('_token'))) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->remove($document);
|
||||
$em->flush();
|
||||
$form = $this->createForm(FormType::class);
|
||||
$form->add('submit', SubmitType::class, ['label' => 'Delete']);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->getDoctrine()->getManager()->remove($document);
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator->trans('The document is successfully removed'));
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $this->redirect($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'person_document_index',
|
||||
['person' => $person->getId()]
|
||||
return $this->redirectToRoute('person_document_index', ['person' => $person->getId()]);
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'ChillDocStoreBundle:PersonDocument:delete.html.twig',
|
||||
[
|
||||
'document' => $document,
|
||||
'delete_form' => $form->createView(),
|
||||
'person' => $person,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@ -98,7 +116,6 @@ class DocumentPersonController extends AbstractController
|
||||
PersonDocumentType::class,
|
||||
$document,
|
||||
[
|
||||
'center' => $document->getCenter(),
|
||||
'role' => 'CHILL_PERSON_DOCUMENT_UPDATE',
|
||||
]
|
||||
);
|
||||
@ -199,7 +216,6 @@ class DocumentPersonController extends AbstractController
|
||||
$document->setDate(new DateTime('Now'));
|
||||
|
||||
$form = $this->createForm(PersonDocumentType::class, $document, [
|
||||
'center' => $document->getCenter(),
|
||||
'role' => 'CHILL_PERSON_DOCUMENT_CREATE',
|
||||
]);
|
||||
$form->handleRequest($request);
|
||||
|
@ -13,15 +13,13 @@ namespace Chill\DocStoreBundle\Form;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\Document;
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
@ -31,34 +29,16 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class PersonDocumentType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
/**
|
||||
* @var ObjectManager
|
||||
*/
|
||||
protected $om;
|
||||
|
||||
/**
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
|
||||
/**
|
||||
* the user running this form.
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
protected $user;
|
||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||
|
||||
private ParameterBagInterface $parameterBag;
|
||||
|
||||
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
TranslatableStringHelper $translatableStringHelper,
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
ScopeResolverDispatcher $scopeResolverDispatcher,
|
||||
ParameterBagInterface $parameterBag
|
||||
) {
|
||||
@ -96,7 +76,7 @@ class PersonDocumentType extends AbstractType
|
||||
|
||||
if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
|
||||
$builder->add('scope', ScopePickerType::class, [
|
||||
'center' => $options['center'],
|
||||
'center' => $this->centerResolverDispatcher->resolveCenter($document),
|
||||
'role' => $options['role'],
|
||||
]);
|
||||
}
|
||||
@ -108,8 +88,7 @@ class PersonDocumentType extends AbstractType
|
||||
'data_class' => Document::class,
|
||||
]);
|
||||
|
||||
$resolver->setRequired(['role', 'center'])
|
||||
->setAllowedTypes('role', ['string'])
|
||||
->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class]);
|
||||
$resolver->setRequired(['role'])
|
||||
->setAllowedTypes('role', ['string']);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
<form method="post" action="{{ path('accompanying_course_document_delete', {'id': document.id, 'course': course.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ document.id) }}">
|
||||
<button class="btn btn-delete">{{ 'Delete' | trans }}</button>
|
||||
</form>
|
@ -0,0 +1,43 @@
|
||||
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% block title %}{{ 'Delete document ?' }}{% endblock %}
|
||||
|
||||
{% block docdescription %}
|
||||
<dl class="chill_view_data">
|
||||
<dt>{{ 'Title'|trans }}</dt>
|
||||
<dd>{{ document.title }}</dd>
|
||||
|
||||
{% if document.scope is not null %}
|
||||
<dt>{{ 'Scope' | trans }}</dt>
|
||||
<dd>{{ document.scope.name | localize_translatable_string }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt>{{ 'Category'|trans }}</dt>
|
||||
<dd>{{ document.category.name|localize_translatable_string }}</dd>
|
||||
|
||||
<dt>{{ 'Description' | trans }}</dt>
|
||||
<dd>
|
||||
{% if document.description is empty %}
|
||||
<span class="chill-no-data-statement">{{ 'Any description'|trans }}</span>
|
||||
{% else %}
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ document.description|chill_markdown_to_html }}
|
||||
</blockquote>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ include('@ChillMain/Util/confirmation_template.html.twig',
|
||||
{
|
||||
'title' : 'Delete document ?'|trans,
|
||||
'display_content' : block('docdescription'),
|
||||
'confirm_question' : 'Are you sure you want to remove this document ?'|trans,
|
||||
'cancel_route' : 'accompanying_course_document_index',
|
||||
'cancel_parameters' : {'course' : accompanyingCourse.id, 'id': document.id},
|
||||
'form' : delete_form
|
||||
} ) }}
|
||||
{% endblock %}
|
@ -25,6 +25,11 @@
|
||||
{{ 'Back to the list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_DELETE', document) %}
|
||||
<li class="delete">
|
||||
<a href="{{ chill_return_path_or('chill_docstore_accompanying_course_document_delete', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-delete"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="edit">
|
||||
<button class="btn btn-edit">{{ 'Edit'|trans }}</button>
|
||||
</li>
|
||||
|
@ -49,12 +49,9 @@
|
||||
{{ 'Back to the list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
</li>
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button }}
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_DELETE', document) %}
|
||||
<li class="delete">
|
||||
<a href="{{ chill_return_path_or('chill_docstore_accompanying_course_document_delete', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-delete"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %}
|
||||
@ -63,6 +60,14 @@
|
||||
class="btn btn-edit" title="{{ 'Edit attributes' | trans }}"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
</li>
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %}
|
||||
{% if workflows_frame is not empty %}
|
||||
<li>
|
||||
|
@ -24,8 +24,8 @@
|
||||
<div class="item-col">
|
||||
<div class="container">
|
||||
{% if document.date is not null %}
|
||||
<div class="dates row" style="float: right;">
|
||||
<span>{{ document.createdAt|format_date('short') }}</span>
|
||||
<div class="dates row text-end">
|
||||
<span>{{ document.date|format_date('short') }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -42,9 +42,18 @@
|
||||
<div class="item-col item-meta">
|
||||
{{ mmm.createdBy(document) }}
|
||||
</div>
|
||||
<div class="item-col">
|
||||
<ul class="record_actions">
|
||||
<ul class="item-col record_actions flex-shrink-1">
|
||||
{% if document.course is defined %}
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_DELETE', document) %}
|
||||
<li class="delete">
|
||||
<a href="{{ chill_return_path_or('chill_docstore_accompanying_course_document_delete', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-delete"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %}
|
||||
<li>
|
||||
<a href="{{ path('accompanying_course_document_edit', {'course': accompanyingCourse.id, 'id': document.id }) }}" class="btn btn-update"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_SEE_DETAILS', document) %}
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
@ -53,15 +62,20 @@
|
||||
<a href="{{ chill_path_add_return_path('accompanying_course_document_show', {'course': accompanyingCourse.id, 'id': document.id}) }}" class="btn btn-show"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE', document) %}
|
||||
<li>
|
||||
<a href="{{ path('accompanying_course_document_edit', {'course': accompanyingCourse.id, 'id': document.id }) }}" class="btn btn-update"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{{ chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) }}
|
||||
</li>
|
||||
{% else %}
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_DELETE', document) %}
|
||||
<li class="delete">
|
||||
<a href="{{ chill_return_path_or('chill_docstore_person_document_delete', {'person': person.id, 'id': document.id}) }}" class="btn btn-delete"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %}
|
||||
<li>
|
||||
<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="btn btn-update"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_SEE_DETAILS', document) %}
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
@ -70,13 +84,8 @@
|
||||
<a href="{{ path('person_document_show', {'person': person.id, 'id': document.id}) }}" class="btn btn-show"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_UPDATE', document) %}
|
||||
<li>
|
||||
<a href="{{ path('person_document_edit', {'person': person.id, 'id': document.id}) }}" class="btn btn-update"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,43 @@
|
||||
{% extends "@ChillPerson/Person/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% block title %}{{ 'Delete document ?' }}{% endblock %}
|
||||
|
||||
{% block docdescription %}
|
||||
<dl class="chill_view_data">
|
||||
<dt>{{ 'Title'|trans }}</dt>
|
||||
<dd>{{ document.title }}</dd>
|
||||
|
||||
{% if document.scope is not null %}
|
||||
<dt>{{ 'Scope' | trans }}</dt>
|
||||
<dd>{{ document.scope.name | localize_translatable_string }}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt>{{ 'Category'|trans }}</dt>
|
||||
<dd>{{ document.category.name|localize_translatable_string }}</dd>
|
||||
|
||||
<dt>{{ 'Description' | trans }}</dt>
|
||||
<dd>
|
||||
{% if document.description is empty %}
|
||||
<span class="chill-no-data-statement">{{ 'Any description'|trans }}</span>
|
||||
{% else %}
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ document.description|chill_markdown_to_html }}
|
||||
</blockquote>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
{% endblock %}
|
||||
|
||||
{% block personcontent %}
|
||||
{{ include('@ChillMain/Util/confirmation_template.html.twig',
|
||||
{
|
||||
'title' : 'Delete document ?'|trans,
|
||||
'display_content' : block('docdescription'),
|
||||
'confirm_question' : 'Are you sure you want to remove this document ?'|trans,
|
||||
'cancel_route' : 'person_document_index',
|
||||
'cancel_parameters' : {'person' : person.id, 'id': document.id},
|
||||
'form' : delete_form
|
||||
} ) }}
|
||||
{% endblock %}
|
@ -36,20 +36,20 @@
|
||||
{{ form_row(form.description) }}
|
||||
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
|
||||
|
||||
<ul class="record_actions">
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="btn btn-cancel">
|
||||
<a href="{{ chill_return_path_or('person_document_index', {'person': person.id}) }}" class="btn btn-cancel">
|
||||
{{ 'Back to the list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_DELETE', document) %}
|
||||
<li class="delete">
|
||||
<a href="{{ chill_return_path_or('chill_docstore_person_document_delete', {'person': person.id, 'id': document.id}) }}" class="btn btn-delete"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="edit">
|
||||
<button class="btn btn-edit">{{ 'Edit'|trans }}</button>
|
||||
</li>
|
||||
{# {% if is_granted('CHILL_PERSON_DOCUMENT_DELETE', document) %}
|
||||
<li class="delete">
|
||||
{{ include('ChillDocStoreBundle:PersonDocument:_delete_form.html.twig') }}
|
||||
</li>
|
||||
{% endif %} #}
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
@ -40,9 +40,9 @@
|
||||
{{ form_row(form.description) }}
|
||||
{{ form_row(form.object, { 'label': 'Document', 'existing': document.object }) }}
|
||||
|
||||
<ul class="record_actions">
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<a href="{{ path('person_document_index', {'person': person.id}) }}" class="btn btn-cancel">
|
||||
<a href="{{ chill_return_path_or('person_document_index', {'person': person.id}) }}" class="btn btn-cancel">
|
||||
{{ 'Back to the list' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
|
@ -64,13 +64,9 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
</li>
|
||||
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button }}
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_DELETE', document) %}
|
||||
<li class="delete">
|
||||
<a href="{{ chill_return_path_or('chill_docstore_person_document_delete', {'person': person.id, 'id': document.id}) }}" class="btn btn-delete"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@ -82,5 +78,15 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li>
|
||||
{{ m.download_button(document.object, document.title) }}
|
||||
</li>
|
||||
|
||||
{% if chill_document_is_editable(document.object) %}
|
||||
<li>
|
||||
{{ document.object|chill_document_edit_button }}
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{# {{ include('ChillDocStoreBundle:PersonDocument:_delete_form.html.twig') }} #}
|
||||
{% endblock %}
|
||||
|
@ -19,6 +19,12 @@ The document is successfully registered: Le document est enregistré
|
||||
The document is successfully updated: Le document est mis à jour
|
||||
Any description: Aucune description
|
||||
|
||||
# delete
|
||||
Delete document ?: Supprimer le document ?
|
||||
Are you sure you want to remove this document ?: Êtes-vous sûr·e de vouloir supprimer ce document ?
|
||||
The document is successfully removed: Le document a été supprimé
|
||||
|
||||
|
||||
# dropzone upload
|
||||
File too big: Fichier trop volumineux
|
||||
Drop your file or click here: Cliquez ici ou faites glissez votre nouveau fichier dans cette zone
|
||||
|
@ -39,6 +39,7 @@ use Chill\MainBundle\Form\LocationTypeType;
|
||||
use Chill\MainBundle\Form\UserJobType;
|
||||
use Chill\MainBundle\Form\UserType;
|
||||
use Exception;
|
||||
use Misd\PhoneNumberBundle\Doctrine\DBAL\Types\PhoneNumberType;
|
||||
use Ramsey\Uuid\Doctrine\UuidType;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
@ -235,6 +236,7 @@ class ChillMainExtension extends Extension implements
|
||||
'dateinterval' => NativeDateIntervalType::class,
|
||||
'point' => PointType::class,
|
||||
'uuid' => UuidType::class,
|
||||
'phone_number' => PhoneNumberType::class,
|
||||
],
|
||||
],
|
||||
]
|
||||
|
@ -97,6 +97,9 @@ class Configuration implements ConfigurationInterface
|
||||
->scalarNode('twilio_secret')
|
||||
->defaultNull()
|
||||
->end()
|
||||
->scalarNode('default_carrier_code')
|
||||
->defaultNull()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('acl')
|
||||
|
@ -18,9 +18,9 @@ use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="chill_main_location")
|
||||
@ -90,20 +90,18 @@ class Location implements TrackCreationInterface, TrackUpdateInterface
|
||||
private ?string $name = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=64, nullable=true)
|
||||
* @ORM\Column(type="phone_number", nullable=true)
|
||||
* @Serializer\Groups({"read", "write", "docgen:read"})
|
||||
* @Assert\Regex(pattern="/^([\+{1}])([0-9\s*]{4,20})$/")
|
||||
* @PhonenumberConstraint(type="any")
|
||||
*/
|
||||
private ?string $phonenumber1 = null;
|
||||
private ?PhoneNumber $phonenumber1 = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=64, nullable=true)
|
||||
* @ORM\Column(type="phone_number", nullable=true)
|
||||
* @Serializer\Groups({"read", "write", "docgen:read"})
|
||||
* @Assert\Regex(pattern="/^([\+{1}])([0-9\s*]{4,20})$/")
|
||||
* @PhonenumberConstraint(type="any")
|
||||
*/
|
||||
private ?string $phonenumber2 = null;
|
||||
private ?PhoneNumber $phonenumber2 = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable", nullable=true)
|
||||
@ -162,12 +160,12 @@ class Location implements TrackCreationInterface, TrackUpdateInterface
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getPhonenumber1(): ?string
|
||||
public function getPhonenumber1(): ?PhoneNumber
|
||||
{
|
||||
return $this->phonenumber1;
|
||||
}
|
||||
|
||||
public function getPhonenumber2(): ?string
|
||||
public function getPhonenumber2(): ?PhoneNumber
|
||||
{
|
||||
return $this->phonenumber2;
|
||||
}
|
||||
@ -238,14 +236,14 @@ class Location implements TrackCreationInterface, TrackUpdateInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPhonenumber1(?string $phonenumber1): self
|
||||
public function setPhonenumber1(?PhoneNumber $phonenumber1): self
|
||||
{
|
||||
$this->phonenumber1 = $phonenumber1;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPhonenumber2(?string $phonenumber2): self
|
||||
public function setPhonenumber2(?PhoneNumber $phonenumber2): self
|
||||
{
|
||||
$this->phonenumber2 = $phonenumber2;
|
||||
|
||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\LocationType as EntityLocationType;
|
||||
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
||||
use Chill\MainBundle\Form\Type\PickAddressType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
@ -46,8 +47,8 @@ final class LocationFormType extends AbstractType
|
||||
},
|
||||
])
|
||||
->add('name', TextType::class)
|
||||
->add('phonenumber1', TextType::class, ['required' => false])
|
||||
->add('phonenumber2', TextType::class, ['required' => false])
|
||||
->add('phonenumber1', ChillPhoneNumberType::class, ['required' => false])
|
||||
->add('phonenumber2', ChillPhoneNumberType::class, ['required' => false])
|
||||
->add('email', TextType::class, ['required' => false])
|
||||
->add('address', PickAddressType::class, [
|
||||
'required' => false,
|
||||
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Form\Type;
|
||||
|
||||
use libphonenumber\PhoneNumberFormat;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use Misd\PhoneNumberBundle\Form\Type\PhoneNumberType;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use function array_key_exists;
|
||||
|
||||
class ChillPhoneNumberType extends AbstractType
|
||||
{
|
||||
private string $defaultCarrierCode;
|
||||
|
||||
private PhoneNumberUtil $phoneNumberUtil;
|
||||
|
||||
public function __construct(ParameterBagInterface $parameterBag)
|
||||
{
|
||||
$this->defaultCarrierCode = $parameterBag->get('chill_main')['phone_helper']['default_carrier_code'];
|
||||
$this->phoneNumberUtil = PhoneNumberUtil::getInstance();
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver
|
||||
->setDefault('default_region', $this->defaultCarrierCode)
|
||||
->setDefault('format', PhoneNumberFormat::NATIONAL)
|
||||
->setDefault('type', \libphonenumber\PhoneNumberType::FIXED_LINE_OR_MOBILE)
|
||||
->setNormalizer('attr', function (Options $options, $value) {
|
||||
if (array_key_exists('placeholder', $value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$examplePhoneNumber = $this->phoneNumberUtil->getExampleNumberForType($this->defaultCarrierCode, $options['type']);
|
||||
|
||||
return array_merge(
|
||||
$value,
|
||||
[
|
||||
'placeholder' => PhoneNumberUtil::getInstance()->format($examplePhoneNumber, $options['format']),
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return PhoneNumberType::class;
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ class ObjectToIdTransformer implements DataTransformerInterface
|
||||
*/
|
||||
public function reverseTransform($id)
|
||||
{
|
||||
if (!$id) {
|
||||
if (null === $id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ class ObjectToIdTransformer implements DataTransformerInterface
|
||||
->getRepository($this->class)
|
||||
->find($id);
|
||||
|
||||
if (!$object) {
|
||||
if (null === $object) {
|
||||
throw new TransformationFailedException();
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ class ObjectToIdTransformer implements DataTransformerInterface
|
||||
*/
|
||||
public function transform($object)
|
||||
{
|
||||
if (!$object) {
|
||||
if (null === $object) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Phonenumber;
|
||||
|
||||
use libphonenumber\PhoneNumber;
|
||||
|
||||
/**
|
||||
* Helper to some task linked to phonenumber.
|
||||
*
|
||||
* Currently, only Twilio is supported (https://www.twilio.com/lookup). A method
|
||||
* allow to check if the helper is configured for validation. This should be used
|
||||
* before doing some validation.
|
||||
*/
|
||||
interface PhoneNumberHelperInterface
|
||||
{
|
||||
public function format(PhoneNumber $phoneNumber): string;
|
||||
|
||||
/**
|
||||
* Get type (mobile, landline, ...) for phone number.
|
||||
*/
|
||||
public function getType(string $phonenumber): string;
|
||||
|
||||
/**
|
||||
* Return true if the validation is configured and available.
|
||||
*/
|
||||
public function isPhonenumberValidationConfigured(): bool;
|
||||
|
||||
/**
|
||||
* Return true if the phonenumber is a landline or voip phone. Return always true
|
||||
* if the validation is not configured.
|
||||
*/
|
||||
public function isValidPhonenumberAny(string $phonenumber): bool;
|
||||
|
||||
/**
|
||||
* Return true if the phonenumber is a landline or voip phone. Return always true
|
||||
* if the validation is not configured.
|
||||
*/
|
||||
public function isValidPhonenumberLandOrVoip(string $phonenumber): bool;
|
||||
|
||||
/**
|
||||
* REturn true if the phoennumber is a mobile phone. Return always true
|
||||
* if the validation is not configured.
|
||||
*/
|
||||
public function isValidPhonenumberMobile(string $phonenumber): bool;
|
||||
}
|
@ -15,8 +15,12 @@ use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use libphonenumber\NumberParseException;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
use function array_key_exists;
|
||||
use function in_array;
|
||||
@ -24,40 +28,32 @@ use function json_decode;
|
||||
use function preg_replace;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Helper to some task linked to phonenumber.
|
||||
*
|
||||
* Currently, only Twilio is supported (https://www.twilio.com/lookup). A method
|
||||
* allow to check if the helper is configured for validation. This should be used
|
||||
* before doing some validation.
|
||||
*/
|
||||
class PhonenumberHelper
|
||||
final class PhonenumberHelper implements PhoneNumberHelperInterface
|
||||
{
|
||||
public const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
|
||||
|
||||
public const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
|
||||
|
||||
protected CacheItemPoolInterface $cachePool;
|
||||
private CacheItemPoolInterface $cachePool;
|
||||
|
||||
/**
|
||||
* TRUE if the client is properly configured.
|
||||
*/
|
||||
protected bool $isConfigured = false;
|
||||
private array $config;
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
private bool $isConfigured = false;
|
||||
|
||||
/**
|
||||
* Twilio client.
|
||||
*/
|
||||
protected Client $twilioClient;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
private PhonenumberUtil $phoneNumberUtil;
|
||||
|
||||
private Client $twilioClient;
|
||||
|
||||
public function __construct(
|
||||
CacheItemPoolInterface $cachePool,
|
||||
$config,
|
||||
CacheItemPoolInterface $cacheUserData,
|
||||
ParameterBagInterface $parameterBag,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->cachePool = $cachePool;
|
||||
$this->cachePool = $cacheUserData;
|
||||
$this->config = $config = $parameterBag->get('chill_main.phone_helper');
|
||||
|
||||
if (
|
||||
array_key_exists('twilio_sid', $config)
|
||||
@ -72,11 +68,19 @@ class PhonenumberHelper
|
||||
]);
|
||||
$this->isConfigured = true;
|
||||
}
|
||||
|
||||
$this->phoneNumberUtil = PhoneNumberUtil::getInstance();
|
||||
}
|
||||
|
||||
public function format($phonenumber)
|
||||
/**
|
||||
* @param string $phoneNumber A national phone number starting with +
|
||||
*
|
||||
* @throws NumberParseException
|
||||
*/
|
||||
public function format(PhoneNumber $phoneNumber): string
|
||||
{
|
||||
return $this->performTwilioFormat($phonenumber);
|
||||
return $this->phoneNumberUtil
|
||||
->formatOutOfCountryCallingNumber($phoneNumber, $this->config['default_carrier_code']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,7 +141,7 @@ class PhonenumberHelper
|
||||
}
|
||||
|
||||
/**
|
||||
* REturn true if the phoennumber is a mobile phone. Return always true
|
||||
* REturn true if the phonenumber is a mobile phone. Return always true
|
||||
* if the validation is not configured.
|
||||
*
|
||||
* @param string $phonenumber
|
||||
@ -157,68 +161,7 @@ class PhonenumberHelper
|
||||
return 'mobile' === $validation;
|
||||
}
|
||||
|
||||
protected function performTwilioFormat($phonenumber)
|
||||
{
|
||||
if (false === $this->isPhonenumberValidationConfigured()) {
|
||||
return $phonenumber;
|
||||
}
|
||||
|
||||
// filter only number
|
||||
$filtered = preg_replace('/[^0-9]/', '', $phonenumber);
|
||||
|
||||
$item = $this->cachePool->getItem('pnum_format_nat_' . $filtered);
|
||||
|
||||
if ($item->isHit()) {
|
||||
return $item->get();
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->twilioClient->get(sprintf(self::FORMAT_URI, '+' . $filtered), [
|
||||
'http_errors' => true,
|
||||
]);
|
||||
} catch (ClientException $e) {
|
||||
$response = $e->getResponse();
|
||||
$this->logger->error('[phonenumber helper] Could not format number '
|
||||
. 'due to client error', [
|
||||
'message' => $response->getBody()->getContents(),
|
||||
'status_code' => $response->getStatusCode(),
|
||||
'phonenumber' => $phonenumber,
|
||||
]);
|
||||
|
||||
return $phonenumber;
|
||||
} catch (ServerException $e) {
|
||||
$response = $e->getResponse();
|
||||
$this->logger->error('[phonenumber helper] Could not format number '
|
||||
. 'due to server error', [
|
||||
'message' => $response->getBody()->getContents(),
|
||||
'status_code' => $response->getStatusCode(),
|
||||
'phonenumber' => $phonenumber,
|
||||
]);
|
||||
|
||||
return null;
|
||||
} catch (ConnectException $e) {
|
||||
$this->logger->error('[phonenumber helper] Could not format number '
|
||||
. 'due to connect error', [
|
||||
'message' => $e->getMessage(),
|
||||
'phonenumber' => $phonenumber,
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$format = json_decode($response->getBody()->getContents())->national_format;
|
||||
|
||||
$item
|
||||
->set($format)
|
||||
// expires after 3d
|
||||
->expiresAfter(3600 * 24 * 3);
|
||||
|
||||
$this->cachePool->save($item);
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
protected function performTwilioLookup($phonenumber)
|
||||
private function performTwilioLookup($phonenumber)
|
||||
{
|
||||
if (false === $this->isPhonenumberValidationConfigured()) {
|
||||
return null;
|
||||
@ -230,7 +173,7 @@ class PhonenumberHelper
|
||||
$item = $this->cachePool->getItem('pnum_' . $filtered);
|
||||
|
||||
if ($item->isHit()) {
|
||||
//return $item->get();
|
||||
return $item->get();
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -16,10 +16,7 @@ use Twig\TwigFilter;
|
||||
|
||||
class Templating extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var PhonenumberHelper
|
||||
*/
|
||||
protected $phonenumberHelper;
|
||||
protected PhonenumberHelper $phonenumberHelper;
|
||||
|
||||
public function __construct(PhonenumberHelper $phonenumberHelper)
|
||||
{
|
||||
|
@ -278,6 +278,7 @@ table.table-bordered {
|
||||
}
|
||||
|
||||
/// meta-data
|
||||
div.createdBy,
|
||||
div.updatedBy,
|
||||
div.metadata {
|
||||
span.user, span.date {
|
||||
|
@ -279,6 +279,7 @@ div.wrap-header {
|
||||
|
||||
li {
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -316,7 +317,7 @@ div.float-button {
|
||||
|
||||
div.action {
|
||||
height: calc(100% - 0em);
|
||||
shape-outside: inset(calc(100% - 4em) 0 0);
|
||||
shape-outside: inset(calc(100% - 2em) 0 0);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
padding: 0 0 0 1em;
|
||||
@ -328,6 +329,10 @@ div.float-button {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chill-entity.entity-address .address p {
|
||||
display: unset;
|
||||
}
|
||||
}
|
||||
&.debug {
|
||||
padding: 1em;
|
||||
|
@ -1,7 +1,7 @@
|
||||
ul.record_actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap-reverse;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
padding: 0.5em 0;
|
||||
|
||||
|
@ -72,9 +72,9 @@ section.chill-entity {
|
||||
}
|
||||
}
|
||||
p {
|
||||
// display: inline-block;
|
||||
margin: 0 0 0 1.5em;
|
||||
text-indent: -1.5em;
|
||||
display: inline-block;
|
||||
//margin: 0 0 0 1.5em;
|
||||
//text-indent: -1.5em;
|
||||
|
||||
&.street {
|
||||
span.streetnumber {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Generic api method that can be adapted to any fetch request
|
||||
*/
|
||||
const makeFetch = (method, url, body) => {
|
||||
const makeFetch = (method, url, body) => {
|
||||
return fetch(url, {
|
||||
method: method,
|
||||
headers: {
|
||||
@ -11,19 +11,20 @@
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
console.log('200 error')
|
||||
return response.json();
|
||||
}
|
||||
|
||||
if (response.status === 422) {
|
||||
console.log('422 error')
|
||||
return response.json().then(response => {
|
||||
throw ValidationException(response)
|
||||
});
|
||||
}
|
||||
|
||||
if (response.status === 403) {
|
||||
return response.json().then(() => {
|
||||
throw AccessException();
|
||||
});
|
||||
console.log('403 error')
|
||||
throw AccessException(response);
|
||||
}
|
||||
|
||||
throw {
|
||||
@ -88,14 +89,13 @@ const ValidationException = (response) => {
|
||||
error.violations = response.violations.map((violation) => `${violation.title}: ${violation.propertyPath}`);
|
||||
error.titles = response.violations.map((violation) => violation.title);
|
||||
error.propertyPaths = response.violations.map((violation) => violation.propertyPath);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
const AccessException = () => {
|
||||
const AccessException = (response) => {
|
||||
const error = {};
|
||||
error.name = 'AccessException';
|
||||
error.violations = ['You are no longer permitted to perform this action'];
|
||||
error.violations = ['You are not allowed to perform this action'];
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -1,16 +1,39 @@
|
||||
.confidential{
|
||||
.confidential {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
.toggle-far-twig {
|
||||
i {
|
||||
bottom: 0px;
|
||||
right: -30px;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-close-twig {
|
||||
i {
|
||||
bottom: 0px;
|
||||
right: -5px;
|
||||
}
|
||||
}
|
||||
.toggle{
|
||||
margin-left: 30px;
|
||||
margin-top: 5px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 10px;
|
||||
z-index: 5;
|
||||
right: -30px
|
||||
}
|
||||
|
||||
.toggle-far {
|
||||
bottom: 0px;
|
||||
right: 20px !important;
|
||||
}
|
||||
|
||||
.toggle-close {
|
||||
bottom: 125px;
|
||||
right: 15px !important;
|
||||
}
|
||||
|
||||
.blur {
|
||||
-webkit-filter: blur(5px);
|
||||
-moz-filter: blur(5px);
|
||||
|
@ -4,7 +4,7 @@
|
||||
<slot name="confidential-content"></slot>
|
||||
</div>
|
||||
<div>
|
||||
<i class="fa fa-eye toggle" aria-hidden="true" @click="toggleBlur"></i>
|
||||
<i class="fa fa-eye toggle" :class="positionBtn" aria-hidden="true" @click="toggleBlur"></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -12,6 +12,7 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "Confidential",
|
||||
props: ['positionBtnFar'],
|
||||
data() {
|
||||
return {
|
||||
isBlurred: true,
|
||||
@ -19,9 +20,14 @@ export default {
|
||||
},
|
||||
methods : {
|
||||
toggleBlur() {
|
||||
console.log('toggle blur');
|
||||
console.log(this.positionBtnFar);
|
||||
this.isBlurred = !this.isBlurred;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
positionBtn() {
|
||||
return this.positionBtnFar ? 'toggle-far' : 'toggle-close'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<component :is="component" class="address" :class="multiline">
|
||||
|
||||
<div v-if="isConfidential">
|
||||
<confidential>
|
||||
<confidential :positionBtnFar="true">
|
||||
<template v-slot:confidential-content>
|
||||
<div v-if="isMultiline === true">
|
||||
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
|
||||
|
@ -70,7 +70,8 @@ const messages = {
|
||||
},
|
||||
holder: "Titulaire",
|
||||
years_old: "an | {n} an | {n} ans",
|
||||
|
||||
residential_address: "Adresse de résidence",
|
||||
located_at: "réside chez"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -59,7 +59,7 @@
|
||||
must be shown in such list
|
||||
#}
|
||||
{%- if render == 'list' -%}
|
||||
<li class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
|
||||
<li class="chill-entity entity-address {% if address.confidential %} confidential toggle-far-twig {% endif %}">
|
||||
{% if options['with_picto'] %}
|
||||
<i class="fa fa-li fa-map-marker"></i>
|
||||
{% endif %}
|
||||
@ -68,7 +68,7 @@
|
||||
{%- endif -%}
|
||||
|
||||
{%- if render == 'inline' -%}
|
||||
<span class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
|
||||
<span class="chill-entity entity-address {% if address.confidential %} confidential toggle-far-twig {% endif %}">
|
||||
{% if options['with_picto'] %}
|
||||
<i class="fa fa-fw fa-map-marker"></i>
|
||||
{% endif %}
|
||||
@ -77,7 +77,7 @@
|
||||
{%- endif -%}
|
||||
|
||||
{%- if render == 'bloc' -%}
|
||||
<div class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
|
||||
<div class="chill-entity entity-address {% if address.confidential %} confidential toggle-close-twig {% endif %}">
|
||||
{% if options['has_no_address'] == true and address.isNoAddress == true %}
|
||||
{% if address.postCode is not empty %}
|
||||
<div class="address{% if options['multiline'] %} multiline{% endif %}{% if options['with_delimiter'] %} delimiter{% endif %}">
|
||||
|
@ -18,8 +18,10 @@
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.name }}</td>
|
||||
<td>{{ entity.phonenumber1 }}</td>
|
||||
<td>{{ entity.phonenumber2 }}</td>
|
||||
<td>
|
||||
{{ entity.phonenumber1|chill_format_phonenumber }}
|
||||
</td>
|
||||
<td>{{ entity.phonenumber2|chill_format_phonenumber }}</td>
|
||||
<td>{{ entity.email }}</td>
|
||||
<td>
|
||||
{% if entity.address is not null %}
|
||||
|
@ -11,8 +11,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Search\Utils;
|
||||
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use LogicException;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function preg_match;
|
||||
@ -24,6 +26,13 @@ class ExtractPhonenumberFromPattern
|
||||
{
|
||||
private const PATTERN = '([\\+]{0,1}[0-9\\ ]{5,})';
|
||||
|
||||
private string $defaultCarrierCode;
|
||||
|
||||
public function __construct(ParameterBagInterface $parameterBag)
|
||||
{
|
||||
$this->defaultCarrierCode = $parameterBag->get('chill_main')['phone_helper']['default_carrier_code'];
|
||||
}
|
||||
|
||||
public function extractPhonenumber(string $subject): SearchExtractionResult
|
||||
{
|
||||
$matches = [];
|
||||
@ -35,11 +44,21 @@ class ExtractPhonenumberFromPattern
|
||||
|
||||
foreach (str_split(trim($matches[0])) as $key => $char) {
|
||||
switch ($char) {
|
||||
case '+':
|
||||
if (0 === $key) {
|
||||
$phonenumber[] = $char;
|
||||
} else {
|
||||
throw new LogicException('should not match not alnum character');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '0':
|
||||
$length++;
|
||||
|
||||
if (0 === $key) {
|
||||
$phonenumber[] = '+32';
|
||||
$util = PhoneNumberUtil::getInstance();
|
||||
$phonenumber[] = '+' . $util->getCountryCodeForRegion($this->defaultCarrierCode);
|
||||
} else {
|
||||
$phonenumber[] = $char;
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||
|
||||
use libphonenumber\NumberParseException;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
class PhonenumberNormalizer implements NormalizerInterface, DenormalizerInterface
|
||||
{
|
||||
private string $defaultCarrierCode;
|
||||
|
||||
private PhoneNumberUtil $phoneNumberUtil;
|
||||
|
||||
public function __construct(ParameterBagInterface $parameterBag)
|
||||
{
|
||||
$this->defaultCarrierCode = $parameterBag->get('chill_main')['phone_helper']['default_carrier_code'];
|
||||
$this->phoneNumberUtil = PhoneNumberUtil::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @param mixed $type
|
||||
* @param null|mixed $format
|
||||
*
|
||||
* @throws UnexpectedValueException
|
||||
*/
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
{
|
||||
try {
|
||||
return $this->phoneNumberUtil->parse($data, $this->defaultCarrierCode);
|
||||
} catch (NumberParseException $e) {
|
||||
throw new UnexpectedValueException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function normalize($object, ?string $format = null, array $context = []): string
|
||||
{
|
||||
return $this->phoneNumberUtil->formatOutOfCountryCallingNumber($object, $this->defaultCarrierCode);
|
||||
}
|
||||
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
{
|
||||
return 'libphonenumber\PhoneNumber' === $type;
|
||||
}
|
||||
|
||||
public function supportsNormalization($data, ?string $format = null)
|
||||
{
|
||||
return $data instanceof PhoneNumber;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Tests\Routing\Loader;
|
||||
|
||||
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
final class PhonenumberHelperTest extends KernelTestCase
|
||||
{
|
||||
public function formatPhonenumbers()
|
||||
{
|
||||
yield [
|
||||
'BE',
|
||||
'+3281136917',
|
||||
'081 13 69 17',
|
||||
];
|
||||
|
||||
yield [
|
||||
'FR',
|
||||
'+33 6 23 12 45 54',
|
||||
'06 23 12 45 54',
|
||||
];
|
||||
|
||||
yield [
|
||||
'FR',
|
||||
'+32 81 13 69 17',
|
||||
'00 32 81 13 69 17',
|
||||
];
|
||||
|
||||
yield [
|
||||
'BE',
|
||||
'+33 6 23 12 45 54',
|
||||
'00 33 6 23 12 45 54',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider formatPhonenumbers
|
||||
*/
|
||||
public function testFormatPhonenumbers(string $defaultCarrierCode, string $phoneNumber, string $expected)
|
||||
{
|
||||
$util = PhoneNumberUtil::getInstance();
|
||||
$subject = new PhonenumberHelper(
|
||||
new ArrayAdapter(),
|
||||
new ParameterBag([
|
||||
'chill_main.phone_helper' => [
|
||||
'default_carrier_code' => $defaultCarrierCode,
|
||||
],
|
||||
]),
|
||||
new NullLogger()
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $subject->format($util->parse($phoneNumber)));
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ namespace Search\Utils;
|
||||
|
||||
use Chill\MainBundle\Search\Utils\ExtractPhonenumberFromPattern;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -22,17 +23,25 @@ final class ExtractPhonenumberFromPatternTest extends KernelTestCase
|
||||
{
|
||||
public function provideData()
|
||||
{
|
||||
yield ['Diallo', 0, [], 'Diallo', 'no phonenumber'];
|
||||
yield ['BE', 'Diallo', 0, [], 'Diallo', 'no phonenumber'];
|
||||
|
||||
yield ['Diallo 15/06/2021', 0, [], 'Diallo 15/06/2021', 'no phonenumber and a date'];
|
||||
yield ['BE', 'Diallo 15/06/2021', 0, [], 'Diallo 15/06/2021', 'no phonenumber and a date'];
|
||||
|
||||
yield ['Diallo 0486 123 456', 1, ['+32486123456'], 'Diallo', 'a phonenumber and a name'];
|
||||
yield ['BE', 'Diallo 0486 123 456', 1, ['+32486123456'], 'Diallo', 'a phonenumber and a name'];
|
||||
|
||||
yield ['Diallo 123 456', 1, ['123456'], 'Diallo', 'a number and a name, without leadiing 0'];
|
||||
yield ['BE', 'Diallo 123 456', 1, ['123456'], 'Diallo', 'a number and a name, without leadiing 0'];
|
||||
|
||||
yield ['123 456', 1, ['123456'], '', 'only phonenumber'];
|
||||
yield ['BE', '123 456', 1, ['123456'], '', 'only phonenumber'];
|
||||
|
||||
yield ['0123 456', 1, ['+32123456'], '', 'only phonenumber with a leading 0'];
|
||||
yield ['BE', '0123 456', 1, ['+32123456'], '', 'only phonenumber with a leading 0'];
|
||||
|
||||
yield ['FR', '123 456', 1, ['123456'], '', 'only phonenumber'];
|
||||
|
||||
yield ['FR', '0123 456', 1, ['+33123456'], '', 'only phonenumber with a leading 0'];
|
||||
|
||||
yield ['FR', 'Diallo 0486 123 456', 1, ['+33486123456'], 'Diallo', 'a phonenumber and a name'];
|
||||
|
||||
yield ['FR', 'Diallo +32486 123 456', 1, ['+32486123456'], 'Diallo', 'a phonenumber and a name'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,9 +53,11 @@ final class ExtractPhonenumberFromPatternTest extends KernelTestCase
|
||||
* @param mixed $filteredSubject
|
||||
* @param mixed $msg
|
||||
*/
|
||||
public function testExtract($subject, $expectedCount, $expected, $filteredSubject, $msg)
|
||||
public function testExtract(string $defaultCarrierCode, $subject, $expectedCount, $expected, $filteredSubject, $msg)
|
||||
{
|
||||
$extractor = new ExtractPhonenumberFromPattern();
|
||||
$extractor = new ExtractPhonenumberFromPattern(new ParameterBag(['chill_main' => [
|
||||
'phone_helper' => ['default_carrier_code' => $defaultCarrierCode],
|
||||
]]));
|
||||
$result = $extractor->extractPhonenumber($subject);
|
||||
|
||||
$this->assertCount($expectedCount, $result->getFound());
|
||||
|
@ -11,24 +11,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Validation\Validator;
|
||||
|
||||
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
|
||||
use Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface;
|
||||
use LogicException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
class ValidPhonenumber extends ConstraintValidator
|
||||
final class ValidPhonenumber extends ConstraintValidator
|
||||
{
|
||||
protected $logger;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* @var PhonenumberHelper
|
||||
*/
|
||||
protected $phonenumberHelper;
|
||||
private PhoneNumberHelperInterface $phonenumberHelper;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
PhonenumberHelper $phonenumberHelper
|
||||
PhoneNumberHelperInterface $phonenumberHelper
|
||||
) {
|
||||
$this->phonenumberHelper = $phonenumberHelper;
|
||||
$this->logger = $logger;
|
||||
@ -46,7 +43,7 @@ class ValidPhonenumber extends ConstraintValidator
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($value)) {
|
||||
if (null === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,8 @@
|
||||
],
|
||||
"require": {
|
||||
"league/csv": "^9.6",
|
||||
"phpoffice/phpspreadsheet": "~1.2"
|
||||
"phpoffice/phpspreadsheet": "~1.2",
|
||||
"odolbeau/phone-number-bundle": "^3.6"
|
||||
},
|
||||
"require-dev": {
|
||||
},
|
||||
|
@ -96,3 +96,4 @@ services:
|
||||
- "@security.token_storage"
|
||||
|
||||
Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface: '@Chill\MainBundle\Security\Resolver\CenterResolverDispatcher'
|
||||
|
||||
|
@ -3,19 +3,12 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Phonenumber\PhonenumberHelper:
|
||||
arguments:
|
||||
$config: '%chill_main.phone_helper%'
|
||||
$cachePool: '@cache.user_data'
|
||||
Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
|
||||
|
||||
Chill\MainBundle\Phonenumber\Templating:
|
||||
arguments:
|
||||
$phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
Chill\MainBundle\Phonenumber\PhonenumberHelper: ~
|
||||
|
||||
Chill\MainBundle\Phonenumber\Templating: ~
|
||||
|
||||
Chill\MainBundle\Validation\Validator\ValidPhonenumber:
|
||||
arguments:
|
||||
$phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
|
||||
tags:
|
||||
- { name: validator.constraint_validator }
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Rename residential_address table.
|
||||
*/
|
||||
final class Version20220217133607 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE IF EXISTS chill_person_residential_address RENAME TO chill_main_residential_address;');
|
||||
$this->addSql('ALTER SEQUENCE IF EXISTS chill_person_residential_address_id_seq RENAME TO chill_main_residential_address_id_seq;');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Rename residential_address table';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE IF EXISTS chill_main_residential_address RENAME TO chill_person_residential_address;');
|
||||
$this->addSql('ALTER SEQUENCE IF EXISTS chill_main_residential_address_id_seq RENAME TO chill_person_residential_address_id_seq;');
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
|
||||
final class Version20220302132728 extends AbstractMigration implements ContainerAwareInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber1 TYPE VARCHAR(64)');
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber1 DROP DEFAULT');
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber2 TYPE VARCHAR(64)');
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber2 DROP DEFAULT');
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_location.phonenumber1 IS NULL');
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_location.phonenumber2 IS NULL');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Upgrade phonenumber on location';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$carrier_code = $this->container
|
||||
->getParameter('chill_main')['phone_helper']['default_carrier_code'];
|
||||
|
||||
if (null === $carrier_code) {
|
||||
throw new RuntimeException('no carrier code');
|
||||
}
|
||||
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber1 TYPE VARCHAR(35)');
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber1 DROP DEFAULT');
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber1 TYPE VARCHAR(35)');
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber2 TYPE VARCHAR(35)');
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber2 DROP DEFAULT');
|
||||
$this->addSql('ALTER TABLE chill_main_location ALTER phonenumber2 TYPE VARCHAR(35)');
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_location.phonenumber1 IS \'(DC2Type:phone_number)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_location.phonenumber2 IS \'(DC2Type:phone_number)\'');
|
||||
|
||||
$this->addSql(
|
||||
'UPDATE chill_main_location SET ' .
|
||||
$this->buildMigrationPhonenumberClause($carrier_code, 'phonenumber1') .
|
||||
', ' .
|
||||
$this->buildMigrationPhoneNumberClause($carrier_code, 'phonenumber2')
|
||||
);
|
||||
}
|
||||
|
||||
private function buildMigrationPhoneNumberClause(string $defaultCarriercode, string $field): string
|
||||
{
|
||||
$util = PhoneNumberUtil::getInstance();
|
||||
|
||||
$countryCode = $util->getCountryCodeForRegion($defaultCarriercode);
|
||||
|
||||
return sprintf('%s=CASE
|
||||
WHEN %s = \'\' THEN NULL
|
||||
WHEN LEFT(%s, 1) = \'0\'
|
||||
THEN \'+%s\' || replace(replace(substr(%s, 2), \'(0)\', \'\'), \' \', \'\')
|
||||
ELSE replace(replace(%s, \'(0)\', \'\'),\' \', \'\')
|
||||
END', $field, $field, $field, $countryCode, $field, $field);
|
||||
}
|
||||
}
|
@ -322,19 +322,39 @@ final class AccompanyingCourseApiController extends ApiController
|
||||
/**
|
||||
* @Route("/api/1.0/person/accompanying-course/{id}/confidential.json", name="chill_api_person_accompanying_period_confidential")
|
||||
* @ParamConverter("accompanyingCourse", options={"id": "id"})
|
||||
*
|
||||
* @param mixed $id
|
||||
*/
|
||||
public function toggleConfidentialApi(AccompanyingPeriod $accompanyingCourse, Request $request)
|
||||
public function toggleConfidentialApi(AccompanyingPeriod $accompanyingCourse, $id, Request $request)
|
||||
{
|
||||
if ($request->getMethod() === 'POST') {
|
||||
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::TOGGLE_CONFIDENTIAL, $accompanyingCourse);
|
||||
|
||||
$accompanyingCourse->setConfidential(!$accompanyingCourse->isConfidential());
|
||||
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
}
|
||||
|
||||
return $this->json($accompanyingCourse->isConfidential(), Response::HTTP_OK, [], ['groups' => ['read']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/api/1.0/person/accompanying-course/{id}/intensity.json", name="chill_api_person_accompanying_period_intensity")
|
||||
* @ParamConverter("accompanyingCourse", options={"id": "id"})
|
||||
*/
|
||||
public function toggleIntensityApi(AccompanyingPeriod $accompanyingCourse, Request $request)
|
||||
{
|
||||
if ($request->getMethod() === 'POST') {
|
||||
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::TOGGLE_INTENSITY, $accompanyingCourse);
|
||||
|
||||
$status = $accompanyingCourse->getIntensity() === 'regular' ? 'occasional' : 'regular';
|
||||
$accompanyingCourse->setIntensity($status);
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
}
|
||||
|
||||
return $this->json($accompanyingCourse->getIntensity(), Response::HTTP_OK, [], ['groups' => ['read']]);
|
||||
}
|
||||
|
||||
public function workApi($id, Request $request, string $_format): Response
|
||||
{
|
||||
return $this->addRemoveSomething(
|
||||
|
@ -69,7 +69,7 @@ class AccompanyingPeriodWorkEvaluationApiController
|
||||
$evaluations =
|
||||
array_filter(
|
||||
$this->docGeneratorTemplateRepository
|
||||
->findByEntity(AccompanyingPeriodWorkEvaluation::class),
|
||||
->findByEntity(AccompanyingPeriodWorkEvaluation::class, 0, 500),
|
||||
static function (DocGeneratorTemplate $t) use ($evaluation) {
|
||||
$ids = $t->getOptions()['evaluations'] ?? [];
|
||||
|
||||
|
@ -11,10 +11,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\ResidentialAddress;
|
||||
use Chill\MainBundle\Form\Type\ResidentialAddressType;
|
||||
use Chill\MainBundle\Repository\ResidentialAddressRepository;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Person\ResidentialAddress;
|
||||
use Chill\PersonBundle\Form\Type\ResidentialAddressType;
|
||||
use Chill\PersonBundle\Repository\ResidentialAddressRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
|
@ -395,16 +395,16 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
|
||||
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
|
||||
],
|
||||
],
|
||||
'confidential' => [
|
||||
'methods' => [
|
||||
Request::METHOD_POST => true,
|
||||
Request::METHOD_GET => true,
|
||||
],
|
||||
'controller_action' => 'toggleConfidentialApi',
|
||||
'roles' => [
|
||||
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::TOGGLE_CONFIDENTIAL,
|
||||
],
|
||||
],
|
||||
// 'confidential' => [
|
||||
// 'methods' => [
|
||||
// Request::METHOD_POST => true,
|
||||
// Request::METHOD_GET => true,
|
||||
// ],
|
||||
// 'controller_action' => 'toggleConfidentialApi',
|
||||
// 'roles' => [
|
||||
// Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::TOGGLE_CONFIDENTIAL,
|
||||
// ],
|
||||
// ],
|
||||
'findAccompanyingPeriodsByPerson' => [
|
||||
'path' => '/by-person/{person_id}.{_format}',
|
||||
'controller_action' => 'getAccompanyingPeriodsByPerson',
|
||||
|
@ -30,6 +30,8 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\AccompanyingPeriodValidity;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\ConfidentialCourseMustHaveReferrer;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\LocationValidity;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\ParticipationOverlap;
|
||||
use Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod\ResourceDuplicateCheck;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
@ -62,12 +64,9 @@ use const SORT_REGULAR;
|
||||
* "accompanying_period": AccompanyingPeriod::class
|
||||
* })
|
||||
* @Assert\GroupSequenceProvider
|
||||
* @Assert\Expression(
|
||||
* "this.isConfidential and this.getUser === NULL",
|
||||
* message="If the accompanying course is confirmed and confidential, a referrer must remain assigned."
|
||||
* )
|
||||
*
|
||||
* @AccompanyingPeriodValidity(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED})
|
||||
* @LocationValidity(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED})
|
||||
* @ConfidentialCourseMustHaveReferrer(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED})
|
||||
*/
|
||||
class AccompanyingPeriod implements
|
||||
GroupSequenceProviderInterface,
|
||||
@ -201,7 +200,7 @@ class AccompanyingPeriod implements
|
||||
/**
|
||||
* @var string
|
||||
* @ORM\Column(type="string", nullable=true)
|
||||
* @Groups({"read", "write"})
|
||||
* @Groups({"read"})
|
||||
* @Assert\NotBlank(groups={AccompanyingPeriod::STEP_CONFIRMED})
|
||||
*/
|
||||
private $intensity = self::INTENSITY_OCCASIONAL;
|
||||
@ -1000,7 +999,7 @@ class AccompanyingPeriod implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation function.
|
||||
* Validation functions.
|
||||
*/
|
||||
public function isDateConsistent(ExecutionContextInterface $context)
|
||||
{
|
||||
|
@ -37,6 +37,7 @@ use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Exception;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
@ -371,15 +372,10 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
/**
|
||||
* The person's mobile phone number.
|
||||
*
|
||||
* @ORM\Column(type="text")
|
||||
* @Assert\Regex(
|
||||
* pattern="/^([\+{1}])([0-9\s*]{4,20})$/",
|
||||
* )
|
||||
* @PhonenumberConstraint(
|
||||
* type="mobile",
|
||||
* )
|
||||
* @PhonenumberConstraint(type="mobile")
|
||||
* @ORM\Column(type="phone_number", nullable=true)
|
||||
*/
|
||||
private string $mobilenumber = '';
|
||||
private ?PhoneNumber $mobilenumber = null;
|
||||
|
||||
/**
|
||||
* The person's nationality.
|
||||
@ -429,15 +425,12 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
/**
|
||||
* The person's phonenumber.
|
||||
*
|
||||
* @ORM\Column(type="text")
|
||||
* @Assert\Regex(
|
||||
* pattern="/^([\+{1}])([0-9\s*]{4,20})$/",
|
||||
* )
|
||||
* @ORM\Column(type="phone_number", nullable=true)
|
||||
* @PhonenumberConstraint(
|
||||
* type="landline",
|
||||
* )
|
||||
*/
|
||||
private string $phonenumber = '';
|
||||
private ?PhoneNumber $phonenumber = null;
|
||||
|
||||
/**
|
||||
* The person's place of birth.
|
||||
@ -1227,10 +1220,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this->memo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mobilenumber.
|
||||
*/
|
||||
public function getMobilenumber(): string
|
||||
public function getMobilenumber(): ?PhoneNumber
|
||||
{
|
||||
return $this->mobilenumber;
|
||||
}
|
||||
@ -1295,10 +1285,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this->otherPhoneNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get phonenumber.
|
||||
*/
|
||||
public function getPhonenumber(): string
|
||||
public function getPhonenumber(): ?PhoneNumber
|
||||
{
|
||||
return $this->phonenumber;
|
||||
}
|
||||
@ -1737,16 +1724,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mobilenumber.
|
||||
*
|
||||
* @param string $mobilenumber
|
||||
*
|
||||
* @return Person
|
||||
*/
|
||||
public function setMobilenumber(?string $mobilenumber = '')
|
||||
public function setMobilenumber(?PhoneNumber $mobilenumber)
|
||||
{
|
||||
$this->mobilenumber = (string) $mobilenumber;
|
||||
$this->mobilenumber = $mobilenumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -1782,16 +1762,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set phonenumber.
|
||||
*
|
||||
* @param string $phonenumber
|
||||
*
|
||||
* @return Person
|
||||
*/
|
||||
public function setPhonenumber(?string $phonenumber = '')
|
||||
public function setPhonenumber(?PhoneNumber $phonenumber)
|
||||
{
|
||||
$this->phonenumber = (string) $phonenumber;
|
||||
$this->phonenumber = $phonenumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -9,24 +9,28 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
namespace Chill\PersonBundle\Entity\Person;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||
use Chill\MainBundle\Repository\ResidentialAddressRepository;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Repository\ResidentialAddressRepository;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation\Context;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=ResidentialAddressRepository::class)
|
||||
* @ORM\Table(name="chill_main_residential_address")
|
||||
* @ORM\Table(name="chill_person_residential_address")
|
||||
*/
|
||||
class ResidentialAddress
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Address::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private ?Address $address = null;
|
||||
|
||||
@ -37,18 +41,22 @@ class ResidentialAddress
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable", nullable=true)
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Person::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
* @Groups({"read"})
|
||||
* @Context(normalizationContext={"groups": {"minimal"}})
|
||||
*/
|
||||
private ?Person $hostPerson = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private ?ThirdParty $hostThirdParty = null;
|
||||
|
||||
@ -67,6 +75,7 @@ class ResidentialAddress
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable")
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private ?DateTimeImmutable $startDate = null;
|
||||
|
@ -13,17 +13,18 @@ namespace Chill\PersonBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Form\Event\CustomizeFormEvent;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
||||
use Chill\MainBundle\Form\Type\PickCenterType;
|
||||
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Form\Type\GenderType;
|
||||
use Chill\PersonBundle\Form\Type\PersonAltNameType;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use libphonenumber\PhoneNumberType;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TelType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
@ -60,11 +61,13 @@ final class CreationPersonType extends AbstractType
|
||||
->add('birthdate', ChillDateType::class, [
|
||||
'required' => false,
|
||||
])
|
||||
->add('phonenumber', TelType::class, [
|
||||
->add('phonenumber', ChillPhoneNumberType::class, [
|
||||
'required' => false,
|
||||
'type' => PhoneNumberType::FIXED_LINE,
|
||||
])
|
||||
->add('mobilenumber', TelType::class, [
|
||||
->add('mobilenumber', ChillPhoneNumberType::class, [
|
||||
'required' => false,
|
||||
'type' => PhoneNumberType::MOBILE,
|
||||
])
|
||||
->add('email', EmailType::class, [
|
||||
'required' => false,
|
||||
|
@ -14,26 +14,27 @@ namespace Chill\PersonBundle\Form;
|
||||
use Chill\CustomFieldsBundle\Form\Type\CustomFieldType;
|
||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\CommentType;
|
||||
use Chill\MainBundle\Form\Type\PickCivilityType;
|
||||
use Chill\MainBundle\Form\Type\Select2CountryType;
|
||||
use Chill\MainBundle\Form\Type\Select2LanguageType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\PersonPhone;
|
||||
use Chill\PersonBundle\Form\Type\GenderType;
|
||||
use Chill\PersonBundle\Form\Type\PersonAltNameType;
|
||||
use Chill\PersonBundle\Form\Type\PersonPhoneType;
|
||||
use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TelType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@ -57,17 +58,21 @@ class PersonType extends AbstractType
|
||||
|
||||
protected TranslatableStringHelper $translatableStringHelper;
|
||||
|
||||
private ParameterBagInterface $parameterBag;
|
||||
|
||||
/**
|
||||
* @param string[] $personFieldsConfiguration configuration of visibility of some fields
|
||||
*/
|
||||
public function __construct(
|
||||
array $personFieldsConfiguration,
|
||||
ConfigPersonAltNamesHelper $configAltNamesHelper,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
TranslatableStringHelperInterface $translatableStringHelper,
|
||||
ParameterBagInterface $parameterBag
|
||||
) {
|
||||
$this->config = $personFieldsConfiguration;
|
||||
$this->configAltNamesHelper = $configAltNamesHelper;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
$this->parameterBag = $parameterBag;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
@ -126,22 +131,34 @@ class PersonType extends AbstractType
|
||||
}
|
||||
|
||||
if ('visible' === $this->config['phonenumber']) {
|
||||
$builder->add('phonenumber', TelType::class, [
|
||||
$builder
|
||||
->add(
|
||||
'phonenumber',
|
||||
ChillPhoneNumberType::class,
|
||||
[
|
||||
'required' => false,
|
||||
// 'placeholder' => '+33623124554' //TODO placeholder for phone numbers
|
||||
]);
|
||||
'type' => \libphonenumber\PhoneNumberType::FIXED_LINE,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ('visible' === $this->config['mobilenumber']) {
|
||||
$builder
|
||||
->add('mobilenumber', TelType::class, ['required' => false])
|
||||
->add(
|
||||
'mobilenumber',
|
||||
ChillPhoneNumberType::class,
|
||||
[
|
||||
'type' => \libphonenumber\PhoneNumberType::MOBILE,
|
||||
'required' => false,
|
||||
]
|
||||
)
|
||||
->add('acceptSMS', CheckboxType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
$builder->add('otherPhoneNumbers', ChillCollectionType::class, [
|
||||
'entry_type' => PersonPhoneType::class,
|
||||
'entry_type' => ChillPhoneNumberType::class,
|
||||
'button_add_label' => 'Add new phone',
|
||||
'button_remove_label' => 'Remove phone',
|
||||
'required' => false,
|
||||
|
@ -9,10 +9,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Form\Type;
|
||||
namespace Chill\PersonBundle\Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\ResidentialAddress;
|
||||
use Chill\PersonBundle\Form\Type\PickPersonDynamicType;
|
||||
use Chill\MainBundle\Form\Type\CommentType;
|
||||
use Chill\MainBundle\Form\Type\PickAddressType;
|
||||
use Chill\PersonBundle\Entity\Person\ResidentialAddress;
|
||||
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
@ -9,10 +9,14 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\ResidentialAddress;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Person\ResidentialAddress;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
@ -28,6 +32,39 @@ class ResidentialAddressRepository extends ServiceEntityRepository
|
||||
parent::__construct($registry, ResidentialAddress::class);
|
||||
}
|
||||
|
||||
public function buildQueryFindCurrentResidentialAddresses(Person $person, ?DateTimeImmutable $at = null): QueryBuilder
|
||||
{
|
||||
$date = null === $at ? new DateTimeImmutable('today') : $at;
|
||||
$qb = $this->createQueryBuilder('ra');
|
||||
|
||||
$dateFilter = $qb->expr()->andX(
|
||||
$qb->expr()->lte('ra.startDate', ':dateIn'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('ra.endDate'),
|
||||
$qb->expr()->gte('ra.endDate', ':dateIn')
|
||||
)
|
||||
);
|
||||
|
||||
$qb
|
||||
->where($dateFilter)
|
||||
->setParameter('dateIn', $date, Types::DATE_IMMUTABLE)
|
||||
->andWhere('ra.person = :person')
|
||||
->setParameter('person', $person);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|ResidentialAddress[]|null
|
||||
*/
|
||||
public function findCurrentResidentialAddressByPerson(Person $person, ?DateTimeImmutable $at = null): array
|
||||
{
|
||||
return $this->buildQueryFindCurrentResidentialAddresses($person, $at)
|
||||
->select('ra')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return ResidentialAddress[] Returns an array of ResidentialAddress objects
|
||||
// */
|
@ -58,7 +58,7 @@ export default {
|
||||
this.$store.dispatch('toggleIntensity', value)
|
||||
.catch(({name, violations}) => {
|
||||
if (name === 'ValidationException' || name === 'AccessException') {
|
||||
violations.forEach((violation) => this.$toast.open({message: violation}));
|
||||
this.$toast.open({message: this.$t('Only the referrer can toggle the intensity of an accompanying course')})
|
||||
} else {
|
||||
this.$toast.open({message: 'An error occurred'})
|
||||
}
|
||||
@ -75,16 +75,11 @@ export default {
|
||||
});
|
||||
},
|
||||
toggleConfidential() {
|
||||
this.$store.dispatch('fetchPermissions').then(() => {
|
||||
if (!this.$store.getters.canTogglePermission) {
|
||||
this.$toast.open({message: "Seul le référent peut modifier la confidentialité"});
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return this.$store.dispatch('toggleConfidential', (!this.isConfidential));
|
||||
}
|
||||
}).catch(({name, violations}) => {
|
||||
this.$store.dispatch('toggleConfidential')
|
||||
.catch(({name, violations}) => {
|
||||
console.log(name);
|
||||
if (name === 'ValidationException' || name === 'AccessException') {
|
||||
violations.forEach((violation) => this.$toast.open({message: violation}));
|
||||
this.$toast.open({message: this.$t('Only the referrer can toggle the confidentiality of an accompanying course')})
|
||||
} else {
|
||||
this.$toast.open({message: 'An error occurred'})
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
<input type="checkbox" v-model="requestorIsAnonymous" class="me-2" />
|
||||
{{ $t('requestor.is_anonymous') }}
|
||||
</label>
|
||||
<confidential v-if="accompanyingCourse.requestor.type === 'thirdparty'">
|
||||
<confidential :positionBtn="false" v-if="accompanyingCourse.requestor.type === 'thirdparty'">
|
||||
<template v-slot:confidential-content>
|
||||
<third-party-render-box
|
||||
:thirdparty="accompanyingCourse.requestor"
|
||||
@ -33,7 +33,7 @@
|
||||
</template>
|
||||
</confidential>
|
||||
|
||||
<confidential v-else-if="accompanyingCourse.requestor.type === 'person'">
|
||||
<confidential :positionBtnFar="false" v-else-if="accompanyingCourse.requestor.type === 'person'">
|
||||
<template v-slot:confidential-content>
|
||||
<person-render-box render="bloc"
|
||||
:person="accompanyingCourse.requestor"
|
||||
|
@ -168,7 +168,7 @@ export default {
|
||||
}
|
||||
else if (payload.type === 'thirdparty') {
|
||||
console.log('data', payload.data)
|
||||
body.name = payload.data.text;
|
||||
body.name = payload.data.name;
|
||||
body.email = payload.data.email;
|
||||
body.telephone = payload.data.phonenumber;
|
||||
body.address = payload.data.address ? { id: payload.data.address.address_id } : null;
|
||||
|
@ -46,6 +46,12 @@ if (root === 'banner') {
|
||||
})
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.use(VueToast, {
|
||||
position: "bottom-right",
|
||||
type: "error",
|
||||
duration: 5000,
|
||||
dismissible: true
|
||||
})
|
||||
.component('banner', Banner)
|
||||
.mount('#banner-accompanying-course');
|
||||
});
|
||||
|
@ -167,6 +167,8 @@ const appMessages = {
|
||||
'Error while retriving users.': "Erreur du serveur lors du chargement de la liste des travailleurs.",
|
||||
'Error while getting whoami.': "Erreur du serveur lors de la requête 'qui suis-je ?'",
|
||||
'Error while retriving origin\'s list.': "Erreur du serveur lors du chargement de la liste des origines de la demande.",
|
||||
'Only the referrer can toggle the intensity of an accompanying course': "Seul le référent peut modifier l'intensité d'un parcours.",
|
||||
'Only the referrer can toggle the confidentiality of an accompanying course': "Seul le référent peut modifier la confidentialité d'un parcours."
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -432,12 +432,12 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
||||
* Update accompanying course intensity/emergency/confidentiality
|
||||
*/
|
||||
toggleIntensity({ commit }, payload) {
|
||||
const url = `/api/1.0/person/accompanying-course/${id}.json`
|
||||
const url = `/api/1.0/person/accompanying-course/${id}/intensity.json`
|
||||
const body = { type: "accompanying_period", 'intensity': payload }
|
||||
|
||||
return makeFetch('PATCH', url, body)
|
||||
return makeFetch('POST', url, body)
|
||||
.then((response) => {
|
||||
commit('toggleIntensity', response.intensity);
|
||||
commit('toggleIntensity', response);
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -459,14 +459,18 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
|
||||
})
|
||||
},
|
||||
toggleConfidential({ commit }, payload) {
|
||||
const url = `/api/1.0/person/accompanying-course/${id}.json`
|
||||
const url = `/api/1.0/person/accompanying-course/${id}/confidential.json`
|
||||
const body = { type: "accompanying_period", confidential: payload }
|
||||
|
||||
return makeFetch('PATCH', url, body)
|
||||
console.log('url', url, 'body', body);
|
||||
|
||||
return makeFetch('POST', url, body)
|
||||
.then((response) => {
|
||||
commit('toggleConfidential', response.confidential);
|
||||
console.log('response', response);
|
||||
commit('toggleConfidential', response);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('error', error)
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
})
|
||||
|
@ -113,15 +113,6 @@
|
||||
<p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
|
||||
</li>
|
||||
|
||||
<li v-if="person.email">
|
||||
<i class="fa fa-li fa-envelope-o"></i>
|
||||
<a :href="'mailto: ' + person.email">{{ person.email }}</a>
|
||||
</li>
|
||||
<li v-else-if="options.addNoData">
|
||||
<i class="fa fa-li fa-envelope-o"></i>
|
||||
<p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
|
||||
</li>
|
||||
|
||||
<li v-if="person.centers !== undefined && person.centers.length > 0 && options.addCenter">
|
||||
<i class="fa fa-li fa-long-arrow-right"></i>
|
||||
<template v-for="c in person.centers">{{ c.name }}</template>
|
||||
@ -137,6 +128,52 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item-col mx-3" v-if="this.showResidentialAddresses && (person.current_residential_addresses || []).length > 0">
|
||||
<div class="float-button bottom">
|
||||
<div class="box" >
|
||||
<ul class="list-content fa-ul">
|
||||
<li v-for="(addr, i) in person.current_residential_addresses" :key="i">
|
||||
<i class="fa fa-li fa-map-marker"></i>
|
||||
<div v-if="addr.address">
|
||||
<address-render-box
|
||||
:address="addr.address"
|
||||
:isMultiline="isMultiline">
|
||||
</address-render-box>
|
||||
<p>({{ $t('renderbox.residential_address') }})</p>
|
||||
</div>
|
||||
<div v-else-if="addr.hostPerson" class="mt-3">
|
||||
<p>{{ $t('renderbox.located_at') }}:</p>
|
||||
<span class="chill-entity entity-person badge-person">
|
||||
<person-text
|
||||
v-if="addr.hostPerson"
|
||||
:person="addr.hostPerson"
|
||||
></person-text>
|
||||
</span>
|
||||
<address-render-box v-if="addr.hostPerson.address"
|
||||
:address="addr.hostPerson.address"
|
||||
:isMultiline="isMultiline">
|
||||
</address-render-box>
|
||||
</div>
|
||||
<div v-else-if="addr.hostThirdParty" class="mt-3">
|
||||
<p>{{ $t('renderbox.located_at') }}:</p>
|
||||
<span class="chill-entity entity-person badge-thirdparty">
|
||||
<third-party-text
|
||||
v-if="addr.hostThirdParty"
|
||||
:thirdparty="addr.hostThirdParty"
|
||||
></third-party-text>
|
||||
</span>
|
||||
<address-render-box v-if="addr.hostThirdParty.address"
|
||||
:address="addr.hostThirdParty.address"
|
||||
:isMultiline="isMultiline">
|
||||
</address-render-box>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@ -168,6 +205,7 @@ import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRe
|
||||
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
|
||||
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
|
||||
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
|
||||
import ThirdPartyText from 'ChillThirdPartyAssets/vuejs/_components/Entity/ThirdPartyText.vue';
|
||||
|
||||
export default {
|
||||
name: "PersonRenderBox",
|
||||
@ -175,9 +213,28 @@ export default {
|
||||
AddressRenderBox,
|
||||
Confidential,
|
||||
BadgeEntity,
|
||||
PersonText
|
||||
PersonText,
|
||||
ThirdPartyText
|
||||
},
|
||||
props: {
|
||||
person: {
|
||||
required: true,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
render: {
|
||||
type: String,
|
||||
},
|
||||
returnPath: {
|
||||
type: String,
|
||||
},
|
||||
showResidentialAddresses: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
props: ['person', 'options', 'render', 'returnPath'],
|
||||
computed: {
|
||||
isMultiline: function() {
|
||||
if(this.options.isMultiline){
|
||||
|
@ -15,6 +15,7 @@
|
||||
addNoData: true,
|
||||
isMultiline: true
|
||||
}"
|
||||
:show-residential-addresses="true"
|
||||
></person-render-box>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -151,14 +151,14 @@
|
||||
{% if accompanyingCourse.requestorPerson is not null %}
|
||||
<h4 class="item-key">{{ 'Requestor'|trans }}</h4>
|
||||
{% if accompanyingCourse.requestorAnonymous %}
|
||||
<div class="confidential"><p>{{ _self.insert_onthefly('person', accompanyingCourse.requestorPerson) }}</p></div>
|
||||
<div class="confidential toggle-close-twig"><p>{{ _self.insert_onthefly('person', accompanyingCourse.requestorPerson) }}</p></div>
|
||||
{% else %}
|
||||
{{ _self.insert_onthefly('person', accompanyingCourse.requestorPerson) }}
|
||||
{% endif %}
|
||||
{% elseif accompanyingCourse.requestorThirdParty is not null %}
|
||||
<h4 class="item-key">{{ 'Requestor'|trans }}</h4>
|
||||
{% if accompanyingCourse.requestorAnonymous %}
|
||||
<div class="confidential"><p>{{ _self.insert_onthefly('thirdparty', accompanyingCourse.requestorThirdParty) }}</p></div>
|
||||
<div class="confidential toggle-close-twig"><p>{{ _self.insert_onthefly('thirdparty', accompanyingCourse.requestorThirdParty) }}</p></div>
|
||||
{% else %}
|
||||
{{ _self.insert_onthefly('thirdparty', accompanyingCourse.requestorThirdParty) }}
|
||||
{% endif %}
|
||||
|
@ -107,8 +107,7 @@
|
||||
</div>
|
||||
|
||||
{% if displayAction is defined and displayAction == true %}
|
||||
<div class="item-col">
|
||||
<ul class="record_actions">
|
||||
<ul class="item-col record_actions">
|
||||
{% set suppEvaluations = [] %}
|
||||
{% for e in w.accompanyingPeriodWorkEvaluations %}
|
||||
{% set suppEvaluations = suppEvaluations|merge([
|
||||
@ -137,7 +136,6 @@
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -146,22 +146,27 @@
|
||||
'with_valid_from': false
|
||||
}) }}
|
||||
{% endif %}
|
||||
{% if person.phonenumber is not null %}
|
||||
<li>
|
||||
{% if person.mobilenumber %}
|
||||
<i class="fa fa-li fa-mobile"></i><a href="{{ 'tel:' ~ person.mobilenumber }}">
|
||||
{{ person.mobilenumber|chill_format_phonenumber }}
|
||||
</a>
|
||||
{% else %}
|
||||
<i class="fa fa-li fa-phone"></i>
|
||||
{% if person.phonenumber %}
|
||||
<a href="{{ 'tel:' ~ person.phonenumber }}">
|
||||
<a href="{{ 'tel:' ~ person.phonenumber|phone_number_format('E164') }}">
|
||||
{{ person.phonenumber|chill_format_phonenumber }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'No data given'|trans }}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if person.mobilenumber is not null %}
|
||||
<li>
|
||||
<i class="fa fa-li fa-mobile"></i><a href="{{ 'tel:' ~ person.mobilenumber|phone_number_format('E164') }}">
|
||||
{{ person.mobilenumber|chill_format_phonenumber }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if person.phonenumber is null and person.mobilenumber is null %}
|
||||
<li>
|
||||
<i class="fa fa-li fa-phone"></i>
|
||||
<span class="chill-no-data-statement">{{ 'No data given'|trans }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if options['addCenter'] and person|chill_resolve_center|length > 0 %}
|
||||
<li>
|
||||
<i class="fa fa-li fa-long-arrow-right"></i>
|
||||
|
@ -25,14 +25,14 @@
|
||||
{% if person.phonenumber %}
|
||||
<span class="phonenumber d-block d-sm-inline-block">
|
||||
<i class="fa fa-fw fa-phone"></i>
|
||||
<a href="{{ 'tel:' ~ person.phonenumber }}" class="phone mr-3" title="{{ 'Phonenumber'|trans }}">
|
||||
<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">
|
||||
<i class="fa fa-fw fa-mobile"></i>
|
||||
<a href="{{ 'tel:' ~ person.mobilenumber }}" class="phone mr-3" title="{{ 'Mobilenumber'|trans }}">
|
||||
<a href="{{ 'tel:' ~ person.mobilenumber|phone_number_format('E164') }}" class="phone mr-3" title="{{ 'Mobilenumber'|trans }}">
|
||||
{{ person.mobilenumber|chill_format_phonenumber }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
@ -62,12 +62,12 @@
|
||||
<ul>
|
||||
{% if person.phonenumber is not empty %}
|
||||
<li>
|
||||
<a href="tel:{{ person.phonenumber }}"><img src="{{ asset('build/images/mobile-alt-solid.svg') }}"> <pre>{{ person.phonenumber|chill_format_phonenumber }}</pre></a>
|
||||
<a href="tel:{{ person.phonenumber|phone_number_format('E164') }}"><img src="{{ asset('build/images/mobile-alt-solid.svg') }}"> <pre>{{ person.phonenumber|chill_format_phonenumber }}</pre></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if person.mobilenumber is not empty%}
|
||||
<li>
|
||||
<a href="tel:{{ person.mobilenumber }}"><img src="{{ asset('build/images/phone-alt-solid.svg') }}"> <pre>{{ person.mobilenumber|chill_format_phonenumber }}</pre></a>
|
||||
<a href="tel:{{ person.mobilenumber|phone_number_format('E164') }}"><img src="{{ asset('build/images/phone-alt-solid.svg') }}"> <pre>{{ person.mobilenumber|chill_format_phonenumber }}</pre></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
@ -232,14 +232,14 @@ This view should receive those arguments:
|
||||
{%- if chill_person.fields.phonenumber == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{ 'Phonenumber'|trans }} :</dt>
|
||||
<dd>{% if person.phonenumber is not empty %}<a href="tel:{{ person.phonenumber }}"><pre>{{ person.phonenumber|chill_format_phonenumber }}</pre></a>{% else %}<span class="chill-no-data-statement">{{ 'No data given'|trans }}{% endif %}</dd>
|
||||
<dd>{% if person.phonenumber is not empty %}<a href="tel:{{ person.phonenumber|phone_number_format('E164') }}">{{ person.phonenumber|chill_format_phonenumber }}</a>{% else %}<span class="chill-no-data-statement">{{ 'No data given'|trans }}{% endif %}</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
|
||||
{%- if chill_person.fields.mobilenumber == 'visible' -%}
|
||||
<dl>
|
||||
<dt>{{ 'Mobilenumber'|trans }} :</dt>
|
||||
<dd>{% if person.mobilenumber is not empty %}<a href="tel:{{ person.mobilenumber }}">{{ person.mobilenumber|chill_format_phonenumber }}</a>{% else %}<span class="chill-no-data-statement">{{ 'No data given'|trans }}{% endif %}</dd>
|
||||
<dd>{% if person.mobilenumber is not empty %}<a href="tel:{{ person.mobilenumber|phone_number_format('E164') }}">{{ person.mobilenumber|chill_format_phonenumber }}</a>{% else %}<span class="chill-no-data-statement">{{ 'No data given'|trans }}{% endif %}</dd>
|
||||
<p>{% if person.acceptSMS %}{{ 'Accept short text message'|trans }}{% endif %}</p>
|
||||
</dl>
|
||||
{% endif %}
|
||||
@ -250,7 +250,7 @@ This view should receive those arguments:
|
||||
<dt>{{ 'Others phone numbers'|trans }} :</dt>
|
||||
{% for el in person.otherPhoneNumbers %}
|
||||
{% if el.phonenumber is not empty %}
|
||||
<dd>{% if el.description is not empty %}{{ el.description }} : {% endif %}<a href="tel:{{ el.phonenumber }}">{{ el.phonenumber|chill_format_phonenumber }}</a></dd>
|
||||
<dd>{% if el.description is not empty %}{{ el.description }} : {% endif %}<a href="tel:{{ el.phonenumber|phone_number_format('E164') }}">{{ el.phonenumber|chill_format_phonenumber }}</a></dd>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
@ -62,7 +62,7 @@
|
||||
<li>
|
||||
<i class="fa fa-li fa-home"></i>
|
||||
<span class="item-key">{{ "Address of"|trans}}</span>
|
||||
<span class="chill-entity entity-person badge-person">{{ a.hostThirdParty|chill_entity_render_box }}</span>
|
||||
<span class="chill-entity entity-person badge-thirdparty">{{ a.hostThirdParty|chill_entity_render_box }}</span>
|
||||
</li>
|
||||
<li>
|
||||
{% if a.hostThirdParty.address is not null %}
|
||||
|
@ -33,6 +33,7 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH
|
||||
self::DELETE,
|
||||
self::FULL,
|
||||
self::TOGGLE_CONFIDENTIAL_ALL,
|
||||
self::TOGGLE_INTENSITY,
|
||||
];
|
||||
|
||||
public const CREATE = 'CHILL_PERSON_ACCOMPANYING_PERIOD_CREATE';
|
||||
@ -62,6 +63,11 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH
|
||||
*/
|
||||
public const TOGGLE_CONFIDENTIAL_ALL = 'CHILL_PERSON_ACCOMPANYING_PERIOD_TOGGLE_CONFIDENTIAL_ALL';
|
||||
|
||||
/**
|
||||
* Right to toggle urgency of parcours.
|
||||
*/
|
||||
public const TOGGLE_INTENSITY = 'CHILL_PERSON_ACCOMPANYING_PERIOD_TOGGLE_INTENSITY';
|
||||
|
||||
private Security $security;
|
||||
|
||||
private VoterHelperInterface $voterHelper;
|
||||
@ -125,11 +131,20 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH
|
||||
}
|
||||
|
||||
if (self::TOGGLE_CONFIDENTIAL === $attribute) {
|
||||
if ($subject->getUser() === $token->getUser()) {
|
||||
if (null !== $subject->getUser() && ($subject->getUser() === $token->getUser())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->voterHelper->voteOnAttribute(self::TOGGLE_CONFIDENTIAL_ALL, $subject, $token);
|
||||
return false;
|
||||
// return $this->voterHelper->voteOnAttribute(self::TOGGLE_CONFIDENTIAL_ALL, $subject, $token);
|
||||
}
|
||||
|
||||
if (self::TOGGLE_INTENSITY === $attribute) {
|
||||
if (null !== $subject->getUser() && ($subject->getUser() === $token->getUser())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// if confidential, only the referent can see it
|
||||
|
@ -95,9 +95,9 @@ class PersonDocGenNormalizer implements
|
||||
'maritalStatus' => null !== ($ms = $person->getMaritalStatus()) ? $this->translatableStringHelper->localize($ms->getName()) : '',
|
||||
'maritalStatusDate' => $this->normalizer->normalize($person->getMaritalStatusDate(), $format, $dateContext),
|
||||
'email' => $person->getEmail(),
|
||||
'firstPhoneNumber' => $person->getPhonenumber() ?? $person->getMobilenumber(),
|
||||
'fixPhoneNumber' => $person->getPhonenumber(),
|
||||
'mobilePhoneNumber' => $person->getMobilenumber(),
|
||||
'firstPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber() ?? $person->getMobilenumber(), $format, $context),
|
||||
'fixPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber(), $format, $context),
|
||||
'mobilePhoneNumber' => $this->normalizer->normalize($person->getMobilenumber(), $format, $context),
|
||||
'nationality' => null !== ($c = $person->getNationality()) ? $this->translatableStringHelper->localize($c->getName()) : '',
|
||||
'placeOfBirth' => $person->getPlaceOfBirth(),
|
||||
'memo' => $person->getMemo(),
|
||||
|
@ -12,21 +12,27 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Serializer\Normalizer;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\PersonAltName;
|
||||
use Chill\PersonBundle\Repository\PersonRepository;
|
||||
use Chill\PersonBundle\Repository\ResidentialAddressRepository;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* Serialize a Person entity.
|
||||
@ -41,18 +47,26 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
|
||||
|
||||
private CenterResolverManagerInterface $centerResolverManager;
|
||||
|
||||
private PhoneNumberHelperInterface $phoneNumberHelper;
|
||||
|
||||
private ChillEntityRenderExtension $render;
|
||||
|
||||
private PersonRepository $repository;
|
||||
|
||||
private ResidentialAddressRepository $residentialAddressRepository;
|
||||
|
||||
public function __construct(
|
||||
ChillEntityRenderExtension $render,
|
||||
PersonRepository $repository,
|
||||
CenterResolverManagerInterface $centerResolverManager
|
||||
CenterResolverManagerInterface $centerResolverManager,
|
||||
ResidentialAddressRepository $residentialAddressRepository,
|
||||
PhoneNumberHelperInterface $phoneNumberHelper
|
||||
) {
|
||||
$this->render = $render;
|
||||
$this->repository = $repository;
|
||||
$this->centerResolverManager = $centerResolverManager;
|
||||
$this->residentialAddressRepository = $residentialAddressRepository;
|
||||
$this->phoneNumberHelper = $phoneNumberHelper;
|
||||
}
|
||||
|
||||
public function denormalize($data, $type, $format = null, array $context = [])
|
||||
@ -106,12 +120,12 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
|
||||
break;
|
||||
|
||||
case 'phonenumber':
|
||||
$person->setPhonenumber($data[$item]);
|
||||
$person->setPhonenumber($this->denormalizer->denormalize($data[$item], PhoneNumber::class, $format, $context));
|
||||
|
||||
break;
|
||||
|
||||
case 'mobilenumber':
|
||||
$person->setMobilenumber($data[$item]);
|
||||
$person->setMobilenumber($this->denormalizer->denormalize($data[$item], PhoneNumber::class, $format, $context));
|
||||
|
||||
break;
|
||||
|
||||
@ -174,27 +188,42 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
|
||||
*/
|
||||
public function normalize($person, $format = null, array $context = [])
|
||||
{
|
||||
$groups = $context[AbstractNormalizer::GROUPS] ?? [];
|
||||
if (is_string($groups)) {
|
||||
$groups = [$groups];
|
||||
}
|
||||
$household = $person->getCurrentHousehold();
|
||||
$currentResidentialAddresses = $this->residentialAddressRepository->findCurrentResidentialAddressByPerson($person);
|
||||
|
||||
return [
|
||||
$data = [
|
||||
'type' => 'person',
|
||||
'id' => $person->getId(),
|
||||
'text' => $this->render->renderString($person, ['addAge' => false]),
|
||||
'textAge' => $this->render->renderString($person, ['addAge' => true]),
|
||||
'firstName' => $person->getFirstName(),
|
||||
'lastName' => $person->getLastName(),
|
||||
'current_household_address' => $this->normalizer->normalize($person->getCurrentHouseholdAddress(), $format, $context),
|
||||
'birthdate' => $this->normalizer->normalize($person->getBirthdate(), $format, $context),
|
||||
'deathdate' => $this->normalizer->normalize($person->getDeathdate(), $format, $context),
|
||||
'age' => $this->normalizer->normalize($person->getAge(), $format, $context),
|
||||
'centers' => $this->normalizer->normalize($this->centerResolverManager->resolveCenters($person), $format, $context),
|
||||
'phonenumber' => $person->getPhonenumber(),
|
||||
'mobilenumber' => $person->getMobilenumber(),
|
||||
'phonenumber' => $this->normalizer->normalize($person->getPhonenumber()),
|
||||
'mobilenumber' => $this->normalizer->normalize($person->getMobilenumber()),
|
||||
'email' => $person->getEmail(),
|
||||
'altNames' => $this->normalizeAltNames($person->getAltNames()),
|
||||
'gender' => $person->getGender(),
|
||||
'current_household_address' => $this->normalizer->normalize($person->getCurrentHouseholdAddress(), $format, $context),
|
||||
'current_household_id' => $household ? $this->normalizer->normalize($household->getId(), $format, $context) : null,
|
||||
];
|
||||
|
||||
if (in_array('minimal', $groups, true) && 1 === count($groups)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
return array_merge($data, [
|
||||
'centers' => $this->normalizer->normalize($this->centerResolverManager->resolveCenters($person), $format, $context),
|
||||
'altNames' => $this->normalizeAltNames($person->getAltNames()),
|
||||
'current_household_id' => $household ? $this->normalizer->normalize($household->getId(), $format, $context) : null,
|
||||
'current_residential_addresses' => $currentResidentialAddresses ?
|
||||
$this->normalizer->normalize($currentResidentialAddresses, $format, $context) :
|
||||
null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function supportsDenormalization($data, $type, $format = null)
|
||||
|
@ -13,9 +13,7 @@ namespace Chill\PersonBundle\Tests\AccompanyingPeriod;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -42,7 +40,7 @@ final class AccompanyingPeriodConfidentialTest extends WebTestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function dataGenerateRandomAccompanyingCourse()
|
||||
public function testConfidentialInvalid()
|
||||
{
|
||||
// Disabling this dataprovider to avoid having errors while running the test.
|
||||
return yield from [];
|
||||
@ -88,10 +86,7 @@ final class AccompanyingPeriodConfidentialTest extends WebTestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataGenerateRandomAccompanyingCourse
|
||||
*/
|
||||
public function testRemoveUserWhenConfidential(int $periodId)
|
||||
public function testConfidentialValid()
|
||||
{
|
||||
$this->markTestIncomplete(
|
||||
'Marked as incomplete because of a problem in the dataprovider, at line 81.'
|
||||
@ -101,8 +96,7 @@ final class AccompanyingPeriodConfidentialTest extends WebTestCase
|
||||
->find($periodId);
|
||||
$em = self::$kernel->getContainer()->get('doctrine.orm.entity_manager');
|
||||
|
||||
$isConfidential = $period->isConfidential();
|
||||
$step = $period->getStep();
|
||||
$violations = self::$validator->validate($period, null, ['confirmed']);
|
||||
|
||||
$initialUser = $period->getUser();
|
||||
|
||||
|
@ -297,13 +297,14 @@ final class PersonControllerUpdateTest extends WebTestCase
|
||||
// reminder: this value is capitalized
|
||||
['placeOfBirth', 'A PLACE', static function (Person $person) { return $person->getPlaceOfBirth(); }],
|
||||
['birthdate', '1980-12-15', static function (Person $person) { return $person->getBirthdate()->format('Y-m-d'); }],
|
||||
['phonenumber', '+32123456789', static function (Person $person) { return $person->getPhonenumber(); }],
|
||||
// TODO test on phonenumber update
|
||||
// ['phonenumber', '+32123456789', static function (Person $person) { return $person->getPhonenumber(); }],
|
||||
['memo', 'jfkdlmq jkfldmsq jkmfdsq', static function (Person $person) { return $person->getMemo(); }],
|
||||
['countryOfBirth', 'BE', static function (Person $person) { return $person->getCountryOfBirth()->getCountryCode(); }],
|
||||
['nationality', 'FR', static function (Person $person) { return $person->getNationality()->getCountryCode(); }],
|
||||
['placeOfBirth', '', static function (Person $person) { return $person->getPlaceOfBirth(); }],
|
||||
['birthdate', '', static function (Person $person) { return $person->getBirthdate(); }],
|
||||
['phonenumber', '', static function (Person $person) { return $person->getPhonenumber(); }],
|
||||
//['phonenumber', '', static function (Person $person) { return $person->getPhonenumber(); }],
|
||||
['memo', '', static function (Person $person) { return $person->getMemo(); }],
|
||||
['countryOfBirth', null, static function (Person $person) { return $person->getCountryOfBirth(); }],
|
||||
['nationality', null, static function (Person $person) { return $person->getNationality(); }],
|
||||
|
@ -158,7 +158,7 @@ final class AccompanyingPeriodTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
$participationL = $period->closeParticipationFor($person);
|
||||
$this->assertSame($participationL, $participation);
|
||||
$this->assertTrue($participation->getEndDate() instanceof DateTimeInterface);
|
||||
$this->assertTrue($participationL->getEndDate() instanceof DateTimeInterface);
|
||||
|
||||
$participation = $period->getOpenParticipationContainsPerson($person);
|
||||
$this->assertNull($participation);
|
||||
|
@ -15,6 +15,7 @@ use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
|
||||
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
@ -28,10 +29,13 @@ class AccompanyingPeriodValidityValidator extends ConstraintValidator
|
||||
|
||||
private SocialIssueRender $socialIssueRender;
|
||||
|
||||
public function __construct(ActivityRepository $activityRepository, SocialIssueRender $socialIssueRender)
|
||||
private TokenStorageInterface $token;
|
||||
|
||||
public function __construct(ActivityRepository $activityRepository, SocialIssueRender $socialIssueRender, TokenStorageInterface $token)
|
||||
{
|
||||
$this->activityRepository = $activityRepository;
|
||||
$this->socialIssueRender = $socialIssueRender;
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function validate($period, Constraint $constraint)
|
||||
@ -44,6 +48,7 @@ class AccompanyingPeriodValidityValidator extends ConstraintValidator
|
||||
throw new UnexpectedValueException($period, AccompanyingPeriod::class);
|
||||
}
|
||||
|
||||
/** Check if a social issue can be deleted (is not linked to an action or activity within the parcours) */
|
||||
$socialIssues = [];
|
||||
|
||||
$activities = $this->activityRepository->findBy(['accompanyingPeriod' => $period]);
|
||||
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class ConfidentialCourseMustHaveReferrer extends Constraint
|
||||
{
|
||||
public string $message = 'A confidential parcours must have a referrer';
|
||||
|
||||
public function getTargets()
|
||||
{
|
||||
return [self::CLASS_CONSTRAINT];
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Validator\Constraints\AccompanyingPeriod;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class ConfidentialCourseMustHaveReferrerValidator extends ConstraintValidator
|
||||
{
|
||||
public function validate($value, Constraint $constraint)
|
||||
{
|
||||
if (!$value instanceof AccompanyingPeriod) {
|
||||
throw new UnexpectedTypeException($value, AccompanyingPeriod::class);
|
||||
}
|
||||
|
||||
if (!$constraint instanceof ConfidentialCourseMustHaveReferrer) {
|
||||
throw new UnexpectedTypeException($constraint, ConfidentialCourseMustHaveReferrer::class);
|
||||
}
|
||||
|
||||
if ($value->isConfidential() && null === $value->getUser()) {
|
||||
$this->context
|
||||
->buildViolation($constraint->message)
|
||||
->atPath('user')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
@ -1177,6 +1177,44 @@ paths:
|
||||
422:
|
||||
description: "object with validation errors"
|
||||
|
||||
/1.0/person/accompanying-course/{id}/intensity.json:
|
||||
post:
|
||||
tags:
|
||||
- person
|
||||
summary: "Toggle intensity status of accompanying course"
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The accompanying period's id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
requestBody:
|
||||
description: "Intensity toggle"
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- "accompanying_period"
|
||||
intensity:
|
||||
type: string
|
||||
responses:
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
404:
|
||||
description: "Not found"
|
||||
200:
|
||||
description: "OK"
|
||||
422:
|
||||
description: "object with validation errors"
|
||||
|
||||
/1.0/person/accompanying-course/by-person/{person_id}.json:
|
||||
get:
|
||||
tags:
|
||||
|
@ -1,15 +1,15 @@
|
||||
services:
|
||||
|
||||
Chill\PersonBundle\Form\:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
resource: '../../Form/'
|
||||
|
||||
Chill\PersonBundle\Form\PersonType:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
arguments:
|
||||
$personFieldsConfiguration: '%chill_person.person_fields%'
|
||||
$configAltNamesHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
|
||||
$translatableStringHelper: '@Chill\MainBundle\Templating\TranslatableStringHelper'
|
||||
tags:
|
||||
- { name: form.type, alias: '@chill.person.form.person_creation' }
|
||||
|
||||
|
@ -19,8 +19,5 @@ Chill\PersonBundle\Entity\AccompanyingPeriod:
|
||||
Chill\PersonBundle\Entity\PersonPhone:
|
||||
properties:
|
||||
phonenumber:
|
||||
- Regex:
|
||||
pattern: '/^([\+{1}])([0-9\s*]{4,20})$/'
|
||||
message: 'Invalid phone number: it should begin with the international prefix starting with "+", hold only digits and be smaller than 20 characters. Ex: +33123456789'
|
||||
- Chill\MainBundle\Validation\Constraint\PhonenumberConstraint:
|
||||
type: any
|
||||
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Exception;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
|
||||
final class Version20220215135509 extends AbstractMigration implements ContainerAwareInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
throw new Exception('You should not do that.');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Update phone numbers for person';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$carrier_code = $this->container
|
||||
->getParameter('chill_main')['phone_helper']['default_carrier_code'];
|
||||
|
||||
if (null === $carrier_code) {
|
||||
throw new RuntimeException('no carrier code');
|
||||
}
|
||||
|
||||
$this->addSql('ALTER TABLE chill_person_person ALTER phonenumber TYPE TEXT');
|
||||
$this->addSql('ALTER TABLE chill_person_person ALTER phonenumber DROP DEFAULT');
|
||||
$this->addSql('ALTER TABLE chill_person_person ALTER phonenumber DROP NOT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_person.phonenumber IS NULL');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_person_person ALTER mobilenumber TYPE TEXT');
|
||||
$this->addSql('ALTER TABLE chill_person_person ALTER mobilenumber DROP DEFAULT');
|
||||
$this->addSql('ALTER TABLE chill_person_person ALTER mobilenumber DROP NOT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_person.mobilenumber IS NULL');
|
||||
|
||||
$this->addSql(
|
||||
'UPDATE chill_person_person SET ' .
|
||||
$this->buildMigrationPhonenumberClause($carrier_code, 'phonenumber') .
|
||||
', ' .
|
||||
$this->buildMigrationPhoneNumberClause($carrier_code, 'mobilenumber')
|
||||
);
|
||||
|
||||
$this->addSql('ALTER TABLE chill_person_phone ALTER phonenumber TYPE TEXT');
|
||||
$this->addSql('ALTER TABLE chill_person_phone ALTER phonenumber DROP DEFAULT');
|
||||
$this->addSql('ALTER TABLE chill_person_phone ALTER phonenumber DROP NOT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN chill_person_phone.phonenumber IS NULL');
|
||||
|
||||
$this->addSql(
|
||||
'UPDATE chill_person_phone SET ' .
|
||||
$this->buildMigrationPhoneNumberClause($carrier_code, 'phonenumber')
|
||||
);
|
||||
}
|
||||
|
||||
private function buildMigrationPhoneNumberClause(string $defaultCarriercode, string $field): string
|
||||
{
|
||||
$util = PhoneNumberUtil::getInstance();
|
||||
|
||||
$countryCode = $util->getCountryCodeForRegion($defaultCarriercode);
|
||||
|
||||
return sprintf('%s=CASE
|
||||
WHEN %s = \'\' THEN NULL
|
||||
WHEN LEFT(%s, 1) = \'0\'
|
||||
THEN \'+%s\' || replace(replace(substr(%s, 2), \'(0)\', \'\'), \' \', \'\')
|
||||
ELSE replace(replace(%s, \'(0)\', \'\'),\' \', \'\')
|
||||
END', $field, $field, $field, $countryCode, $field, $field);
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ Two addresses has the same validFrom date: La date de validité est identique à
|
||||
The firstname cannot be empty: Le prénom ne peut pas être vide
|
||||
The lastname cannot be empty: Le nom de famille ne peut pas être vide
|
||||
The gender must be set: Le genre doit être renseigné
|
||||
You are not allowed to perform this action: Vous n'avez pas le droit de changer cette valeur.
|
||||
|
||||
#export list
|
||||
You must select at least one element: Vous devez sélectionner au moins un élément
|
||||
@ -51,6 +52,8 @@ household_membership:
|
||||
A course must contains at least one social issue: 'Un parcours doit être associé à au moins une problématique sociale'
|
||||
A course must be associated to at least one scope: 'Un parcours doit être associé à au moins un service'
|
||||
The social %name% issue cannot be deleted because it is associated with an activity or an action: 'La problématique sociale "%name%" ne peut pas être supprimée car elle est associée à une activité ou une action'
|
||||
A confidential parcours must have a referrer: 'Un parcours confidentiel doit avoir un référent'
|
||||
Only the referrer can change the confidentiality of a parcours: 'Seul le référent peut modifier la confidentialité'
|
||||
|
||||
# resource
|
||||
You must associate at least one entity: Associez un usager, un tiers ou indiquez une description libre
|
||||
|
@ -3,7 +3,7 @@
|
||||
{% block title 'Tasks for this accompanying period'|trans %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10 col-xxl">
|
||||
<div class="task-list">
|
||||
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
|
@ -21,6 +21,7 @@ use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Iterator;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use Nelmio\Alice\Loader\NativeLoader;
|
||||
use Nelmio\Alice\ObjectSet;
|
||||
|
||||
@ -29,6 +30,13 @@ use function count;
|
||||
|
||||
class LoadThirdParty extends Fixture implements DependentFixtureInterface
|
||||
{
|
||||
private PhoneNumberUtil $phoneNumberUtil;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->phoneNumberUtil = PhoneNumberUtil::getInstance();
|
||||
}
|
||||
|
||||
public function getDependencies()
|
||||
{
|
||||
return [
|
||||
@ -66,7 +74,7 @@ class LoadThirdParty extends Fixture implements DependentFixtureInterface
|
||||
Address::class => [
|
||||
'address1' => [
|
||||
'name' => '<fr_FR:company()>',
|
||||
'telephone' => '<fr_FR:phonenumber()>',
|
||||
'telephone' => $this->phoneNumberUtil->getExampleNumber('FR'),
|
||||
'email' => '<email()>',
|
||||
'comment' => '<fr_FR:realTextBetween(10, 500)>',
|
||||
],
|
||||
@ -116,7 +124,7 @@ class LoadThirdParty extends Fixture implements DependentFixtureInterface
|
||||
ThirdParty::class => [
|
||||
'thirdparty{1..75}' => [
|
||||
'name' => '<fr_FR:company()>',
|
||||
'telephone' => '<fr_FR:phonenumber()>',
|
||||
'telephone' => $this->phoneNumberUtil->getExampleNumber('FR'),
|
||||
'email' => '<email()>',
|
||||
'comment' => '<fr_FR:realTextBetween(10, 500)>',
|
||||
'address' => '@address<current()>',
|
||||
|
@ -24,6 +24,7 @@ use DateTimeInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use Symfony\Component\Serializer\Annotation\Context;
|
||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
@ -253,14 +254,11 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
|
||||
private ?ThirdPartyProfession $profession = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="telephone", type="string", length=64, nullable=true)
|
||||
* @Assert\Regex("/^([\+{1}])([0-9\s*]{4,20})$/",
|
||||
* message="Invalid phone number: it should begin with the international prefix starting with ""+"", hold only digits and be smaller than 20 characters. Ex: +33123456789"
|
||||
* )
|
||||
* @ORM\Column(name="telephone", type="phone_number", nullable=true)
|
||||
* @PhonenumberConstraint(type="any")
|
||||
* @Groups({"read", "write", "docgen:read", "docgen:read:3party:parent"})
|
||||
*/
|
||||
private ?string $telephone = null;
|
||||
private ?PhoneNumber $telephone = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="types", type="json", nullable=true)
|
||||
@ -502,7 +500,7 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
|
||||
/**
|
||||
* Get telephone.
|
||||
*/
|
||||
public function getTelephone(): ?string
|
||||
public function getTelephone(): ?PhoneNumber
|
||||
{
|
||||
return $this->telephone;
|
||||
}
|
||||
@ -821,12 +819,8 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface
|
||||
|
||||
/**
|
||||
* Set telephone.
|
||||
*
|
||||
* @param string|null $telephone
|
||||
*
|
||||
* @return ThirdParty
|
||||
*/
|
||||
public function setTelephone($telephone = null)
|
||||
public function setTelephone(?PhoneNumber $telephone = null): self
|
||||
{
|
||||
$this->telephone = $telephone;
|
||||
|
||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\ThirdPartyBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
||||
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\PickAddressType;
|
||||
use Chill\MainBundle\Form\Type\PickCenterType;
|
||||
@ -75,7 +76,7 @@ class ThirdPartyType extends AbstractType
|
||||
->add('name', TextType::class, [
|
||||
'required' => true,
|
||||
])
|
||||
->add('telephone', TextType::class, [
|
||||
->add('telephone', ChillPhoneNumberType::class, [
|
||||
'label' => 'Phonenumber',
|
||||
'required' => false,
|
||||
])
|
||||
|
@ -22,6 +22,9 @@ div.thirdparty-list {
|
||||
div.item-col:first-child {
|
||||
flex-basis: 25%;
|
||||
}
|
||||
div.wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@
|
||||
-->
|
||||
</ul>
|
||||
<div v-if="thirdparty.contactDataAnonymous">
|
||||
<confidential>
|
||||
<confidential :positionBtnFar="false">
|
||||
<template v-slot:confidential-content>
|
||||
<ul class="list-content fa-ul">
|
||||
<li v-if="thirdparty.address">
|
||||
|
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<span v-if="isCut">{{ cutText }}</span>
|
||||
<span v-else class="thirdparty-text">
|
||||
<span class="firstname">{{ thirdparty.text }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "ThirdPartyText",
|
||||
props: {
|
||||
thirdparty: {
|
||||
required: true,
|
||||
},
|
||||
isCut: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
cutText: function() {
|
||||
let more = (this.thirdparty.text.length > 15) ?'…' : '';
|
||||
return this.thirdparty.text.slice(0,15) + more;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -110,8 +110,8 @@
|
||||
}) }}
|
||||
</li>
|
||||
<li><i class="fa fa-li fa-phone"></i>
|
||||
{% if thirdparty.telephone %}
|
||||
<a href="{{ 'tel:' ~ thirdparty.telephone }}">{{ thirdparty.telephone|chill_format_phonenumber }}</a>
|
||||
{% if thirdparty.telephone is not null %}
|
||||
<a href="{{ 'tel:' ~ thirdparty.telephone|phone_number_format('E164') }}">{{ thirdparty.telephone|chill_format_phonenumber }}</a>
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'thirdparty.No_phonenumber'|trans }}</span>
|
||||
{% endif %}
|
||||
@ -136,7 +136,7 @@
|
||||
</li>
|
||||
<li><i class="fa fa-li fa-phone"></i>
|
||||
{% if thirdparty.telephone %}
|
||||
<a href="{{ 'tel:' ~ thirdparty.telephone }}">{{ thirdparty.telephone|chill_format_phonenumber }}</a>
|
||||
<a href="{{ 'tel:' ~ thirdparty.telephone|phone_number_format('E164') }}">{{ thirdparty.telephone|chill_format_phonenumber }}</a>
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'thirdparty.No_phonenumber'|trans }}</span>
|
||||
{% endif %}
|
||||
@ -173,7 +173,7 @@
|
||||
{% if options['showContacts'] and thirdparty.activeChildren|length > 0 %}
|
||||
<div class="item-row">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col">
|
||||
<div class="item-col wrap">
|
||||
<h5 class="me-2">{{ 'thirdparty.Children'|trans ~ ': ' }}</h5>
|
||||
{% for c in thirdparty.activeChildren %}
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
|
@ -67,10 +67,10 @@
|
||||
<dt>{{ 'Phonenumber'|trans }}</dt>
|
||||
<dd>
|
||||
{% if thirdParty.telephone == null %}
|
||||
<span class="chill-no-data-statement">{{ 'No phone given'|trans }}</span>
|
||||
<span class="chill-no-data-statement">{{ 'thirdparty.No_phonenumber'|trans }}</span>
|
||||
{% else %}
|
||||
<a href="{{ 'tel:' ~ thirdParty.telephone }}">
|
||||
{{ thirdParty.telephone|chill_print_or_message("thirdparty.No_phonenumber") }}
|
||||
<a href="{{ 'tel:' ~ thirdParty.telephone|phone_number_format('E164') }}">
|
||||
{{ thirdParty.telephone|chill_format_phonenumber }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user