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 upgrade-php82
This commit is contained in:
commit
4dbb195b45
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@ composer.phar
|
|||||||
composer.lock
|
composer.lock
|
||||||
docs/build/
|
docs/build/
|
||||||
node_modules/*
|
node_modules/*
|
||||||
|
.php_cs.cache
|
||||||
.cache/*
|
.cache/*
|
||||||
|
|
||||||
###> symfony/framework-bundle ###
|
###> symfony/framework-bundle ###
|
||||||
|
@ -61,7 +61,7 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
|
|||||||
->setEmail('centreA@test.chill.social')
|
->setEmail('centreA@test.chill.social')
|
||||||
->setLocationType($type = new LocationType())
|
->setLocationType($type = new LocationType())
|
||||||
->setPhonenumber1(PhoneNumberUtil::getInstance()->parse('+3287653812'));
|
->setPhonenumber1(PhoneNumberUtil::getInstance()->parse('+3287653812'));
|
||||||
$type->setTitle('Service');
|
$type->setTitle(['fr' => 'Service']);
|
||||||
$address->setStreet('Rue des Épaules')->setStreetNumber('14')
|
$address->setStreet('Rue des Épaules')->setStreetNumber('14')
|
||||||
->setPostcode($postCode = new PostalCode());
|
->setPostcode($postCode = new PostalCode());
|
||||||
$postCode->setCode('4145')->setName('Houte-Si-Plout')->setCountry(
|
$postCode->setCode('4145')->setName('Houte-Si-Plout')->setCountry(
|
||||||
|
@ -12,6 +12,8 @@ declare(strict_types=1);
|
|||||||
namespace Chill\CalendarBundle\DataFixtures\ORM;
|
namespace Chill\CalendarBundle\DataFixtures\ORM;
|
||||||
|
|
||||||
use Chill\CalendarBundle\Entity\Invite;
|
use Chill\CalendarBundle\Entity\Invite;
|
||||||
|
use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||||
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
|
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
|
||||||
use Doctrine\Persistence\ObjectManager;
|
use Doctrine\Persistence\ObjectManager;
|
||||||
@ -33,14 +35,21 @@ class LoadInvite extends Fixture implements FixtureGroupInterface
|
|||||||
public function load(ObjectManager $manager): void
|
public function load(ObjectManager $manager): void
|
||||||
{
|
{
|
||||||
$arr = [
|
$arr = [
|
||||||
['name' => ['fr' => 'Rendez-vous décliné']],
|
[
|
||||||
['name' => ['fr' => 'Rendez-vous accepté']],
|
'name' => ['fr' => 'Rendez-vous décliné'],
|
||||||
|
'status' => Invite::DECLINED,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => ['fr' => 'Rendez-vous accepté'],
|
||||||
|
'status' => Invite::ACCEPTED,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($arr as $a) {
|
foreach ($arr as $a) {
|
||||||
echo 'Creating calendar invite : ' . $a['name']['fr'] . "\n";
|
echo 'Creating calendar invite : ' . $a['name']['fr'] . "\n";
|
||||||
$invite = (new Invite())
|
$invite = (new Invite())
|
||||||
->setStatus($a['name']);
|
->setStatus($a['status'])
|
||||||
|
->setUser($this->getRandomUser());
|
||||||
$manager->persist($invite);
|
$manager->persist($invite);
|
||||||
$reference = 'Invite_' . $a['name']['fr'];
|
$reference = 'Invite_' . $a['name']['fr'];
|
||||||
$this->addReference($reference, $invite);
|
$this->addReference($reference, $invite);
|
||||||
@ -49,4 +58,11 @@ class LoadInvite extends Fixture implements FixtureGroupInterface
|
|||||||
|
|
||||||
$manager->flush();
|
$manager->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getRandomUser(): User
|
||||||
|
{
|
||||||
|
$userRef = array_rand(LoadUsers::$refs);
|
||||||
|
|
||||||
|
return $this->getReference($userRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,30 +17,20 @@
|
|||||||
<td class="eval">
|
<td class="eval">
|
||||||
<ul class="eval_title">
|
<ul class="eval_title">
|
||||||
<li>
|
<li>
|
||||||
{{ mm.mimeIcon(d.storedObject.type) }}
|
<div class="row">
|
||||||
{{ d.storedObject.title }}
|
<div class="col text-start">
|
||||||
{% if d.dateTimeVersion < d.calendar.dateTimeVersion %}
|
{{ d.storedObject.title }}
|
||||||
<span class="badge bg-danger">{{ 'chill_calendar.Document outdated'|trans }}</span>
|
{% if d.dateTimeVersion < d.calendar.dateTimeVersion %}
|
||||||
{% endif %}
|
<span class="badge bg-danger">{{ 'chill_calendar.Document outdated'|trans }}</span>
|
||||||
|
{% endif %}
|
||||||
<ul class="record_actions small inline">
|
</div>
|
||||||
{% if chill_document_is_editable(d.storedObject) and is_granted('CHILL_CALENDAR_DOC_EDIT', d) %}
|
<div class="col-md-auto text-center">
|
||||||
<li>
|
{{ mm.mimeIcon(d.storedObject.type) }}
|
||||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendardoc_delete', {'id': d.id})}}" class="btn btn-delete"></a>
|
</div>
|
||||||
</li>
|
<div class="col col-lg-4 text-end">
|
||||||
<li>
|
{{ d.storedObject|chill_document_button_group(d.storedObject.title, is_granted('CHILL_CALENDAR_DOC_EDIT', d), {'small': true}) }}
|
||||||
{{ d.storedObject|chill_document_edit_button }}
|
</div>
|
||||||
</li>
|
</div>
|
||||||
{% endif %}
|
|
||||||
{% if is_granted('CHILL_CALENDAR_DOC_EDIT', d) %}
|
|
||||||
<li>
|
|
||||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendardoc_edit', {'id': d.id})}}" class="btn btn-edit"></a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
<li>
|
|
||||||
{{ m.download_button(d.storedObject, d.storedObject.title) }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ encore_entry_script_tags('mod_answer') }}
|
{{ encore_entry_script_tags('mod_answer') }}
|
||||||
{{ encore_entry_script_tags('mod_async_upload') }}
|
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ encore_entry_link_tags('mod_answer') }}
|
{{ encore_entry_link_tags('mod_answer') }}
|
||||||
{{ encore_entry_link_tags('mod_async_upload') }}
|
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
@ -240,6 +240,7 @@ final class CalendarContext implements CalendarContextInterface
|
|||||||
|
|
||||||
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
|
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
|
||||||
{
|
{
|
||||||
|
$normalized = [];
|
||||||
$normalized['title'] = $data['title'] ?? '';
|
$normalized['title'] = $data['title'] ?? '';
|
||||||
|
|
||||||
foreach (['mainPerson', 'thirdParty'] as $k) {
|
foreach (['mainPerson', 'thirdParty'] as $k) {
|
||||||
@ -253,6 +254,7 @@ final class CalendarContext implements CalendarContextInterface
|
|||||||
|
|
||||||
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
|
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
|
||||||
{
|
{
|
||||||
|
$denormalized = [];
|
||||||
$denormalized['title'] = $data['title'];
|
$denormalized['title'] = $data['title'];
|
||||||
|
|
||||||
if (null !== ($data['mainPerson'] ?? null)) {
|
if (null !== ($data['mainPerson'] ?? null)) {
|
||||||
|
@ -16,14 +16,14 @@ window.addEventListener('DOMContentLoaded', function (e) {
|
|||||||
filename: string,
|
filename: string,
|
||||||
canEdit: string,
|
canEdit: string,
|
||||||
storedObject: string,
|
storedObject: string,
|
||||||
small: string,
|
buttonSmall: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
const
|
const
|
||||||
storedObject = JSON.parse(datasets.storedObject) as StoredObject,
|
storedObject = JSON.parse(datasets.storedObject) as StoredObject,
|
||||||
filename = datasets.filename,
|
filename = datasets.filename,
|
||||||
canEdit = datasets.canEdit === '1',
|
canEdit = datasets.canEdit === '1',
|
||||||
small = datasets.small === '1'
|
small = datasets.buttonSmall === '1'
|
||||||
;
|
;
|
||||||
|
|
||||||
return { storedObject, filename, canEdit, small };
|
return { storedObject, filename, canEdit, small };
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="'ready' === props.storedObject.status" class="dropdown">
|
<div v-if="'ready' === props.storedObject.status" class="dropdown">
|
||||||
<button :class="Object.assign({'btn': true, 'btn-outline-primary': true, 'dropdown-toggle': true, small: props.small})" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button :class="Object.assign({'btn': true, 'btn-outline-primary': true, 'dropdown-toggle': true, 'btn-sm': props.small})" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
Actions
|
Actions
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
@ -157,7 +157,7 @@ final class WopiEditTwigExtensionRuntime implements RuntimeExtensionInterface
|
|||||||
'document_json' => $this->normalizer->normalize($document, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
'document_json' => $this->normalizer->normalize($document, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'can_edit' => $canEdit,
|
'can_edit' => $canEdit,
|
||||||
'options' => array_merge($options, self::DEFAULT_OPTIONS_TEMPLATE_BUTTON_GROUP),
|
'options' => array_merge(self::DEFAULT_OPTIONS_TEMPLATE_BUTTON_GROUP, $options),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
65
src/Bundle/ChillMainBundle/Controller/AbsenceController.php
Normal file
65
src/Bundle/ChillMainBundle/Controller/AbsenceController.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Form\AbsenceType;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
class AbsenceController extends AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Route(
|
||||||
|
* "/{_locale}/absence",
|
||||||
|
* name="chill_main_user_absence_index",
|
||||||
|
* methods={"GET", "POST"}
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function setAbsence(Request $request)
|
||||||
|
{
|
||||||
|
$user = $this->getUser();
|
||||||
|
$form = $this->createForm(AbsenceType::class, $user);
|
||||||
|
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->redirect($this->generateUrl('chill_main_user_absence_index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('@ChillMain/Menu/absence.html.twig', [
|
||||||
|
'user' => $user,
|
||||||
|
'form' => $form->createView(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route(
|
||||||
|
* "/{_locale}/absence/unset",
|
||||||
|
* name="chill_main_user_absence_unset",
|
||||||
|
* methods={"GET", "POST"}
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function unsetAbsence(Request $request)
|
||||||
|
{
|
||||||
|
$user = $this->getUser();
|
||||||
|
|
||||||
|
$user->setAbsenceStart(null);
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->redirect($this->generateUrl('chill_main_user_absence_index'));
|
||||||
|
}
|
||||||
|
}
|
@ -298,6 +298,8 @@ class ExportController extends AbstractController
|
|||||||
'csrf_protection' => $isGenerate ? false : true,
|
'csrf_protection' => $isGenerate ? false : true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// TODO: add a condition to be able to select a regroupment of centers?
|
||||||
|
|
||||||
if ('centers' === $step || 'generate_centers' === $step) {
|
if ('centers' === $step || 'generate_centers' === $step) {
|
||||||
$builder->add('centers', PickCenterType::class, [
|
$builder->add('centers', PickCenterType::class, [
|
||||||
'export_alias' => $alias,
|
'export_alias' => $alias,
|
||||||
|
@ -24,7 +24,7 @@ class Regroupment
|
|||||||
/**
|
/**
|
||||||
* @var Center
|
* @var Center
|
||||||
* @ORM\ManyToMany(
|
* @ORM\ManyToMany(
|
||||||
* targetEntity="Chill\MainBundle\Entity\Center"
|
* targetEntity=Center::class
|
||||||
* )
|
* )
|
||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
*/
|
*/
|
||||||
@ -43,7 +43,7 @@ class Regroupment
|
|||||||
private bool $isActive = true;
|
private bool $isActive = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="string", length=15, options={"default": ""}, nullable=false)
|
* @ORM\Column(type="text", options={"default": ""}, nullable=false)
|
||||||
*/
|
*/
|
||||||
private string $name = '';
|
private string $name = '';
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ class Regroupment
|
|||||||
$this->centers = new ArrayCollection();
|
$this->centers = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCenters(): ?Collection
|
public function getCenters(): Collection
|
||||||
{
|
{
|
||||||
return $this->centers;
|
return $this->centers;
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Entity;
|
namespace Chill\MainBundle\Entity;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use function in_array;
|
use function in_array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,6 +41,11 @@ class User implements UserInterface
|
|||||||
*/
|
*/
|
||||||
protected ?int $id = null;
|
protected ?int $id = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="datetime_immutable", nullable=true)
|
||||||
|
*/
|
||||||
|
private ?DateTimeImmutable $absenceStart = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array where SAML attributes's data are stored.
|
* Array where SAML attributes's data are stored.
|
||||||
*
|
*
|
||||||
@ -173,6 +179,11 @@ class User implements UserInterface
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAbsenceStart(): ?DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->absenceStart;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get attributes.
|
* Get attributes.
|
||||||
*
|
*
|
||||||
@ -291,6 +302,11 @@ class User implements UserInterface
|
|||||||
return $this->usernameCanonical;
|
return $this->usernameCanonical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isAbsent(): bool
|
||||||
|
{
|
||||||
|
return null !== $this->getAbsenceStart() && $this->getAbsenceStart() <= new DateTimeImmutable('now');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@ -355,6 +371,11 @@ class User implements UserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setAbsenceStart(?DateTimeImmutable $absenceStart): void
|
||||||
|
{
|
||||||
|
$this->absenceStart = $absenceStart;
|
||||||
|
}
|
||||||
|
|
||||||
public function setAttributeByDomain(string $domain, string $key, $value): self
|
public function setAttributeByDomain(string $domain, string $key, $value): self
|
||||||
{
|
{
|
||||||
$this->attributes[$domain][$key] = $value;
|
$this->attributes[$domain][$key] = $value;
|
||||||
|
38
src/Bundle/ChillMainBundle/Form/AbsenceType.php
Normal file
38
src/Bundle/ChillMainBundle/Form/AbsenceType.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Form;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class AbsenceType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('absenceStart', ChillDateType::class, [
|
||||||
|
'required' => true,
|
||||||
|
'input' => 'datetime_immutable',
|
||||||
|
'label' => 'absence.Absence start',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => User::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Form\DataMapper;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
use Chill\MainBundle\Entity\Regroupment;
|
||||||
|
use Chill\MainBundle\Repository\RegroupmentRepository;
|
||||||
|
use Exception;
|
||||||
|
use Symfony\Component\Form\DataMapperInterface;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use function count;
|
||||||
|
|
||||||
|
class ExportPickCenterDataMapper implements DataMapperInterface
|
||||||
|
{
|
||||||
|
protected RegroupmentRepository $regroupmentRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|Center[] $data
|
||||||
|
* @param $forms
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function mapDataToForms($data, $forms)
|
||||||
|
{
|
||||||
|
if (null === $data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var array<string, FormInterface> $form */
|
||||||
|
$form = iterator_to_array($forms);
|
||||||
|
|
||||||
|
$pickedRegroupment = [];
|
||||||
|
|
||||||
|
foreach ($this->regroupmentRepository->findAll() as $regroupment) {
|
||||||
|
[$contained, $notContained] = $regroupment->getCenters()->partition(static function (Center $center) {
|
||||||
|
});
|
||||||
|
|
||||||
|
if (0 === count($notContained)) {
|
||||||
|
$pickedRegroupment[] = $regroupment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$form['regroupment']->setData($pickedRegroupment);
|
||||||
|
$form['centers']->setData($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param iterable $forms
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function mapFormsToData($forms, &$data)
|
||||||
|
{
|
||||||
|
/** @var array<string, FormInterface> $forms */
|
||||||
|
$forms = iterator_to_array($forms);
|
||||||
|
|
||||||
|
$centers = [];
|
||||||
|
|
||||||
|
foreach ($forms['center']->getData() as $center) {
|
||||||
|
$centers[spl_object_hash($center)] = $center;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($forms['regroupment']->getData() as $regroupment) {
|
||||||
|
/** @var Regroupment $regroupment */
|
||||||
|
foreach ($regroupment->getCenters() as $center) {
|
||||||
|
$centers[spl_object_hash($center)] = $center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array_values($centers);
|
||||||
|
}
|
||||||
|
}
|
@ -13,55 +13,45 @@ namespace Chill\MainBundle\Form\Type\Export;
|
|||||||
|
|
||||||
use Chill\MainBundle\Center\GroupingCenterInterface;
|
use Chill\MainBundle\Center\GroupingCenterInterface;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
use Chill\MainBundle\Entity\Regroupment;
|
||||||
use Chill\MainBundle\Export\ExportManager;
|
use Chill\MainBundle\Export\ExportManager;
|
||||||
|
use Chill\MainBundle\Form\DataMapper\ExportPickCenterDataMapper;
|
||||||
|
use Chill\MainBundle\Repository\RegroupmentRepository;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\CallbackTransformer;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
|
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use function array_intersect;
|
|
||||||
use function array_key_exists;
|
|
||||||
use function array_merge;
|
|
||||||
use function array_unique;
|
|
||||||
use function count;
|
use function count;
|
||||||
use function in_array;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pick centers amongst available centers for the user.
|
* Pick centers amongst available centers for the user.
|
||||||
*/
|
*/
|
||||||
class PickCenterType extends AbstractType
|
final class PickCenterType extends AbstractType
|
||||||
{
|
{
|
||||||
public const CENTERS_IDENTIFIERS = 'c';
|
public const CENTERS_IDENTIFIERS = 'c';
|
||||||
|
|
||||||
protected AuthorizationHelperInterface $authorizationHelper;
|
private AuthorizationHelperInterface $authorizationHelper;
|
||||||
|
|
||||||
protected ExportManager $exportManager;
|
private ExportManager $exportManager;
|
||||||
|
|
||||||
/**
|
private RegroupmentRepository $regroupmentRepository;
|
||||||
* @var array|GroupingCenterInterface[]
|
|
||||||
*/
|
|
||||||
protected array $groupingCenters = [];
|
|
||||||
|
|
||||||
protected UserInterface $user;
|
private UserInterface $user;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TokenStorageInterface $tokenStorage,
|
TokenStorageInterface $tokenStorage,
|
||||||
ExportManager $exportManager,
|
ExportManager $exportManager,
|
||||||
|
RegroupmentRepository $regroupmentRepository,
|
||||||
AuthorizationHelperInterface $authorizationHelper
|
AuthorizationHelperInterface $authorizationHelper
|
||||||
) {
|
) {
|
||||||
$this->exportManager = $exportManager;
|
$this->exportManager = $exportManager;
|
||||||
$this->user = $tokenStorage->getToken()->getUser();
|
$this->user = $tokenStorage->getToken()->getUser();
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
$this->authorizationHelper = $authorizationHelper;
|
||||||
}
|
$this->regroupmentRepository = $regroupmentRepository;
|
||||||
|
|
||||||
public function addGroupingCenter(GroupingCenterInterface $grouping)
|
|
||||||
{
|
|
||||||
$this->groupingCenters[md5($grouping->getName())] = $grouping;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
@ -72,97 +62,33 @@ class PickCenterType extends AbstractType
|
|||||||
$export->requiredRole()
|
$export->requiredRole()
|
||||||
);
|
);
|
||||||
|
|
||||||
$builder->add(self::CENTERS_IDENTIFIERS, EntityType::class, [
|
$builder->add('center', EntityType::class, [
|
||||||
'class' => Center::class,
|
'class' => Center::class,
|
||||||
|
'label' => 'center',
|
||||||
'choices' => $centers,
|
'choices' => $centers,
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'choice_label' => static function (Center $c) {
|
'choice_label' => static function (Center $c) {
|
||||||
return $c->getName();
|
return $c->getName();
|
||||||
},
|
},
|
||||||
'data' => count($this->groupingCenters) > 0 ? null : $centers,
|
'data' => $centers,
|
||||||
]);
|
])
|
||||||
|
->add('regroupment', EntityType::class, [
|
||||||
if (count($this->groupingCenters) > 0) {
|
'class' => Regroupment::class,
|
||||||
$groupingBuilder = $builder->create('g', null, [
|
'label' => 'regroupment',
|
||||||
'compound' => true,
|
'multiple' => true,
|
||||||
|
'expanded' => true,
|
||||||
|
'choices' => $this->regroupmentRepository->findAllActive(),
|
||||||
|
'choice_label' => static function (Regroupment $r) {
|
||||||
|
return $r->getName();
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
foreach ($this->groupingCenters as $key => $gc) {
|
$builder->setDataMapper(new ExportPickCenterDataMapper());
|
||||||
$choices = $this->buildChoices($centers, $gc);
|
|
||||||
|
|
||||||
if (count($choices) > 0) {
|
|
||||||
$groupingBuilder->add($key, ChoiceType::class, [
|
|
||||||
'choices' => $choices,
|
|
||||||
'multiple' => true,
|
|
||||||
'expanded' => true,
|
|
||||||
'label' => $gc->getName(),
|
|
||||||
'required' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($groupingBuilder->count() > 0) {
|
|
||||||
$builder->add($groupingBuilder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder->addModelTransformer(new CallbackTransformer(
|
|
||||||
function ($data) use ($centers) {
|
|
||||||
return $this->transform($data, $centers);
|
|
||||||
},
|
|
||||||
function ($data) use ($centers) {
|
|
||||||
return $this->reverseTransform($data, $centers);
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
$resolver->setRequired('export_alias');
|
$resolver->setRequired('export_alias');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildChoices($reachablesCenters, GroupingCenterInterface $gc)
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($gc->getGroups() as $group) {
|
|
||||||
foreach ($gc->getCentersForGroup($group) as $center) {
|
|
||||||
if (in_array($center, $reachablesCenters, true)) {
|
|
||||||
$result[$group] = $group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function reverseTransform($data, $centers)
|
|
||||||
{
|
|
||||||
$picked = $data[self::CENTERS_IDENTIFIERS]
|
|
||||||
instanceof \Doctrine\Common\Collections\Collection ?
|
|
||||||
$data[self::CENTERS_IDENTIFIERS]->toArray()
|
|
||||||
:
|
|
||||||
$data[self::CENTERS_IDENTIFIERS];
|
|
||||||
|
|
||||||
if (array_key_exists('g', $data)) {
|
|
||||||
foreach ($data['g'] as $gcid => $group) {
|
|
||||||
$picked =
|
|
||||||
array_merge(
|
|
||||||
array_intersect(
|
|
||||||
$this->groupingCenters[$gcid]->getCentersForGroup($group),
|
|
||||||
$centers
|
|
||||||
),
|
|
||||||
$picked
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_unique($picked);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function transform($data, $centers)
|
|
||||||
{
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Center;
|
|||||||
use Chill\MainBundle\Entity\Location;
|
use Chill\MainBundle\Entity\Location;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Entity\UserJob;
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
use Chill\MainBundle\Form\Type\PickCivilityType;
|
use Chill\MainBundle\Form\Type\PickCivilityType;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
@ -110,6 +111,11 @@ class UserType extends AbstractType
|
|||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
},
|
},
|
||||||
|
])
|
||||||
|
->add('absenceStart', ChillDateType::class, [
|
||||||
|
'required' => false,
|
||||||
|
'input' => 'datetime_immutable',
|
||||||
|
'label' => 'absence.Absence start',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// @phpstan-ignore-next-line
|
// @phpstan-ignore-next-line
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Regroupment;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
|
final class RegroupmentRepository implements ObjectRepository
|
||||||
|
{
|
||||||
|
private EntityRepository $repository;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
|
{
|
||||||
|
$this->repository = $entityManager->getRepository(Regroupment::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id, $lockMode = null, $lockVersion = null): ?Regroupment
|
||||||
|
{
|
||||||
|
return $this->repository->find($id, $lockMode, $lockVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Regroupment[]
|
||||||
|
*/
|
||||||
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->repository->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAllActive(): array
|
||||||
|
{
|
||||||
|
return $this->repository->findBy(['isActive' => true], ['name' => 'ASC']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed|null $limit
|
||||||
|
* @param mixed|null $offset
|
||||||
|
*
|
||||||
|
* @return Regroupment[]
|
||||||
|
*/
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
|
||||||
|
{
|
||||||
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria, ?array $orderBy = null): ?Regroupment
|
||||||
|
{
|
||||||
|
return $this->repository->findOneBy($criteria, $orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassName()
|
||||||
|
{
|
||||||
|
return Regroupment::class;
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,7 @@ export interface User {
|
|||||||
id: number;
|
id: number;
|
||||||
username: string;
|
username: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
text_without_absence: string;
|
||||||
email: string;
|
email: string;
|
||||||
user_job: Job;
|
user_job: Job;
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<span class="chill-entity entity-user">
|
<span class="chill-entity entity-user">
|
||||||
{{ user.label }}
|
{{ user.label }}
|
||||||
<span class="user-job" v-if="user.user_job !== null">({{ user.user_job.label.fr }})</span>
|
<span class="user-job" v-if="user.user_job !== null">({{ user.user_job.label.fr }})</span> <span class="main-scope" v-if="user.main_scope !== null">({{ user.main_scope.name.fr }})</span> <span v-if="user.isAbsent" class="badge bg-danger rounded-pill" :title="Absent">A</span>
|
||||||
<span class="main-scope" v-if="user.main_scope !== null">({{ user.main_scope.name.fr }})</span>
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -6,4 +6,7 @@
|
|||||||
{%- if opts['main_scope'] and user.mainScope is not null %}
|
{%- if opts['main_scope'] and user.mainScope is not null %}
|
||||||
<span class="main-scope">({{ user.mainScope.name|localize_translatable_string }})</span>
|
<span class="main-scope">({{ user.mainScope.name|localize_translatable_string }})</span>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
{%- if opts['absence'] and user.isAbsent %}
|
||||||
|
<span class="badge bg-danger rounded-pill" title="{{ 'absence.Absent'|trans|escape('html_attr') }}">{{ 'absence.A'|trans }}</span>
|
||||||
|
{%- endif -%}
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{#
|
{#
|
||||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||||
<info@champs-libres.coop> / <http://www.champs-libres.coop>
|
<info@champs-libres.coop> / <http://www.champs-libres.coop>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -22,39 +22,33 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
|
|
||||||
{{ include('@ChillMain/Export/_breadcrumb.html.twig') }}
|
{{ include('@ChillMain/Export/_breadcrumb.html.twig') }}
|
||||||
|
|
||||||
<h1>{{ export.title|trans }}</h1>
|
<h1>{{ export.title|trans }}</h1>
|
||||||
|
|
||||||
<p>{{ export.description|trans }}</p>
|
<p>{{ export.description|trans }}</p>
|
||||||
|
|
||||||
{{ form_start(form) }}
|
{{ form_start(form) }}
|
||||||
|
|
||||||
<section class="center mb-4">
|
<section class="center mb-4">
|
||||||
|
|
||||||
<h2>{{ 'Pick centers'|trans }}</h2>
|
<h2>{{ 'Pick centers'|trans }}</h2>
|
||||||
|
|
||||||
<p>{{ 'The export will contains only data from the picked centers.'|trans }}
|
<p>{{ 'The export will contains only data from the picked centers.'|trans }}
|
||||||
{{ 'This will eventually restrict your possibilities in filtering the data.'|trans }}</p>
|
{{ 'This will eventually restrict your possibilities in filtering the data.'|trans }}</p>
|
||||||
|
|
||||||
{{ form_widget(form.centers.c) }}
|
<h3 class="m-3">{{ 'Center'|trans }}</h3>
|
||||||
|
{{ form_widget(form.centers.center) }}
|
||||||
{% if form.centers.children.g is defined %}
|
|
||||||
|
<h3 class="m-3">{{ 'Pick aggregated centers'|trans }}</h3>
|
||||||
<h3>{{ 'Pick aggregated centers'|trans }}</h3>
|
{{ form_widget(form.centers.regroupment) }}
|
||||||
|
|
||||||
{% for f in form.centers.children.g.children %}
|
|
||||||
{{ form_row(f) }}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<p>{{ form_widget(form.submit, { 'attr' : { 'class' : 'btn btn-action btn-create' }, 'label' : 'Go to export options' } ) }}</p>
|
<p>{{ form_widget(form.submit, { 'attr' : { 'class' : 'btn btn-action btn-create' }, 'label' : 'Go to export options' } ) }}</p>
|
||||||
|
|
||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<div class="col-10 mt-5">
|
<div class="col-10 mt-5">
|
||||||
|
|
||||||
{# vue component #}
|
{# vue component #}
|
||||||
<div id="homepage_widget"></div>
|
<div id="homepage_widget"></div>
|
||||||
|
|
||||||
{% include '@ChillMain/Homepage/fast_actions.html.twig' %}
|
{% include '@ChillMain/Homepage/fast_actions.html.twig' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ encore_entry_link_tags('page_homepage_widget') }}
|
{{ encore_entry_link_tags('page_homepage_widget') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ encore_entry_script_tags('page_homepage_widget') }}
|
{{ encore_entry_script_tags('page_homepage_widget') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
{% extends '@ChillMain/Admin/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{{ 'absence.My absence'|trans }}
|
||||||
|
{% endblock title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="col-md-10">
|
||||||
|
<h2>{{ 'absence.My absence'|trans }}</h2>
|
||||||
|
|
||||||
|
{% if user.absenceStart is not null %}
|
||||||
|
<div>
|
||||||
|
<p>{{ 'absence.You are listed as absent, as of'|trans }} {{ user.absenceStart|format_date('long') }}</p>
|
||||||
|
<ul class="record_actions sticky-form-buttons">
|
||||||
|
<li>
|
||||||
|
<a href="{{ path('chill_main_user_absence_unset') }}"
|
||||||
|
class="btn btn-delete">{{ 'absence.Unset absence'|trans }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div>
|
||||||
|
<p class="chill-no-data-statement">{{ 'absence.No absence listed'|trans }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ form_start(form) }}
|
||||||
|
{{ form_row(form.absenceStart) }}
|
||||||
|
|
||||||
|
<ul class="record_actions sticky-form-buttons">
|
||||||
|
<li>
|
||||||
|
<button class="btn btn-save" type="submit">
|
||||||
|
{{ 'Save'|trans }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{ form_end(form) }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -2,20 +2,21 @@
|
|||||||
|
|
||||||
{% block admin_content %}
|
{% block admin_content %}
|
||||||
{% embed '@ChillMain/CRUD/_index.html.twig' %}
|
{% embed '@ChillMain/CRUD/_index.html.twig' %}
|
||||||
|
|
||||||
{% block index_header %}
|
{% block index_header %}
|
||||||
<h1>{{"Users"|trans}}</h1>
|
<h1>{{"Users"|trans}}</h1>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block filter_order %}{{ filter_order|chill_render_filter_order_helper }}{% endblock %}
|
{% block filter_order %}{{ filter_order|chill_render_filter_order_helper }}{% endblock %}
|
||||||
|
|
||||||
{% block table_entities_thead_tr %}
|
{% block table_entities_thead_tr %}
|
||||||
<th>{{ 'Active'|trans }}</th>
|
<th>{{ 'Active'|trans }}</th>
|
||||||
|
<th>{{ 'absence.Is absent'|trans }}</th>
|
||||||
<th>{{ 'Username'|trans }}</th>
|
<th>{{ 'Username'|trans }}</th>
|
||||||
<th>{{ 'Datas'|trans }}</th>
|
<th>{{ 'Datas'|trans }}</th>
|
||||||
<th>{{ 'Actions'|trans }}</th>
|
<th>{{ 'Actions'|trans }}</th>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block table_entities_tbody %}
|
{% block table_entities_tbody %}
|
||||||
{% for entity in entities %}
|
{% for entity in entities %}
|
||||||
<tr>
|
<tr>
|
||||||
@ -26,6 +27,13 @@
|
|||||||
<i class="fa fa-square-o"></i>
|
<i class="fa fa-square-o"></i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if entity.isAbsent %}
|
||||||
|
<i class="fa fa-check-square-o"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="fa fa-square-o"></i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{#
|
{#
|
||||||
{% if entity.civility is not null %}
|
{% if entity.civility is not null %}
|
||||||
@ -64,13 +72,13 @@
|
|||||||
<li>
|
<li>
|
||||||
<a class="btn btn-edit" title="{{ 'Edit'|trans }}" href="{{ path('chill_crud_admin_user_edit', { 'id': entity.id }) }}"></a>
|
<a class="btn btn-edit" title="{{ 'Edit'|trans }}" href="{{ path('chill_crud_admin_user_edit', { 'id': entity.id }) }}"></a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{% if allow_change_password is same as(true) %}
|
{% if allow_change_password is same as(true) %}
|
||||||
<li>
|
<li>
|
||||||
<a class="btn btn-chill-red" href="{{ path('admin_user_edit_password', { 'id' : entity.id }) }}" title="{{ 'Edit password'|trans|e('html_attr') }}"><i class="fa fa-ellipsis-h"></i></a>
|
<a class="btn btn-chill-red" href="{{ path('admin_user_edit_password', { 'id' : entity.id }) }}" title="{{ 'Edit password'|trans|e('html_attr') }}"><i class="fa fa-ellipsis-h"></i></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if is_granted('ROLE_ALLOWED_TO_SWITCH') %}
|
{% if is_granted('ROLE_ALLOWED_TO_SWITCH') %}
|
||||||
<li>
|
<li>
|
||||||
<a class="btn btn-chill-blue" href="{{ path('chill_main_homepage', {'_switch_user': entity.username }) }}" title="{{ "Impersonate"|trans|e('html_attr') }}"><i class="fa fa-user-secret"></i></a>
|
<a class="btn btn-chill-blue" href="{{ path('chill_main_homepage', {'_switch_user': entity.username }) }}" title="{{ "Impersonate"|trans|e('html_attr') }}"><i class="fa fa-user-secret"></i></a>
|
||||||
@ -81,9 +89,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block pagination %}{{ chill_pagination(paginator) }}{% endblock %}
|
{% block pagination %}{{ chill_pagination(paginator) }}{% endblock %}
|
||||||
|
|
||||||
{% block list_actions %}
|
{% block list_actions %}
|
||||||
<ul class="record_actions sticky-form-buttons">
|
<ul class="record_actions sticky-form-buttons">
|
||||||
<li class='cancel'>
|
<li class='cancel'>
|
||||||
@ -94,6 +102,6 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock list_actions %}
|
{% endblock list_actions %}
|
||||||
|
|
||||||
{% endembed %}
|
{% endembed %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -69,18 +69,26 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-8 main_search">
|
<div class="col-8 main_search">
|
||||||
|
{% if app.user.isAbsent %}
|
||||||
|
<div class="d-flex flex-row mb-5 alert alert-warning" role="alert">
|
||||||
|
<p class="m-2">{{'absence.You are marked as being absent'|trans }}</p>
|
||||||
|
<span class="ms-auto">
|
||||||
|
<a class="btn btn-remove" title="Modifier" href="{{ path('chill_main_user_absence_index') }}">{{ 'absence.Unset absence'|trans }}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<h2>{{ 'Search'|trans }}</h2>
|
<h2>{{ 'Search'|trans }}</h2>
|
||||||
|
|
||||||
<form action="{{ path('chill_main_search') }}" method="get">
|
<form action="{{ path('chill_main_search') }}" method="get">
|
||||||
<input class="form-control form-control-lg" name="q" type="search" placeholder="{{ 'Search persons, ...'|trans }}" />
|
<input class="form-control form-control-lg" name="q" type="search" placeholder="{{ 'Search persons, ...'|trans }}" />
|
||||||
<center>
|
<div class="text-center">
|
||||||
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
||||||
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
||||||
</button>
|
</button>
|
||||||
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
|
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
|
||||||
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
|
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
|
||||||
</a>
|
</a>
|
||||||
</center>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -78,6 +78,15 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
|
|
||||||
$nbNotifications = $this->notificationByUserCounter->countUnreadByUser($user);
|
$nbNotifications = $this->notificationByUserCounter->countUnreadByUser($user);
|
||||||
|
|
||||||
|
//TODO add an icon? How exactly? For example a clock icon...
|
||||||
|
$menu
|
||||||
|
->addChild($this->translator->trans('absence.Set absence date'), [
|
||||||
|
'route' => 'chill_main_user_absence_index',
|
||||||
|
])
|
||||||
|
->setExtras([
|
||||||
|
'order' => -8888888,
|
||||||
|
]);
|
||||||
|
|
||||||
$menu
|
$menu
|
||||||
->addChild(
|
->addChild(
|
||||||
$this->translator->trans('notification.My notifications with counter', ['nb' => $nbNotifications]),
|
$this->translator->trans('notification.My notifications with counter', ['nb' => $nbNotifications]),
|
||||||
|
@ -31,6 +31,7 @@ class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAware
|
|||||||
'id' => '',
|
'id' => '',
|
||||||
'username' => '',
|
'username' => '',
|
||||||
'text' => '',
|
'text' => '',
|
||||||
|
'text_without_absent' => '',
|
||||||
'label' => '',
|
'label' => '',
|
||||||
'email' => '',
|
'email' => '',
|
||||||
];
|
];
|
||||||
@ -82,11 +83,13 @@ class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAware
|
|||||||
'id' => $object->getId(),
|
'id' => $object->getId(),
|
||||||
'username' => $object->getUsername(),
|
'username' => $object->getUsername(),
|
||||||
'text' => $this->userRender->renderString($object, []),
|
'text' => $this->userRender->renderString($object, []),
|
||||||
|
'text_without_absent' => $this->userRender->renderString($object, ['absence' => false]),
|
||||||
'label' => $object->getLabel(),
|
'label' => $object->getLabel(),
|
||||||
'email' => (string) $object->getEmail(),
|
'email' => (string) $object->getEmail(),
|
||||||
'user_job' => $this->normalizer->normalize($object->getUserJob(), $format, $userJobContext),
|
'user_job' => $this->normalizer->normalize($object->getUserJob(), $format, $userJobContext),
|
||||||
'main_center' => $this->normalizer->normalize($object->getMainCenter(), $format, $centerContext),
|
'main_center' => $this->normalizer->normalize($object->getMainCenter(), $format, $centerContext),
|
||||||
'main_scope' => $this->normalizer->normalize($object->getMainScope(), $format, $scopeContext),
|
'main_scope' => $this->normalizer->normalize($object->getMainScope(), $format, $scopeContext),
|
||||||
|
'isAbsent' => $object->isAbsent(),
|
||||||
];
|
];
|
||||||
|
|
||||||
if ('docgen' === $format) {
|
if ('docgen' === $format) {
|
||||||
|
@ -13,8 +13,10 @@ namespace Chill\MainBundle\Templating\Entity;
|
|||||||
|
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
use Symfony\Component\Templating\EngineInterface;
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
use Symfony\Component\Templating\EngineInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
|
|
||||||
class UserRender implements ChillEntityRenderInterface
|
class UserRender implements ChillEntityRenderInterface
|
||||||
@ -22,16 +24,20 @@ class UserRender implements ChillEntityRenderInterface
|
|||||||
public const DEFAULT_OPTIONS = [
|
public const DEFAULT_OPTIONS = [
|
||||||
'main_scope' => true,
|
'main_scope' => true,
|
||||||
'user_job' => true,
|
'user_job' => true,
|
||||||
|
'absence' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
private EngineInterface $engine;
|
private EngineInterface $engine;
|
||||||
|
|
||||||
private TranslatableStringHelper $translatableStringHelper;
|
private TranslatableStringHelper $translatableStringHelper;
|
||||||
|
|
||||||
public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine)
|
private TranslatorInterface $translator;
|
||||||
|
|
||||||
|
public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine, TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
$this->translatableStringHelper = $translatableStringHelper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
$this->engine = $engine;
|
$this->engine = $engine;
|
||||||
|
$this->translator = $translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderBox($entity, array $options): string
|
public function renderBox($entity, array $options): string
|
||||||
@ -63,6 +69,10 @@ class UserRender implements ChillEntityRenderInterface
|
|||||||
->localize($entity->getMainScope()->getName()) . ')';
|
->localize($entity->getMainScope()->getName()) . ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($entity->isAbsent() && $opts['absence']) {
|
||||||
|
$str .= ' (' . $this->translator->trans('absence.Absent') . ')';
|
||||||
|
}
|
||||||
|
|
||||||
return $str;
|
return $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,9 +138,9 @@ services:
|
|||||||
autowire: true
|
autowire: true
|
||||||
autoconfigure: true
|
autoconfigure: true
|
||||||
|
|
||||||
Chill\MainBundle\Form\RegroupmentType:
|
Chill\MainBundle\Form\AbsenceType: ~
|
||||||
autowire: true
|
Chill\MainBundle\Form\DataMapper\RegroupmentDataMapper: ~
|
||||||
autoconfigure: true
|
Chill\MainBundle\Form\RegroupmentType: ~
|
||||||
|
|
||||||
Chill\MainBundle\Form\DataTransformer\IdToLocationDataTransformer: ~
|
Chill\MainBundle\Form\DataTransformer\IdToLocationDataTransformer: ~
|
||||||
Chill\MainBundle\Form\DataTransformer\IdToUserDataTransformer: ~
|
Chill\MainBundle\Form\DataTransformer\IdToUserDataTransformer: ~
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Main;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20230111160610 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE users DROP absenceStart');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add absence property to user';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE users ADD absenceStart TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
|
||||||
|
$this->addSql('COMMENT ON COLUMN users.absenceStart IS \'(DC2Type:datetime_immutable)\'');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Main;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20230301155213 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Alter type to TEXT for regroupment.name';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE regroupment ALTER name TYPE TEXT');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE regroupment ALTER name TYPE VARCHAR(15)');
|
||||||
|
}
|
||||||
|
}
|
@ -584,3 +584,17 @@ saved_export:
|
|||||||
Export is deleted: L'export est supprimé
|
Export is deleted: L'export est supprimé
|
||||||
Saved export is saved!: L'export est enregistré
|
Saved export is saved!: L'export est enregistré
|
||||||
Created on %date%: Créé le %date%
|
Created on %date%: Créé le %date%
|
||||||
|
|
||||||
|
|
||||||
|
absence:
|
||||||
|
# single letter for absence
|
||||||
|
A: A
|
||||||
|
My absence: Mon absence
|
||||||
|
Unset absence: Supprimer la date d'absence
|
||||||
|
Set absence date: Indiquer une date d'absence
|
||||||
|
Absence start: Absent à partir du
|
||||||
|
Absent: Absent
|
||||||
|
You are marked as being absent: Vous êtes indiqué absent.
|
||||||
|
You are listed as absent, as of: Votre absence est indiquée à partir du
|
||||||
|
No absence listed: Aucune absence indiquée.
|
||||||
|
Is absent: Absent?
|
||||||
|
@ -11,11 +11,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\DataFixtures\ORM;
|
namespace Chill\PersonBundle\DataFixtures\ORM;
|
||||||
|
|
||||||
|
use Chill\MainBundle\DataFixtures\ORM\LoadCenters;
|
||||||
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
|
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
|
||||||
use Chill\MainBundle\Entity\Address;
|
use Chill\MainBundle\Entity\Address;
|
||||||
use Chill\MainBundle\Entity\PostalCode;
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
use Chill\PersonBundle\Entity\Household\Household;
|
use Chill\PersonBundle\Entity\Household\Household;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||||
use Chill\PersonBundle\Household\MembersEditorFactory;
|
use Chill\PersonBundle\Household\MembersEditorFactory;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
@ -192,14 +194,20 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface
|
|||||||
|
|
||||||
private function preparePersonIds()
|
private function preparePersonIds()
|
||||||
{
|
{
|
||||||
|
$centers = LoadCenters::$centers;
|
||||||
|
|
||||||
// @TODO: Remove this and make this service stateless
|
// @TODO: Remove this and make this service stateless
|
||||||
$this->personIds = $this->em
|
$this->personIds = $this->em
|
||||||
->createQuery(
|
->createQuery(
|
||||||
'SELECT p.id FROM ' . Person::class . ' p ' .
|
'SELECT p.id FROM ' . Person::class . ' p ' .
|
||||||
'JOIN p.center c ' .
|
'WHERE EXISTS( ' .
|
||||||
'WHERE c.name = :center '
|
'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch ' .
|
||||||
|
'JOIN pch.center c ' .
|
||||||
|
'WHERE pch.person = p.id ' .
|
||||||
|
'AND c.name IN (:authorized_centers)' .
|
||||||
|
')'
|
||||||
)
|
)
|
||||||
->setParameter('center', 'Center A')
|
->setParameter('authorized_centers', $centers)
|
||||||
->getScalarResult();
|
->getScalarResult();
|
||||||
|
|
||||||
shuffle($this->personIds);
|
shuffle($this->personIds);
|
||||||
|
@ -22,7 +22,12 @@
|
|||||||
<i>{{ $t('course.open_at') }}{{ $d(accompanyingCourse.openingDate.datetime, 'text') }}</i>
|
<i>{{ $t('course.open_at') }}{{ $d(accompanyingCourse.openingDate.datetime, 'text') }}</i>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="accompanyingCourse.user" class="d-md-block ms-3 ms-md-0">
|
<span v-if="accompanyingCourse.user" class="d-md-block ms-3 ms-md-0">
|
||||||
<span class="item-key">{{ $t('course.referrer') }}:</span> <b>{{ accompanyingCourse.user.text }}</b>
|
<span class="item-key">{{ $t('course.referrer') }}:</span>
|
||||||
|
<b>{{ accompanyingCourse.user.text }}</b>
|
||||||
|
<template v-if="accompanyingCourse.user.isAbsent">
|
||||||
|
|
||||||
|
<span class="badge bg-danger rounded-pill" title="Absent">A</span>
|
||||||
|
</template>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -59,13 +64,15 @@
|
|||||||
import ToggleFlags from './Banner/ToggleFlags';
|
import ToggleFlags from './Banner/ToggleFlags';
|
||||||
import SocialIssue from './Banner/SocialIssue.vue';
|
import SocialIssue from './Banner/SocialIssue.vue';
|
||||||
import PersonsAssociated from './Banner/PersonsAssociated.vue';
|
import PersonsAssociated from './Banner/PersonsAssociated.vue';
|
||||||
|
import UserRenderBoxBadge from 'ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Banner',
|
name: 'Banner',
|
||||||
components: {
|
components: {
|
||||||
ToggleFlags,
|
ToggleFlags,
|
||||||
SocialIssue,
|
SocialIssue,
|
||||||
PersonsAssociated
|
PersonsAssociated,
|
||||||
|
UserRenderBoxBadge,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
accompanyingCourse() {
|
accompanyingCourse() {
|
||||||
|
@ -29,7 +29,8 @@ const appMessages = {
|
|||||||
emergency: "urgent",
|
emergency: "urgent",
|
||||||
confidential: "confidentiel",
|
confidential: "confidentiel",
|
||||||
regular: "régulier",
|
regular: "régulier",
|
||||||
occasional: "ponctuel"
|
occasional: "ponctuel",
|
||||||
|
absent: "Absent",
|
||||||
},
|
},
|
||||||
origin: {
|
origin: {
|
||||||
title: "Origine de la demande",
|
title: "Origine de la demande",
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container usercontainer">
|
<div class="container usercontainer">
|
||||||
<div class="user-identification">
|
<div class="user-identification">
|
||||||
<span class="name">
|
<user-render-box-badge :user="item.result"></user-render-box-badge>
|
||||||
{{ item.result.text }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right_actions">
|
<div class="right_actions">
|
||||||
@ -16,10 +14,12 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
|
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
|
||||||
|
import UserRenderBoxBadge from 'ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SuggestionUser',
|
name: 'SuggestionUser',
|
||||||
components: {
|
components: {
|
||||||
|
UserRenderBoxBadge,
|
||||||
BadgeEntity
|
BadgeEntity
|
||||||
},
|
},
|
||||||
props: ['item'],
|
props: ['item'],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user