merge master into branch

This commit is contained in:
Julie Lenaerts 2022-03-16 11:36:07 +01:00
commit 39ba21f308
74 changed files with 948 additions and 330 deletions

View File

@ -11,6 +11,12 @@ and this project adheres to
## Unreleased
<!-- write down unreleased development here -->
* [person] AccompanyingPeriodWorkEvaluation: fix circular reference when serialising (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/495)
* [person] order accompanying period by opening date in search persons, person and household period lists (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/493)
* [parcours] autosave of the pinned comment for draft accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/477)
* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472)
* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472)
* [person] Add url in accompanying period work evaluations entity and form (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/476)
* [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)
@ -28,8 +34,20 @@ and this project adheres to
* [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)
* [household] bugfix if position of member is null, renderbox no longer throws an error (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/480)
* [parcours] location cannot be removed if linked to a user (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/478)
* [person] email added to twig personRenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/490)
* [activity] Only youngest descendant is kept for social issues and actions (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/471)
* [person] Add link to current household in person banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/484)
* [address] person badge in address history changed to open OnTheFly with all person info (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/489)
* [person] Change 'personne' with 'usager' and '&' with 'ET' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/499)
* [thirdparty] Add parameter condition to display centers or not (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/500)
* [phonenumber] Remove placeholder in phonenumber field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/496)
* [person_resource] separate create page created to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/504)
* [contact] add contact button color changed plus the pipe at the side removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/506)
* [household] create-edit household composition placed in separate page to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/505)
* [blur] Improved positioning of toggle icon (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/486)
## Test releases
### test release 2022-02-21

View File

@ -350,11 +350,6 @@ parameters:
count: 6
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Entity/PersonPhone.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Workflow;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
@ -36,6 +37,13 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
$this->translator = $translator;
}
public function getDeletionRoles(): array
{
return [
AccompanyingCourseDocumentVoter::DELETE,
];
}
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$course = $this->getRelatedEntity($entityWorkflow)
@ -66,6 +74,18 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingCourseDocument $object
*
* @return array[]
*/
public function getRelatedObjects(object $object): array
{
return [
['entityClass' => AccompanyingCourseDocument::class, 'entityId' => $object->getId()],
];
}
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return null;
@ -84,6 +104,11 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
];
}
public function isObjectSupported(object $object): bool
{
return $object instanceof AccompanyingCourseDocument;
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingCourseDocument::class;

View File

@ -256,6 +256,13 @@ class ChillMainExtension extends Extension implements
'channels' => ['chill'],
]);
$container->prependExtensionConfig('security', [
'access_decision_manager' => [
'strategy' => 'unanimous',
'allow_if_all_abstain' => false,
],
]);
//add crud api
$this->prependCruds($container);
}

View File

@ -40,6 +40,7 @@ class UserJob
* @var array|string[]A
* @ORM\Column(name="label", type="json")
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
*/
protected array $label = [];

View File

@ -192,7 +192,7 @@ class EntityWorkflowStep
* You should **not** rely on this method to get all users which are able to
* apply a transition on this step. Use @see{EntityWorkflowStep::getAllDestUser} instead.
*/
public function getDestUser(): collection
public function getDestUser(): Collection
{
return $this->destUser;
}

View File

@ -16,9 +16,7 @@ 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
{
@ -37,21 +35,7 @@ class ChillPhoneNumberType extends AbstractType
$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']),
]
);
});
->setDefault('type', \libphonenumber\PhoneNumberType::FIXED_LINE_OR_MOBILE);
}
public function getParent()

View File

@ -27,7 +27,7 @@ interface PhoneNumberHelperInterface
/**
* Get type (mobile, landline, ...) for phone number.
*/
public function getType(string $phonenumber): string;
public function getType(PhoneNumber $phonenumber): string;
/**
* Return true if the validation is configured and available.

View File

@ -17,6 +17,7 @@ use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberType;
use libphonenumber\PhoneNumberUtil;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface;
@ -86,9 +87,19 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
/**
* Get type (mobile, landline, ...) for phone number.
*/
public function getType(string $phonenumber): string
public function getType(PhoneNumber $phonenumber): string
{
return $this->performTwilioLookup($phonenumber) ?? 'unknown';
switch ($this->phoneNumberUtil->getNumberType($phonenumber)) {
case PhoneNumberType::MOBILE:
return 'mobile';
case PhoneNumberType::FIXED_LINE:
case PhoneNumberType::VOIP:
return 'landline';
default:
return 'landline';
}
}
/**

View File

@ -55,6 +55,30 @@ class EntityWorkflowRepository implements ObjectRepository
return (int) $qb->getQuery()->getSingleScalarResult();
}
public function countRelatedWorkflows(array $relateds): int
{
$qb = $this->repository->createQueryBuilder('w');
$orX = $qb->expr()->orX();
$i = 0;
foreach ($relateds as $related) {
$orX->add(
$qb->expr()->andX(
$qb->expr()->eq('w.relatedEntityClass', ':entity_class_' . $i),
$qb->expr()->eq('w.relatedEntityId', ':entity_id_' . $i)
)
);
$qb
->setParameter('entity_class_' . $i, $related['entityClass'])
->setParameter('entity_id_' . $i, $related['entityId']);
++$i;
}
$qb->where($orX);
return $qb->select('COUNT(w)')->getQuery()->getSingleScalarResult();
}
public function find($id): ?EntityWorkflow
{
return $this->repository->find($id);

View File

@ -25,7 +25,7 @@ $chill-theme-buttons: (
"notify": $chill-blue,
"search": $gray-300,
"unlink": $chill-red,
"tpchild": $chill-pink,
"tpchild": $chill-green,
);
@each $button, $color in $chill-theme-buttons {

View File

@ -1,17 +1,22 @@
/**
* Generic api method that can be adapted to any fetch request
*/
const makeFetch = (method, url, body) => {
return fetch(url, {
const makeFetch = (method, url, body, options) => {
let opts = {
method: method,
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: (body !== null) ? JSON.stringify(body) : null
})
};
if (typeof options !== 'undefined') {
opts = Object.assign(opts, options);
}
return fetch(url, opts)
.then(response => {
if (response.ok) {
console.log('200 error')
return response.json();
}

View File

@ -1,37 +1,26 @@
.confidential {
display: flex;
position: relative;
}
.toggle-far-twig {
i {
bottom: 0px;
right: -30px;
}
margin-right: 20px
}
.toggle-close-twig {
i {
bottom: 0px;
right: -5px;
}
.toggle-container {
position: absolute;
width: 100%;
top: 0;
left: 0;
cursor: pointer;
z-index: 5;
padding-right: 1rem;
}
.toggle{
margin-left: 30px;
margin-top: 5px;
cursor: pointer;
position: absolute;
z-index: 5;
right: -30px
}
.toggle-far {
bottom: 0px;
right: 20px !important;
}
.toggle-close {
bottom: 125px;
right: 15px !important;
right: 4px;
&-twig {
position: absolute;
right: -25px;
bottom: 20px;
}
}
.blur {

View File

@ -2,18 +2,19 @@ require('./blur.scss');
document.querySelectorAll('.confidential').forEach(function (el) {
let i = document.createElement('i');
const classes = ['fa', 'fa-eye', 'toggle'];
const classes = ['fa', 'fa-eye-slash', 'toggle-twig'];
i.classList.add(...classes);
el.appendChild(i);
const toggleBlur = function(e) {
for (let child of el.children) {
if (!child.classList.contains('toggle')) {
if (!child.classList.contains('toggle-twig')) {
child.classList.toggle('blur');
}
}
i.classList.toggle('fa-eye');
i.classList.toggle('fa-eye-slash');
i.classList.toggle('fa-eye');
}
i.addEventListener('click', toggleBlur);
toggleBlur();
});
});

View File

@ -22,6 +22,7 @@ const fetchCountries = () => {
*/
const fetchCities = (country) => {
//console.log('<<< fetching cities for', country);
// warning: do not use fetchResults (in apiMethods): we need only a **part** of the results in the db
const url = `/api/1.0/main/postal-code.json?item_per_page=1000&country=${country.id}`;
return fetch(url)
.then(response => {

View File

@ -42,9 +42,6 @@
<a class="btn btn-sm btn-show" :href="getUrl(e)">
{{ $t('show_entity', { entity: $t('the_evaluation') }) }}
</a>
<a class="btn btn-sm btn-update" :href="getUrl(e.accompanyingPeriodWork)">
{{ $t('show_entity', { entity: $t('the_action') }) }}
</a>
<a class="btn btn-sm btn-show" :href="getUrl(e.accompanyingPeriodWork.accompanyingPeriod)">
{{ $t('show_entity', { entity: $t('the_course') }) }}
</a>
@ -102,4 +99,4 @@ export default {
<style scoped>
</style>
</style>

View File

@ -3,8 +3,8 @@
<div :class="{ 'blur': isBlurred }">
<slot name="confidential-content"></slot>
</div>
<div>
<i class="fa fa-eye toggle" :class="positionBtn" aria-hidden="true" @click="toggleBlur"></i>
<div class="toggle-container">
<i class="fa toggle" :class="toggleIcon" aria-hidden="true" @click="toggleBlur"></i>
</div>
</div>
</template>
@ -12,28 +12,24 @@
<script>
export default {
name: "Confidential",
props: ['positionBtnFar'],
data() {
return {
isBlurred: true,
toggleIcon: 'fa-eye',
};
},
methods : {
toggleBlur() {
console.log(this.positionBtnFar);
this.isBlurred = !this.isBlurred;
this.toggleIcon = this.isBlurred ? 'fa-eye' : 'fa-eye-slash';
},
},
computed: {
positionBtn() {
return this.positionBtnFar ? 'toggle-far' : 'toggle-close'
}
}
}
</script>
<style scoped lang='scss'>
.confidential{
align-items: center;
align-content: center;
}
</style>

View File

@ -59,7 +59,7 @@
must be shown in such list
#}
{%- if render == 'list' -%}
<li class="chill-entity entity-address {% if address.confidential %} confidential toggle-far-twig {% endif %}">
<li class="chill-entity entity-address {% if address.confidential %} confidential {% 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 toggle-far-twig {% endif %}">
<span class="chill-entity entity-address {% if address.confidential %} confidential {% 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 toggle-close-twig {% endif %}">
<div class="chill-entity entity-address {% if address.confidential %} confidential {% 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 %}">

View File

@ -9,6 +9,12 @@
<span class="item-key">{{ 'Le'|trans ~ ' : ' }}</span>
<b>{{ step.previous.transitionAt|format_datetime('short', 'short') }}</b>
</li>
<li>
<span class="item-key">{{ 'workflow.For'|trans ~ ' : ' }}</span>
<b>
{% for d in step.destUser %}{{ d|chill_entity_render_string }}{% if not loop.last %}, {% endif %}{% endfor %}
</b>
</li>
{% else %}
<li>
<span class="item-key">{{ 'workflow.Created by'|trans ~ ' : ' }}</span>

View File

@ -0,0 +1,65 @@
<?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\Security\Authorization;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use RuntimeException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use function in_array;
use function is_object;
class WorkflowEntityDeletionVoter extends Voter
{
private EntityWorkflowRepository $entityWorkflowRepository;
/**
* @var iterable|EntityWorkflowHandlerInterface[]
*/
private iterable $handlers;
public function __construct($handlers, EntityWorkflowRepository $entityWorkflowRepository)
{
$this->handlers = $handlers;
$this->entityWorkflowRepository = $entityWorkflowRepository;
}
protected function supports($attribute, $subject)
{
if (!is_object($subject)) {
return false;
}
foreach ($this->handlers as $handler) {
if ($handler->isObjectSupported($subject)
&& in_array($attribute, $handler->getDeletionRoles($subject), true)) {
return true;
}
}
return false;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
foreach ($this->handlers as $handler) {
if ($handler->isObjectSupported($subject)) {
return 0 === $this->entityWorkflowRepository->countRelatedWorkflows(
$handler->getRelatedObjects($subject)
);
}
}
throw new RuntimeException('no handlers found');
}
}

View File

@ -113,6 +113,7 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw
[
'postcode' => $helper->normalize(self::NULL_POSTCODE_COUNTRY, $format, $context),
'country' => $helper->normalize(self::NULL_POSTCODE_COUNTRY, $format, $context),
'lines' => [],
]
);
}

View File

@ -16,10 +16,11 @@ use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberUtil;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class PhonenumberNormalizer implements NormalizerInterface, DenormalizerInterface
class PhonenumberNormalizer implements ContextAwareNormalizerInterface, DenormalizerInterface
{
private string $defaultCarrierCode;
@ -40,6 +41,10 @@ class PhonenumberNormalizer implements NormalizerInterface, DenormalizerInterfac
*/
public function denormalize($data, $type, $format = null, array $context = [])
{
if ('' === trim($data)) {
return null;
}
try {
return $this->phoneNumberUtil->parse($data, $this->defaultCarrierCode);
} catch (NumberParseException $e) {
@ -49,6 +54,10 @@ class PhonenumberNormalizer implements NormalizerInterface, DenormalizerInterfac
public function normalize($object, ?string $format = null, array $context = []): string
{
if ($format === 'docgen' && null === $object) {
return '';
}
return $this->phoneNumberUtil->formatOutOfCountryCallingNumber($object, $this->defaultCarrierCode);
}
@ -57,8 +66,18 @@ class PhonenumberNormalizer implements NormalizerInterface, DenormalizerInterfac
return 'libphonenumber\PhoneNumber' === $type;
}
public function supportsNormalization($data, ?string $format = null)
public function supportsNormalization($data, ?string $format = null, array $context = []): bool
{
return $data instanceof PhoneNumber;
if ($data instanceof PhoneNumber && $format === 'json') {
return true;
}
if ($format === 'docgen' && (
$data instanceof PhoneNumber || PhoneNumber::class === ($context['docgen:expects'] ?? null)
)) {
return true;
}
return false;
}
}

View File

@ -15,12 +15,19 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
interface EntityWorkflowHandlerInterface
{
/**
* @return array|string[]
*/
public function getDeletionRoles(): array;
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array;
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string;
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object;
public function getRelatedObjects(object $object): array;
/**
* Return a string representing the role required for seeing the workflow.
*
@ -33,6 +40,8 @@ interface EntityWorkflowHandlerInterface
public function getTemplateData(EntityWorkflow $entityWorkflow, array $options = []): array;
public function isObjectSupported(object $object): bool;
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool;
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool;

View File

@ -75,3 +75,9 @@ services:
$locker: '@Chill\MainBundle\Security\PasswordRecover\PasswordRecoverLocker'
tags:
- { name: security.voter }
Chill\MainBundle\Security\Authorization\WorkflowEntityDeletionVoter:
autoconfigure: true
autowire: true
arguments:
$handlers: !tagged_iterator chill_main.workflow_handler

View File

@ -413,6 +413,7 @@ workflow:
Previous workflow without reaction help: Liste des workflows où vous avez été cité comme pouvant réagir à une étape, mais où un autre utilisateur a exécuté une action avant vous.
Previous transitionned: Anciens workflows
Previous workflow transitionned help: Workflows où vous avez exécuté une action.
For: Pour
Subscribe final: Recevoir une notification à l'étape finale

View File

@ -222,6 +222,10 @@ class AccompanyingPeriodController extends AbstractController
$accompanyingPeriodsRaw = $this->accompanyingPeriodACLAwareRepository
->findByPerson($person, AccompanyingPeriodVoter::SEE);
usort($accompanyingPeriodsRaw, static function ($a, $b) {
return $b->getOpeningDate() > $a->getOpeningDate();
});
// filter visible or not visible
$accompanyingPeriods = array_filter($accompanyingPeriodsRaw, function (AccompanyingPeriod $ap) {
return $this->isGranted(AccompanyingPeriodVoter::SEE, $ap);

View File

@ -134,7 +134,7 @@ class HouseholdCompositionController extends AbstractController
public function index(Household $household, Request $request): Response
{
if (!$this->security->isGranted(HouseholdVoter::SEE, $household)) {
throw new AccessDeniedException('not allowed to edit an household');
throw new AccessDeniedException('not allowed to edit a household');
}
$count = $this->householdCompositionRepository->countByHousehold($household);
@ -146,6 +146,20 @@ class HouseholdCompositionController extends AbstractController
$paginator->getCurrentPageFirstItemNumber()
);
return new Response($this->engine->render(
'@ChillPerson/HouseholdComposition/index.html.twig',
[
'household' => $household,
'compositions' => $compositions,
]
));
}
/**
* @Route("/{_locale}/person/household/{id}/composition/new", name="chill_person_household_composition_new")
*/
public function newAction(Household $household, Request $request): Response
{
if ($this->security->isGranted(HouseholdVoter::EDIT, $household)) {
$isEdit = $request->query->has('edit');
@ -195,10 +209,9 @@ class HouseholdCompositionController extends AbstractController
}
return new Response($this->engine->render(
'@ChillPerson/HouseholdComposition/index.html.twig',
'@ChillPerson/HouseholdComposition/create.html.twig',
[
'household' => $household,
'compositions' => $compositions,
'form' => isset($form) ? $form->createView() : null,
'isPosted' => isset($form) ? $form->isSubmitted() : false,
'editId' => $request->query->getInt('edit', -1),

View File

@ -78,6 +78,10 @@ class HouseholdController extends AbstractController
}
}
usort($accompanyingPeriods, static function ($a, $b) {
return $b->getOpeningDate() > $a->getOpeningDate();
});
$oldMembers = $household->getNonCurrentMembers();
$accompanyingPeriodsOld = [];

View File

@ -133,6 +133,19 @@ final class PersonResourceController extends AbstractController
$personResources = [];
$personResources = $this->personResourceRepository->findBy(['personOwner' => $personOwner->getId()]);
return $this->render(
'ChillPersonBundle:PersonResource:list.html.twig',
[
'person' => $personOwner,
'personResources' => $personResources,
]
);
}
public function newAction(Request $request, $person_id)
{
$personOwner = $this->personRepository->find($person_id);
$form = $this->createForm(PersonResourceType::class);
$form->handleRequest($request);
@ -165,11 +178,10 @@ final class PersonResourceController extends AbstractController
}
return $this->render(
'ChillPersonBundle:PersonResource:list.html.twig',
'ChillPersonBundle:PersonResource:create.html.twig',
[
'person' => $personOwner,
'personResources' => $personResources,
'form' => $form->createView(),
'person' => $personOwner,
]
);
}

View File

@ -15,6 +15,7 @@ use Chill\MainBundle\DependencyInjection\MissingBundleException;
use Chill\MainBundle\Security\Authorization\ChillExportVoter;
use Chill\PersonBundle\Controller\HouseholdCompositionTypeApiController;
use Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodCommentVoter;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodResourceVoter;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
@ -415,6 +416,25 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
],
],
],
[
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod\Comment::class,
'name' => 'accompanying_period_comment',
'base_path' => '/api/1.0/person/accompanying-period/comment',
'base_role' => 'ROLE_USER',
'actions' => [
'_entity' => [
'methods' => [
Request::METHOD_GET => false,
Request::METHOD_PATCH => true,
Request::METHOD_HEAD => false,
Request::METHOD_DELETE => false,
],
'roles' => [
Request::METHOD_PATCH => AccompanyingPeriodCommentVoter::EDIT,
],
],
],
],
[
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod\Resource::class,
'name' => 'accompanying_period_resource',

View File

@ -257,9 +257,11 @@ class AccompanyingPeriod implements
/**
* @ORM\ManyToOne(
* targetEntity=Comment::class
* targetEntity=Comment::class,
* cascade={"persist"},
* )
* @Groups({"read"})
* @ORM\JoinColumn(onDelete="SET NULL")
*/
private ?Comment $pinnedComment = null;

View File

@ -124,6 +124,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?DateTimeImmutable $maxDate = null;

View File

@ -109,6 +109,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class,
* mappedBy="person",
* cascade={"persist", "remove", "merge", "detach"})
* @ORM\OrderBy({"startDate": "DESC"})
*/
private $accompanyingPeriodParticipations;

View File

@ -98,7 +98,8 @@ class PersonPhone
public function isEmpty(): bool
{
return empty($this->getDescription()) && empty($this->getPhonenumber());
return ('' === $this->getDescription() || null === $this->getDescription())
&& null === $this->getPhonenumber();
}
public function setDate(DateTime $date): void

View File

@ -62,6 +62,12 @@ class Evaluation
*/
private array $title = [];
/**
* @ORM\Column(type="text", nullable=true)
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?string $url = null;
public function __construct()
{
$this->socialActions = new ArrayCollection();
@ -101,6 +107,11 @@ class Evaluation
return $this->title;
}
public function getUrl(): ?string
{
return $this->url;
}
public function removeSocialAction(SocialAction $socialAction): self
{
if ($this->socialActions->contains($socialAction)) {
@ -130,4 +141,11 @@ class Evaluation
return $this;
}
public function setUrl(?string $url): self
{
$this->url = $url;
return $this;
}
}

View File

@ -27,6 +27,7 @@ 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;
@ -158,7 +159,7 @@ class PersonType extends AbstractType
}
$builder->add('otherPhoneNumbers', ChillCollectionType::class, [
'entry_type' => ChillPhoneNumberType::class,
'entry_type' => PersonPhoneType::class,
'button_add_label' => 'Add new phone',
'button_remove_label' => 'Remove phone',
'required' => false,

View File

@ -11,11 +11,11 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Form\Type;
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
use Chill\PersonBundle\Entity\PersonPhone;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TelType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
@ -36,7 +36,7 @@ class PersonPhoneType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('phonenumber', TelType::class, [
$builder->add('phonenumber', ChillPhoneNumberType::class, [
'label' => 'Other phonenumber',
'required' => true,
]);

View File

@ -33,61 +33,74 @@ div.banner {
padding-top: 1em;
padding-bottom: 1em;
div.contact {
display: flex;
align-content: center;
& > * {
margin-right: 1em;
}
}
.household-link {
border: 1px solid white;
padding: .05rem .3rem;
border-radius: 5px;
color: white;
cursor: pointer;
&:hover {
background-color: white;
color: $chill-person-context
}
}
}
}
div.person-view {
figure.person-details {
h2 {
font-family: 'Open Sans';
font-weight: 600;
margin-bottom: 0.3em;
font-variant: small-caps;
}
dl {
margin-top: 0.3em;
}
dt {
font-family: 'Open Sans';
font-weight: 600;
}
dd {
margin-left: 0;
}
/*
a.sc-button { background-color: $black; padding-top: 0.2em; padding-bottom: 0.2em; }
*/
}
/* custom fields on the home page */
div.custom-fields {
figure.person-details {
display: flex;
flex-flow: row wrap;
div.cf_title_box:nth-child(4n+1) h2 {
@extend .chill-red !optional;
}
div.cf_title_box:nth-child(4n+2) h2 {
@extend .chill-green !optional;
}
div.cf_title_box:nth-child(4n+3) h2 {
@extend .chill-orange !optional;
}
div.cf_title_box:nth-child(4n+4) h2 {
@extend .chill-blue !optional;
}
div.cf_title_box:nth-child(2n+1) {
width: 50%;
margin-right: 40px;
}
div.cf_title_box:nth-child(2n+2) {
width: calc(50% - 40px);
}
}
}
figure.person-details {
h2 {
font-family: 'Open Sans';
font-weight: 600;
margin-bottom: 0.3em;
font-variant: small-caps;
}
dl {
margin-top: 0.3em;
}
dt {
font-family: 'Open Sans';
font-weight: 600;
}
dd {
margin-left: 0;
}
/*
a.sc-button { background-color: $black; padding-top: 0.2em; padding-bottom: 0.2em; }
*/
}
/* custom fields on the home page */
div.custom-fields {
figure.person-details {
display: flex;
flex-flow: row wrap;
div.cf_title_box:nth-child(4n+1) h2 {
@extend .chill-red !optional;
}
div.cf_title_box:nth-child(4n+2) h2 {
@extend .chill-green !optional;
}
div.cf_title_box:nth-child(4n+3) h2 {
@extend .chill-orange !optional;
}
div.cf_title_box:nth-child(4n+4) h2 {
@extend .chill-blue !optional;
}
div.cf_title_box:nth-child(2n+1) {
width: 50%;
margin-right: 40px;
}
div.cf_title_box:nth-child(2n+2) {
width: calc(50% - 40px);
}
}
}
}
/*

View File

@ -19,22 +19,21 @@ import {fetchResults} from 'ChillMainAssets/lib/api/apiMethods.js';
*/
document.querySelectorAll('[data-set-referrer-app]').forEach(function (el) {
let
periodId = Number.parseInt(el.dataset.setReferrerAccompanyingPeriodId);
const periodId = Number.parseInt(el.dataset.setReferrerAccompanyingPeriodId);
const jobId = Number.parseInt(el.dataset.setReferrerJobId);
const url = `/api/1.0/person/accompanying-course/${periodId}/referrers-suggested.json`;
fetchResults(url).then(suggested => {
const filteredSuggested = suggested.filter((s) => s.user_job ? s.user_job.id === jobId : false);
const app = createApp({
components: {
SetReferrer,
},
template:
'<set-referrer :suggested="suggested" :periodId="periodId" @referrerSet="onReferrerSet"></set-referrer>',
'<set-referrer :suggested="filteredSuggested" :periodId="periodId" @referrerSet="onReferrerSet"></set-referrer>',
data() {
return {
periodId, suggested, original: suggested,
periodId, filteredSuggested, original: filteredSuggested,
}
},
methods: {
@ -56,7 +55,7 @@ document.querySelectorAll('[data-set-referrer-app]').forEach(function (el) {
label.textContent = ref.text;
label.classList.remove('chill-no-data-statement');
this.suggested = this.original.filter(user => user.id !== ref.id);
this.filteredSuggested = this.original.filter(user => user.id !== ref.id);
}
}
});

View File

@ -14,24 +14,27 @@
<ckeditor
name="content"
v-bind:placeholder="$t('comment.content')"
:placeholder="$t('comment.content')"
:editor="editor"
v-model="content"
tag-name="textarea">
</ckeditor>
<div v-if="pinnedComment" class="metadata">
{{ $t('comment.created_by', [
pinnedComment.creator.text,
$d(pinnedComment.createdAt.datetime, 'long')
]) }}
<div class="sub-comment">
<div v-if="pinnedComment !== null && typeof pinnedComment.creator !== 'undefined'" class="metadata">
{{ $t('comment.created_by', [
pinnedComment.creator.text,
$d(pinnedComment.updatedAt.datetime, 'long')
])
}}
</div>
<div class="loading">
<i v-if="loading" class="fa fa-circle-o-notch fa-spin" :title="$t('loading')"></i>
</div>
</div>
<div>
<ul class="record_actions">
<li>
<button type="submit" class="btn btn-save">{{ $t('action.save') }}</button>
</li>
<li v-if="pinnedComment !== null">
<a class="btn btn-delete"
@click="removeComment">
@ -50,6 +53,7 @@
<script>
import CKEditor from '@ckeditor/ckeditor5-vue';
import ClassicEditor from "ChillMainAssets/module/ckeditor5";
import { mapState } from "vuex";
export default {
name: "Comment",
@ -59,22 +63,58 @@ export default {
data() {
return {
editor: ClassicEditor,
formdata: {
type: "accompanying_period_comment",
content: ''
}
loading: false,
lastRecordedContent: null,
}
},
computed: {
pinnedComment() {
return this.$store.state.accompanyingCourse.pinnedComment;
},
...mapState({
pinnedComment: state => state.accompanyingCourse.pinnedComment,
}),
content: {
set(value) {
this.formdata.content = value;
console.log('new comment value', value);
console.log('previous value', this.lastRecordedContent);
this.lastRecordedContent = value;
setTimeout(() => {
console.log('performing test on ', value);
if (this.lastRecordedContent === value) {
this.loading = true;
if (value !== '') {
this.$store.dispatch('updatePinnedComment', value)
.then(() => {
this.loading = false;
})
.catch(({name, violations}) => {
if (name === 'ValidationException' || name === 'AccessException') {
violations.forEach((violation) => this.$toast.open({message: violation}));
} else {
this.$toast.open({message: 'An error occurred'})
}
});
} else {
if (this.$store.state.accompanyingCourse.pinnedComment !== null) {
this.$store.dispatch('removePinnedComment', {id: this.pinnedComment.id})
.then(() => {
this.loading = false;
this.lastRecoredContent = null;
})
.catch(({name, violations}) => {
if (name === 'ValidationException' || name === 'AccessException') {
violations.forEach((violation) => this.$toast.open({message: violation}));
} else {
this.$toast.open({message: 'An error occurred'})
}
});
}
}
}
}, 3000);
},
get() {
return (this.pinnedComment)? this.pinnedComment.content : {};
get() {
return this.pinnedComment ? this.pinnedComment.content : '';
}
},
errors() {
@ -82,18 +122,11 @@ export default {
}
},
methods: {
submitform() {
this.$store.dispatch('postFirstComment', this.formdata)
.catch(({name, violations}) => {
if (name === 'ValidationException' || name === 'AccessException') {
violations.forEach((violation) => this.$toast.open({message: violation}));
} else {
this.$toast.open({message: 'An error occurred'})
}
});
onContentChange() {
let lastRecordedContent = this.formData.content;
},
removeComment() {
this.$store.dispatch('postFirstComment', {})
this.$store.dispatch('removePinnedComment', {id: this.pinnedComment.id})
.catch(({name, violations}) => {
if (name === 'ValidationException' || name === 'AccessException') {
violations.forEach((violation) => this.$toast.open({message: violation}));
@ -104,15 +137,18 @@ export default {
}
}
}
/*
* TODO
* - [x] delete button in ul record_actions, but not in form
* - [ ] display updatedAt => pinnedComment fetch PATCH content changes MUST NOT change object id !!
*/
</script>
<style lang="scss">
div.ck-editor.ck-reset {
margin: 0.6em 0;
}
div.sub-comment {
display: flex;
justify-content: space-between;
div.loading {
margin-right: 6px;
margin-left: 6px;
}
}
</style>

View File

@ -59,14 +59,6 @@
ref="addAddress">
</add-address>
</li>
<li v-if="isPersonLocation">
<button
class="btn btn-remove"
@click="removeAddress"
:title="$t('courselocation.remove_button')">
{{ $t('action.remove') }}
</button>
</li>
</ul>
</div>
@ -180,22 +172,6 @@ export default {
}
this.$store.commit('setAddressContext', context);
},
removeAddress() {
let payload = {
target: this.context.target.name,
targetId: this.context.target.id,
locationStatusTo: 'none'
};
//console.log('remove address');
this.$store.dispatch('updateLocation', payload)
.catch(({name, violations}) => {
if (name === 'ValidationException' || name === 'AccessException') {
violations.forEach((violation) => this.$toast.open({message: violation}));
} else {
this.$toast.open({message: 'An error occurred'})
}
});
},
displayErrors() {
return this.$refs.addAddress.errorMsg;
},

View File

@ -9,7 +9,7 @@
<input type="checkbox" v-model="requestorIsAnonymous" class="me-2" />
{{ $t('requestor.is_anonymous') }}
</label>
<confidential :positionBtn="false" v-if="accompanyingCourse.requestor.type === 'thirdparty'">
<confidential 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 :positionBtnFar="false" v-else-if="accompanyingCourse.requestor.type === 'person'">
<confidential v-else-if="accompanyingCourse.requestor.type === 'person'">
<template v-slot:confidential-content>
<person-render-box render="bloc"
:person="accompanyingCourse.requestor"
@ -339,5 +339,6 @@ div.flex-table {
.confidential {
display: block;
margin-right: 0px !important;
}
</style>

View File

@ -170,7 +170,9 @@ export default {
console.log('data', payload.data)
body.name = payload.data.name;
body.email = payload.data.email;
body.telephone = payload.data.phonenumber;
body.telephone = payload.data.telephone;
body.civility = payload.data.civility;
body.profession = payload.data.profession;
body.address = payload.data.address ? { id: payload.data.address.address_id } : null;
makeFetch('PATCH', `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body)

View File

@ -122,7 +122,7 @@ const appMessages = {
title: "Observations",
label: "Ajout d'une note",
content: "Rédigez une première note…",
created_by: "créé par {0}, le {1}"
created_by: "créé par {0}, mis à jour le {1}"
},
confirm: {
title: "Confirmation",

View File

@ -42,7 +42,11 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
referrersSuggested: [],
// all the users available
users: [],
permissions: {}
permissions: {},
// controller for updating comment
updateCommentAbortController: null,
// waiting response for inserting first comment
postFirstPinnedCommentResponse: null,
},
getters: {
isParticipationValid(state) {
@ -203,9 +207,35 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
//console.log('### mutation: toggleConfidential');
state.accompanyingCourse.confidential = value;
},
postFirstComment(state, comment) {
//console.log('### mutation: postFirstComment', comment);
state.accompanyingCourse.pinnedComment = comment;
setPinnedComment(state, content) {
if (null === state.accompanyingCourse.pinnedComment) {
state.accompanyingCourse.pinnedComment = {
id: -1,
content,
type: "accompanying_period_comment",
};
} else {
state.accompanyingCourse.pinnedComment.content = content;
}
},
setPinnedCommentDetails(state, value) {
state.accompanyingCourse.pinnedComment.id = value.id;
if (typeof value.creator !== 'undefined') {
state.accompanyingCourse.pinnedComment.creator = value.creator;
state.accompanyingCourse.pinnedComment.updatedBy = value.updatedBy;
state.accompanyingCourse.pinnedComment.updatedAt = value.updatedAt;
state.accompanyingCourse.pinnedComment.createdAt = value.createdAt;
}
},
removePinnedComment(state, value) {
state.accompanyingCourse.pinnedComment = null;
},
setPinCommentAbortController(state, value) {
state.updateCommentAbortController = value;
},
setPostFirstPinnedCommentResponse(state, value) {
state.postFirstPinnedCommentResponse = value;
},
updateSocialIssues(state, value) {
console.log('updateSocialIssues', value);
@ -337,6 +367,87 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
throw error;
})
},
/**
* Add/remove pinnedComment
*/
removePinnedComment({ commit }, payload) {
const body = {type: "accompanying_period_comment", id: payload.id};
const url = `/api/1.0/person/accompanying-course/${id}/comment.json`;
return makeFetch('DELETE', url, body)
.then((response) => {
commit('removePinnedComment');
})
.catch((error) => {
commit('catchError', error);
throw error;
})
},
/**
* internal method to insert a new comment
*
* @param commit
* @param dispatch
* @param content
* @returns {*}
*/
addPinnedComment({ commit, dispatch }, content) {
const url = `/api/1.0/person/accompanying-course/${id}/comment.json`;
return makeFetch('POST', url, {type: "accompanying_period_comment", content})
.then((response) => {
commit('setPinnedCommentDetails', response);
return dispatch('patchFirstComment', response);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
},
updatePinnedComment({ commit, state, dispatch }, content) {
commit('setPinnedComment', content);
if (state.accompanyingCourse.pinnedComment.id === -1 && state.postFirstPinnedCommentResponse === null) {
let r = dispatch('addPinnedComment', content).then(() => {
commit('setPostFirstPinnedCommentResponse', null);
});
commit('setPostFirstPinnedCommentResponse', r);
} else {
(state.postFirstPinnedCommentResponse === null ? Promise.resolve() : state.postFirstPinnedCommentResponse).then(() => {
dispatch('updateExistingPinnedComment', content);
});
}
},
/**
* internal method to patch an existing comment
*
* @param commit
* @param state
* @param comment
* @returns {*}
*/
updateExistingPinnedComment({ commit, state }, content) {
const payload = {type: "accompanying_period_comment", content, id: state.accompanyingCourse.pinnedComment.id};
const url = `/api/1.0/person/accompanying-period/comment/${payload.id}.json`;
if (state.updateCommentAbortController !== null) {
state.updateCommentAbortController.abort();
commit('setPinCommentAbortController', null);
}
let controller = new AbortController();
commit('setPinCommentAbortController', controller);
return makeFetch('PATCH', url, payload, { signal: controller.signal })
.then((response) => {
commit('setPinCommentAbortController', null);
commit('setPinnedCommentDetails', response);
})
.catch((error) => {
commit('catchError', error);
commit('setPinCommentAbortController', null);
throw error;
})
},
/**
* Add/remove/display anonymous requestor
*/
@ -606,18 +717,20 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
return Promise.all(promises);
},
postFirstComment({ commit }, payload) {
const url = `/api/1.0/person/accompanying-course/${id}.json`
const body = { type: "accompanying_period", pinnedComment: payload }
patchFirstComment({ commit }, payload) {
const url = `/api/1.0/person/accompanying-course/${id}.json`;
const body = {
type: "accompanying_period",
pinnedComment: {
type: "accompanying_period_comment",
id: payload.id
}
};
return makeFetch('PATCH', url, body)
.then((response) => {
commit('postFirstComment', response.pinnedComment);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
});
},
updateSocialIssues({ state, commit, dispatch }, { payload, body, method }) {
const url = `/api/1.0/person/accompanying-course/${id}/socialissue.json`;

View File

@ -5,6 +5,11 @@
<span>{{ evaluation.evaluation.title.fr }}</span>
</div>
<div class="item-url mt-3 mb-4" v-if="evaluation.evaluation.url">
<i class="fa fa-link fa-lg"></i>
<a :href="evaluation.evaluation.url" target="_blank">{{ evaluation.evaluation.url }}</a>
</div>
<div>
<form-evaluation ref="FormEvaluation" :key="evaluation.key" :evaluation="evaluation"></form-evaluation>
@ -21,7 +26,7 @@
></list-workflow-modal>
</li>
<li>
<li v-if="canDelete">
<a class="btn btn-delete" @click="modal.showModal = true" :title="$t('action.delete')"></a>
</li>
</ul>
@ -93,6 +98,19 @@ export default {
pickedEvaluations() {
return this.$store.state.evaluationsPicked;
},
canDelete() {
if (this.evaluation.workflows.length > 0) {
return false;
}
for (let doc of this.evaluation.documents) {
if (doc.workflows.length > 0) {
return false;
}
}
return true;
},
},
methods: {
removeEvaluation(e) {
@ -128,4 +146,11 @@ export default {
}
}
}
div.item-url {
i {
color: unset!important;
margin-left: 1rem;
margin-right: 0.5rem;
}
}
</style>

View File

@ -101,7 +101,7 @@
<li>
<a :href="buildEditLink(d.storedObject)" class="btn btn-wopilink"></a>
</li>
<li>
<li v-if="d.workflows.length === 0">
<a class="btn btn-delete" @click="removeDocument(d)">
</a>
</li>

View File

@ -1,12 +1,12 @@
<template>
<ul class="list-suggest add-items" v-if="suggested.length > 0">
<li v-for="r in suggested" @click="setReferrer(r)"><span>{{ r.text }}</span></li>
<li v-for="(r, i) in suggested" @click="setReferrer(r)" :key="i"><span>{{ r.text }}</span></li>
</ul>
</template>
<script>
import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods.js';
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods.js';
export default {
name: "SetReferrer",

View File

@ -87,10 +87,14 @@ export default {
currentMembers() {
let members = this.household.members.filter(m => this.household.current_members_id.includes(m.id))
.sort((a, b) => {
if (a.position.ordering < b.position.ordering) {
const orderA = a.position ? a.position.ordering : 0;
const orderB = b.position ? b.position.ordering : 0;
if (orderA < orderB) {
return -1;
}
if (a.position.ordering > b.position.ordering) {
if (orderA > orderB) {
return 1;
}
if (a.holder && !b.holder) {

View File

@ -130,11 +130,13 @@
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
></a>
</li>
<li>
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
></a>
</li>
{% if is_granted('CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE', w) %}
<li>
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
></a>
</li>
{% endif %}
</ul>
{% endif %}
</div>

View File

@ -117,12 +117,15 @@
</div>
{% endif %}
<div class="item-row separator">
<div class="item-col item-meta">
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) %}
{% if notif_counter.total > 0 %}
{{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) }}
{% endif %}
</div>
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) %}
{% if notif_counter.total > 0 %}
<div class="item-col item-meta">
{{ chill_counter_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', period.id) }}
</div>
{% endif %}
{% if itemMeta is defined %}
{{ itemMeta }}
{% endif %}
<div class="item-col">
{% if recordAction is defined %}
<ul class="record_actions">

View File

@ -167,7 +167,7 @@
<span class="chill-no-data-statement">{{ 'No data given'|trans }}</span>
</li>
{% endif %}
{% if person.email is not null %}
{% if person.email is not empty %}
<li>
<i class="fa fa-li fa-envelope-o"></i><a href="{{ 'mailto:' ~ person.email }}">
{{ person.email }}

View File

@ -0,0 +1,26 @@
{% extends '@ChillPerson/Household/layout.html.twig' %}
{% block title 'household_composition.create'|trans %}
{% block block_post_menu %}
<div class="post-menu"></div>
{% endblock %}
{% block content %}
<h1>{{ 'household_composition.Create'|trans }}</h1>
<div>
{{ form_start(form) }}
{{ form_widget(form) }}
<ul class="record_actions">
<li>
<button type="submit" class="btn btn-create">{{ 'Save'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
</div>
{% endblock %}

View File

@ -15,7 +15,7 @@
{% else %}
<div class="flex-table">
{% for c in compositions %}
{% if c.id != editId %}
{# {% if c.id != editId %} #}
<div class="item-bloc">
<div class="item-row">
<div class="item-col">
@ -42,7 +42,7 @@
<div class="item-row">
<ul class="record_actions">
<li>
<a href="{{ path('chill_person_household_composition_index', {'id': c.household.id, 'edit': c.id}) }}" class="btn btn-edit"></a>
<a href="{{ path('chill_person_household_composition_new', {'id': c.household.id, 'edit': c.id}) }}" class="btn btn-edit"></a>
</li>
<li>
<a href="{{ chill_path_add_return_path('chill_person_household_composition_delete', {'composition_id': c.id,
@ -54,45 +54,17 @@
</div>
{% endif %}
</div>
{% else %}
{{ form_start(form) }}
{{ form_widget(form) }}
<ul class="record_actions">
<li class="cancel" style="margin-right: auto;">
<a class="btn btn-cancel" href="{{ path('chill_person_household_composition_index', {'id': c.household.id}) }}">{{ 'Cancel'|trans }}</a>
</li>
<li>
<button type="submit" class="btn btn-create">{{ 'Save'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
{% endif %}
{% endfor %}
</div>
{% endif %}
<div id="collapseForm" class="{% if not isPosted %}collapse{% endif %}">
{{ form_start(form) }}
{{ form_widget(form) }}
<ul class="record_actions">
<li>
<button type="submit" class="btn btn-create">{{ 'Save'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
</div>
{% if editId == -1 %}
{% if is_granted('CHILL_PERSON_HOUSEHOLD_EDIT', household) %}
<ul class="record_actions sticky-form-buttons">
<li>
<button class="btn btn-primary btn-create change-icon" type="button" data-bs-toggle="collapse" data-bs-target="#collapseForm" aria-expanded="false" aria-controls="collapseForm">
{{ 'Create'|trans }}
</button>
</li>
<li>
<a href="{{ chill_path_add_return_path('chill_person_household_composition_new', {'id': household.id,}) }}"
class="btn btn-new"
title="{{ 'Create'|trans }}">{{ 'Create'|trans }}</a>
</li>
</ul>
{% endif %}
</div>

View File

@ -58,6 +58,13 @@
}) }}
</span>
{%- endif -%}
{% if person.getCurrentHousehold is not null %}
<span>
<a class="household-link" href="{{ chill_path_add_return_path('chill_person_household_summary', { 'household_id' : person.getCurrentHousehold.id } ) }}">
<i class="fa fa-home"></i>
</a>
</span>
{% endif %}
</div>
</div>

View File

@ -1,15 +1,25 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_person_resource_list' %}
{% block title %}{{ 'Person resources'|trans|capitalize ~ ' ' ~ person|chill_entity_render_string }}{% endblock %}
{% block js %}
{{ encore_entry_script_tags('page_person_resource_showhide_input') }}
{{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('page_person_resource_showhide_input') }}
{{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %}
{% block personcontent %}
<h1 style="margin-bottom: 2rem;">{{ 'Add a person resource'|trans }}</h1>
<div class="col-md col-xxl">
<div id="collapseForm" class="{% if not form.vars.submitted %}collapse{% endif %}">
<h3 style="margin-bottom: 2rem;">{{ 'Add a person resource'|trans }}</h3>
{% include "@ChillPerson/PersonResource/form.html.twig" %}
</div>
<ul class="record_actions sticky-form-buttons">
<li>
<button class="btn btn-primary btn-create change-icon" type="button" data-bs-toggle="collapse" data-bs-target="#collapseForm" aria-expanded="false" aria-controls="collapseForm">
{{ 'Add a person resource'|trans }}
</button>
</li>
</ul>
</div>
</div>
{% endblock %}

View File

@ -85,8 +85,14 @@
<p class="chill-no-data-statement">{{ 'There are no available resources'|trans }}</p>
{% endif %}
<h1 style="margin-bottom: 2rem;">{{ 'Add a person resource'|trans }}</h1>
{% include "@ChillPerson/PersonResource/create.html.twig" %}
{% if is_granted('CHILL_PERSON_UPDATE', person) %}
<ul class="record_actions sticky-form-buttons">
<li>
<a href="{{ chill_path_add_return_path('chill_person_resource_new', {'person_id': person.id,}) }}"
class="btn btn-new"
title="{{ 'Create'|trans }}">{{ 'Create'|trans }}</a>
</li>
</ul>
{% endif %}
{% endblock %}

View File

@ -49,7 +49,12 @@
<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.hostPerson|chill_entity_render_box }}</span>
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
action: 'show', displayBadge: true,
targetEntity: { name: 'person', id: a.hostPerson.id },
buttonText: a.hostPerson|chill_entity_render_string,
isDead: a.hostPerson.deathdate is not null
} %}
</li>
<li>
{% set address_date = date(a.startDate|date("m/d/Y")) %}
@ -62,7 +67,12 @@
<li>
<i class="fa fa-li fa-home"></i>
<span class="item-key">{{ "Address of"|trans}}</span>
<span class="chill-entity entity-person badge-thirdparty">{{ a.hostThirdParty|chill_entity_render_box }}</span>
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
action: 'show', displayBadge: true,
targetEntity: { name: 'thirdparty', id: a.hostThirdParty.id },
buttonText: a.hostThirdParty|chill_entity_render_string,
isDead: false,
} %}
</li>
<li>
{% if a.hostThirdParty.address is not null %}

View File

@ -24,6 +24,8 @@ class AccompanyingPeriodWorkVoter extends Voter
{
public const CREATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_CREATE';
public const DELETE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_DELETE';
public const SEE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_SEE';
public const UPDATE = 'CHILL_MAIN_ACCOMPANYING_PERIOD_WORK_UPDATE';
@ -60,6 +62,7 @@ class AccompanyingPeriodWorkVoter extends Voter
case self::CREATE:
case self::UPDATE:
case self::DELETE:
return $this->security->isGranted(AccompanyingPeriodVoter::EDIT, $subject->getAccompanyingPeriod());
default:
@ -86,6 +89,6 @@ class AccompanyingPeriodWorkVoter extends Voter
private function getRoles(): array
{
return [self::SEE, self::CREATE, self::UPDATE];
return [self::SEE, self::CREATE, self::UPDATE, self::DELETE];
}
}

View File

@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
@ -46,7 +47,10 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements ContextAwareNormaliz
{
$initial = $this->normalizer->normalize($object, $format, array_merge(
$context,
[self::IGNORE_EVALUATION => spl_object_hash($object)]
[self::IGNORE_EVALUATION => spl_object_hash($object)],
[AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => static function ($object, $format, $context) {
return $object->getId();
}]
));
// due to bug: https://api-platform.com/docs/core/serialization/#collection-relation

View File

@ -179,7 +179,8 @@ class AccompanyingPeriodWorkEvaluationContext implements
$doc = new AccompanyingPeriodWorkEvaluationDocument();
$doc->setStoredObject($storedObject)
->setAccompanyingPeriodWorkEvaluation($entity)
->setTemplate($template);
->setTemplate($template)
->setTitle($this->translatableStringHelper->localize($template->getName()));
$this->em->persist($doc);
}
}

View File

@ -37,6 +37,13 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
$this->translator = $translator;
}
public function getDeletionRoles(): array
{
return [
'_',
];
}
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$doc = $this->getRelatedEntity($entityWorkflow);
@ -63,6 +70,18 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingPeriodWorkEvaluationDocument $object
*
* @return array[]
*/
public function getRelatedObjects(object $object): array
{
return [
['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, $object->getId()],
];
}
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return AccompanyingPeriodWorkEvaluationDocumentVoter::SEE;
@ -84,6 +103,11 @@ class AccompanyingPeriodWorkEvaluationDocumentWorkflowHandler implements EntityW
];
}
public function isObjectSupported(object $object): bool
{
return $object instanceof AccompanyingPeriodWorkEvaluationDocument;
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWorkEvaluationDocument::class;

View File

@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationVoter;
use Symfony\Contracts\Translation\TranslatorInterface;
@ -37,6 +38,11 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
$this->translator = $translator;
}
public function getDeletionRoles(): array
{
return ['_'];
}
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
$evaluation = $this->getRelatedEntity($entityWorkflow);
@ -61,6 +67,21 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingPeriodWorkEvaluation $object
*/
public function getRelatedObjects(object $object): array
{
$relateds = [];
$relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluation::class, 'entityId' => $object->getId()];
foreach ($object->getDocuments() as $doc) {
$relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, 'entityId' => $doc->getId()];
}
return $relateds;
}
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return AccompanyingPeriodWorkEvaluationVoter::SEE;
@ -79,6 +100,11 @@ class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowH
];
}
public function isObjectSupported(object $object): bool
{
return $object instanceof AccompanyingPeriodWorkEvaluation;
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWorkEvaluation::class;

View File

@ -15,7 +15,10 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface
@ -36,6 +39,11 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
$this->translator = $translator;
}
public function getDeletionRoles(): array
{
return [AccompanyingPeriodWorkVoter::DELETE];
}
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
{
return [
@ -58,6 +66,25 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
return $this->repository->find($entityWorkflow->getRelatedEntityId());
}
/**
* @param AccompanyingPeriodWork $object
*/
public function getRelatedObjects(object $object): array
{
$relateds = [];
$relateds[] = ['entityClass' => AccompanyingPeriodWork::class, 'entityId' => $object->getId()];
foreach ($object->getAccompanyingPeriodWorkEvaluations() as $evaluation) {
$relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluation::class, 'entityId' => $evaluation->getId()];
foreach ($evaluation->getDocuments() as $doc) {
$relateds[] = ['entityClass' => AccompanyingPeriodWorkEvaluationDocument::class, 'entityId' => $doc->getId()];
}
}
return $relateds;
}
public function getRoleShow(EntityWorkflow $entityWorkflow): ?string
{
return null;
@ -76,6 +103,11 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
];
}
public function isObjectSupported(object $object): bool
{
return $object instanceof AccompanyingPeriodWork;
}
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool
{
return $entityWorkflow->getRelatedEntityClass() === AccompanyingPeriodWork::class;

View File

@ -59,6 +59,10 @@ chill_person_resource_list:
path: /{_locale}/person/{person_id}/resources/list
controller: Chill\PersonBundle\Controller\PersonResourceController::listAction
chill_person_resource_new:
path: /{_locale}/person/{person_id}/resources/new
controller: Chill\PersonBundle\Controller\PersonResourceController::newAction
chill_person_resource_edit:
path: /{_locale}/person/{person_id}/resources/{resource_id}/edit
controller: Chill\PersonBundle\Controller\PersonResourceController::editAction

View File

@ -0,0 +1,37 @@
<?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;
/**
* Add url to SocialWork Evaluation.
*/
final class Version20220303113855 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_social_work_evaluation DROP url');
}
public function getDescription(): string
{
return 'Add url to SocialWork Evaluation';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_social_work_evaluation ADD url TEXT DEFAULT NULL');
$this->addSql("UPDATE chill_person_social_work_evaluation SET url = CASE WHEN title->>'fr' LIKE 'http%' THEN title->>'fr' ELSE null END;");
}
}

View File

@ -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\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Change constraint on pinnedComment in Accompanying period.
*/
final class Version20220310124318 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_accompanying_period DROP CONSTRAINT fk_e260a868b0804e90');
$this->addSql('ALTER TABLE chill_person_accompanying_period ADD CONSTRAINT fk_e260a868b0804e90 FOREIGN KEY (pinnedcomment_id) REFERENCES chill_person_accompanying_period_comment (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}
public function getDescription(): string
{
return 'Change constraint on pinnedComment in Accompanying period';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_person_accompanying_period DROP CONSTRAINT FK_E260A868B0804E90');
$this->addSql('ALTER TABLE chill_person_accompanying_period ADD CONSTRAINT FK_E260A868B0804E90 FOREIGN KEY (pinnedComment_id) REFERENCES chill_person_accompanying_period_comment (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE');
}
}

View File

@ -124,8 +124,8 @@ address_country_code: Code pays
'Alreay existing person': 'Dossiers déjà encodés'
'Add the person': 'Ajouter la personne'
'Add the person and create an accompanying period': "Créer la personne & créer une période d'accompagnement"
'Add the person and create a household': "Créer la personne & créer un ménage"
'Add the person and create an accompanying period': "Créer l'usager ET créer une période d'accompagnement"
'Add the person and create a household': "Créer l'usager' ET créer un ménage"
Show person: Voir le dossier de la personne
'Confirm the creation': 'Confirmer la création'
'You will create this person': 'Vous allez créer le dossier suivant'
@ -566,6 +566,7 @@ household_composition:
Currently no composition: Aucune composition familiale renseignée.
Add a composition: Ajouter une composition familiale
Update composition: Modifier la composition familiale
Create: Créér une nouvelle composition familiale
# docgen
Linked evaluations: Évaluations associées

View File

@ -46,6 +46,7 @@ User: Utilisateur
"Associated tasks": "Tâches associées"
"My tasks": "Mes tâches"
"Tasks for this accompanying period": "Tâches pour ce parcours d'accompagnement"
"Tasks for {{ name }}": "Tâches pour {{ name }}"
"No description": "Pas de description"
"No dates specified": "Dates non spécifiées"
"No one assignee": "Aucune personne assignée"

View File

@ -20,6 +20,7 @@ use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Chill\ThirdPartyBundle\Repository\ThirdPartyACLAwareRepositoryInterface;
use Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter;
use LogicException;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
@ -31,37 +32,39 @@ use function array_merge;
final class ThirdPartyController extends CRUDController
{
/**
* @var AuthorizationHelper
*/
protected $authorizationHelper;
protected AuthorizationHelper $authorizationHelper;
/**
* @var PaginatorFactory
*/
protected $paginatorFactory;
protected PaginatorFactory $paginatorFactory;
protected RequestStack $requestStack;
protected ThirdPartyACLAwareRepositoryInterface $thirdPartyACLAwareRepository;
/**
* @var TranslatorInterface
*/
protected $translator;
protected TranslatorInterface $translator;
private bool $askCenter;
public function __construct(
AuthorizationHelper $authorizationHelper,
TranslatorInterface $translator,
PaginatorFactory $paginatorFactory,
RequestStack $requestStack,
ThirdPartyACLAwareRepositoryInterface $thirdPartyACLAwareRepository
ThirdPartyACLAwareRepositoryInterface $thirdPartyACLAwareRepository,
ParameterBagInterface $parameterBag
) {
$this->authorizationHelper = $authorizationHelper;
$this->translator = $translator;
$this->paginatorFactory = $paginatorFactory;
$this->requestStack = $requestStack;
$this->thirdPartyACLAwareRepository = $thirdPartyACLAwareRepository;
$this->askCenter = $parameterBag->get('chill_main')['acl']['form_show_centers'];
}
public function generateTemplateParameter(string $action, $entity, Request $request, array $defaultTemplateParameters = [])
{
$defaultTemplateParameters['askCenter'] = $this->askCenter;
return $defaultTemplateParameters;
}
protected function buildFilterOrderHelper(string $action, Request $request): ?FilterOrderHelper

View File

@ -36,7 +36,7 @@
<ul class="list-content fa-ul">
<li v-if="getProfession.length > 0">
<i class="fa fa-li fa-id-card"></i>
<p><span v-for="p in getProfession" :key="p" class="list-item">{{ p[0].toUpperCase() + p.slice(1).toLowerCase() }}</span></p>
<p><span v-for="p in getProfession" :key="p" class="list-item list-professions">{{ p[0].toUpperCase() + p.slice(1).toLowerCase() }}</span></p>
</li>
<li v-if="hasParent">
<i class="fa fa-li fa-hand-o-right"></i>
@ -61,9 +61,9 @@
<i class="fa fa-li fa-map-marker"></i>
<address-render-box :address="thirdparty.address" :isMultiline="isMultiline"></address-render-box>
</li>
<li v-if="thirdparty.phonenumber">
<li v-if="thirdparty.telephone">
<i class="fa fa-li fa-mobile"></i>
<a :href="'tel: ' + thirdparty.phonenumber">{{ thirdparty.phonenumber }}</a>
<a :href="'tel: ' + thirdparty.telephone">{{ thirdparty.telephone }}</a>
</li>
<li v-if="thirdparty.email">
<i class="fa fa-li fa-envelope-o"></i>
@ -78,9 +78,9 @@
<i class="fa fa-li fa-map-marker"></i>
<address-render-box :address="thirdparty.address" :isMultiline="isMultiline"></address-render-box>
</li>
<li v-if="thirdparty.phonenumber">
<li v-if="thirdparty.telephone">
<i class="fa fa-li fa-mobile"></i>
<a :href="'tel: ' + thirdparty.phonenumber">{{ thirdparty.phonenumber }}</a>
<a :href="'tel: ' + thirdparty.telephone">{{ thirdparty.telephone }}</a>
</li>
<li v-if="thirdparty.email">
<i class="fa fa-li fa-envelope-o"></i>
@ -165,7 +165,7 @@ export default {
}
}
.list-item {
.list-professions {
&::after {
content: " | ";
}

View File

@ -118,7 +118,7 @@
<div class="input-group mb-3">
<span class="input-group-text" id="phonenumber"><i class="fa fa-fw fa-phone"></i></span>
<input class="form-control form-control-lg"
v-model="thirdparty.phonenumber"
v-model="thirdparty.telephone"
v-bind:placeholder="$t('thirdparty.phonenumber')"
v-bind:aria-label="$t('thirdparty.phonenumber')"
aria-describedby="phonenumber" />

View File

@ -13,7 +13,7 @@
<h1>
{{ title_ }}
<span class="badge bg-{{ thirdParty.active ? 'success' : 'danger' }} float-end"
title="{{ (thirdParty.active ? 'shown to users' : 'not shown to users')|trans }}">
title="{{ (thirdParty.active ? 'shown to users' : 'not shown to users')|trans }}">
{{ (thirdParty.active ? 'Active' : 'Inactive')|trans }}
</span>
</h1>
@ -123,19 +123,20 @@
</dd>
{% endif %}
<dt>{{ 'Centers'|trans }}</dt>
<dd>
{% set centers = thirdParty|chill_resolve_center %}
{% if centers is iterable %}
{{ 'The party is visible in those centers'|trans }}&nbsp;:
{{ centers|join(', ') }}
{% elseif centers is null %}
{{ 'The party is not visible in any center'|trans }}
{% else %}
{{ 'The party is visible in those centers'|trans }}&nbsp;: {{ centers }}
{% endif %}
</dd>
{% if askCenter %}
<dt>{{ 'Centers'|trans }}</dt>
<dd>
{% set centers = thirdParty|chill_resolve_center %}
{% if centers is iterable %}
{{ 'The party is visible in those centers'|trans }}&nbsp;:
{{ centers|join(', ') }}
{% elseif centers is null %}
{{ 'The party is not visible in any center'|trans }}
{% else %}
{{ 'The party is visible in those centers'|trans }}&nbsp;: {{ centers }}
{% endif %}
</dd>
{% endif %}
</dl>
{% endblock %}
{% block content_form_actions_delete %}{% endblock %}

View File

@ -62,7 +62,7 @@ class ThirdPartyNormalizer implements NormalizerAwareInterface, NormalizerInterf
}, $thirdParty->getTypesAndCategories()),
'profession' => $this->normalizer->normalize($thirdParty->getProfession(), $format, $context),
'address' => $this->normalizer->normalize($thirdParty->getAddress(), $format, ['address_rendering' => 'short']),
'phonenumber' => $this->normalizer->normalize($thirdParty->getTelephone()),
'telephone' => $this->normalizer->normalize($thirdParty->getTelephone()),
'email' => $thirdParty->getEmail(),
'isChild' => $thirdParty->isChild(),
'parent' => $this->normalizer->normalize($thirdParty->getParent(), $format, $context),