Merge branch 'calendar/finalization' into testing

This commit is contained in:
Julien Fastré 2022-10-24 11:12:49 +02:00
commit 2e44689549
216 changed files with 5516 additions and 1054 deletions

View File

@ -265,7 +265,7 @@ Tests reside inside the installed bundles. You must `cd` into that directory, do
**Note**: some bundle require the fixture to be executed. See the dedicated _how-tos_.
Exemple, for running test inside `main` bundle:
Exemple, for running unit test inside `main` bundle:
.. code-block:: bash
@ -280,6 +280,30 @@ Exemple, for running test inside `main` bundle:
# run tests
bin/phpunit src/Bundle/path/to/your/test
Or for running tests to check code style and php conventions with csfixer and phpstan:
.. code-block:: bash
# run code style fixer
bin/grumphp run --tasks=phpcsfixer
# run phpstan
bin/grumphp run --tasks=phpstan
.. note::
To avoid phpstan block your commits:
.. code-block:: bash
git commit -n ...
To avoid phpstan block your commits permanently:
.. code-block:: bash
./bin/grumphp git:deinit
How to run webpack interactively
================================

View File

@ -42,7 +42,6 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Serializer\SerializerInterface;
use function array_key_exists;

View File

@ -62,7 +62,7 @@ class ByCreatorFilter implements FilterInterface
}
return ['Filtered activity by creator: only %users%', [
'%users%' => implode(', ou ', $users),
'%users%' => implode(', ', $users),
]];
}

View File

@ -14,10 +14,9 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Form\Type\PickSocialActionType;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
@ -37,22 +36,17 @@ class BySocialActionFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (!in_array('actsocialaction', $qb->getAllAliases(), true)) {
$qb->join('activity.socialActions', 'actsocialaction');
}
$clause = $qb->expr()->in('actsocialaction.id', ':socialactions');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('socialactions', $data['accepted_socialactions']);
$qb->andWhere($clause)
->setParameter(
'socialactions',
SocialAction::getDescendantsWithThisForActions($data['accepted_socialactions'])
);
}
public function applyOn(): string
@ -62,13 +56,8 @@ class BySocialActionFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_socialactions', EntityType::class, [
'class' => SocialAction::class,
'choice_label' => function (SocialAction $sa) {
return $this->actionRender->renderString($sa, []);
},
$builder->add('accepted_socialactions', PickSocialActionType::class, [
'multiple' => true,
'expanded' => true,
]);
}
@ -76,12 +65,14 @@ class BySocialActionFilter implements FilterInterface
{
$actions = [];
foreach ($data['accepted_socialactions'] as $sa) {
$actions[] = $this->actionRender->renderString($sa, []);
foreach ($data['accepted_socialactions'] as $action) {
$actions[] = $this->actionRender->renderString($action, [
'show_and_children' => true,
]);
}
return ['Filtered activity by linked socialaction: only %actions%', [
'%actions%' => implode(', ou ', $actions),
'%actions%' => implode(', ', $actions),
]];
}

View File

@ -14,10 +14,9 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Form\Type\PickSocialIssueType;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
@ -37,22 +36,17 @@ class BySocialIssueFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (!in_array('actsocialissue', $qb->getAllAliases(), true)) {
$qb->join('activity.socialIssues', 'actsocialissue');
}
$clause = $qb->expr()->in('actsocialissue.id', ':socialissues');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('socialissues', $data['accepted_socialissues']);
$qb->andWhere($clause)
->setParameter(
'socialissues',
SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues'])
);
}
public function applyOn(): string
@ -62,13 +56,8 @@ class BySocialIssueFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_socialissues', EntityType::class, [
'class' => SocialIssue::class,
'choice_label' => function (SocialIssue $si) {
return $this->issueRender->renderString($si, []);
},
$builder->add('accepted_socialissues', PickSocialIssueType::class, [
'multiple' => true,
'expanded' => true,
]);
}
@ -76,12 +65,14 @@ class BySocialIssueFilter implements FilterInterface
{
$issues = [];
foreach ($data['accepted_socialissues'] as $si) {
$issues[] = $this->issueRender->renderString($si, []);
foreach ($data['accepted_socialissues'] as $issue) {
$issues[] = $this->issueRender->renderString($issue, [
'show_and_children' => true,
]);
}
return ['Filtered activity by linked socialissue: only %issues%', [
'%issues%' => implode(', ou ', $issues),
'%issues%' => implode(', ', $issues),
]];
}

View File

@ -13,7 +13,7 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\Select2LocationTypeType;
use Chill\MainBundle\Form\Type\PickLocationTypeType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
@ -60,7 +60,7 @@ class LocationTypeFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_locationtype', Select2LocationTypeType::class, [
$builder->add('accepted_locationtype', PickLocationTypeType::class, [
'multiple' => true,
//'label' => false,
]);
@ -77,7 +77,7 @@ class LocationTypeFilter implements FilterInterface
}
return ['Filtered activity by locationtype: only %types%', [
'%types%' => implode(', ou ', $types),
'%types%' => implode(', ', $types),
]];
}

View File

@ -71,7 +71,7 @@ class UserFilter implements FilterInterface
}
return ['Filtered activity by user: only %users%', [
'%users%' => implode(', ou ', $users),
'%users%' => implode(', ', $users),
]];
}

View File

@ -85,7 +85,7 @@ class UserScopeFilter implements FilterInterface
}
return ['Filtered activity by userscope: only %scopes%', [
'%scopes%' => implode(', ou ', $scopes),
'%scopes%' => implode(', ', $scopes),
]];
}

View File

@ -91,7 +91,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
);
return ['Filtered by activity type: only %list%', [
'%list%' => implode(', ou ', $reasonsNames),
'%list%' => implode(', ', $reasonsNames),
]];
}

View File

@ -0,0 +1,81 @@
<?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\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class UsersJobFilter implements FilterInterface
{
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(TranslatableStringHelperInterface $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act
JOIN activity_users_job_filter_act.users users WHERE users.userJob IN (:activity_users_job_filter_jobs) AND activity_users_job_filter_act = activity '
)
)
->setParameter('activity_users_job_filter_jobs', $data['jobs']);
}
public function applyOn()
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('jobs', EntityType::class, [
'class' => UserJob::class,
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize($j->getLabel()),
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string')
{
return ['export.filter.activity.by_usersjob.Filtered activity by users job: only %jobs%', [
'%jobs%' => implode(
', ',
array_map(
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
$data['jobs']->toArray()
)
),
]];
}
public function getTitle()
{
return 'export.filter.activity.by_usersjob.Filter by users job';
}
}

View File

@ -0,0 +1,88 @@
<?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\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class UsersScopeFilter implements FilterInterface
{
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ScopeRepositoryInterface $scopeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . Activity::class . ' activity_users_scope_filter_act
JOIN activity_users_scope_filter_act.users users WHERE users.mainScope IN (:activity_users_scope_filter_scopes) AND activity_users_scope_filter_act = activity '
)
)
->setParameter('activity_users_scope_filter_scopes', $data['scopes']);
}
public function applyOn()
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('scopes', EntityType::class, [
'class' => Scope::class,
'choices' => $this->scopeRepository->findAllActive(),
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string')
{
return ['export.filter.activity.by_usersscope.Filtered activity by users scope: only %scopes%', [
'%scopes%' => implode(
', ',
array_map(
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
$data['scopes']->toArray()
)
),
]];
}
public function getTitle()
{
return 'export.filter.activity.by_usersscope.Filter by users scope';
}
}

View File

@ -29,7 +29,7 @@
{% endmacro %}
{% set blocks = [] %}
{% if context == 'calendar_accompanyingCourse' or entity.activityType.personsVisible %}
{% if context == 'calendar_accompanyingCourse' or context == 'calendar_person' or entity.activityType.personsVisible %}
{% if context == 'person' %}
{% set blocks = blocks|merge([{
'title': 'Others persons'|trans,
@ -54,7 +54,7 @@
}]) %}
{% endif %}
{% endif %}
{% if context == 'calendar_accompanyingCourse' or entity.activityType.thirdPartiesVisible %}
{% if context == 'calendar_accompanyingCourse' or context == 'calendar_person' or entity.activityType.thirdPartiesVisible %}
{% set blocks = blocks|merge([{
'title': 'Third parties'|trans,
'items': entity.thirdParties,
@ -63,7 +63,7 @@
'key' : 'id',
}]) %}
{% endif %}
{% if context == 'calendar_accompanyingCourse' or entity.activityType.usersVisible %}
{% if context == 'calendar_accompanyingCourse' or context == 'calendar_person' or entity.activityType.usersVisible %}
{% set blocks = blocks|merge([{
'title': 'Users concerned'|trans,
'items': entity.users,
@ -143,7 +143,7 @@
{% if bloc.type == 'user' %}
<span class="badge-user">
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }}
{%- if context == 'calendar_accompanyingCourse' %}
{%- if context == 'calendar_accompanyingCourse' or context == 'calendar_person' %}
{% set invite = entity.inviteForUser(item) %}
{% if invite is not null %}
{{ invite.invite_span(invite) }}

View File

@ -4,18 +4,15 @@ services:
autoconfigure: true
## Indicators
chill.activity.export.count_activity_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\CountActivity
Chill\ActivityBundle\Export\Export\LinkedToPerson\CountActivity:
tags:
- { name: chill.export, alias: 'count_activity_linked_to_person' }
chill.activity.export.sum_activity_duration_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\StatActivityDuration
Chill\ActivityBundle\Export\Export\LinkedToPerson\StatActivityDuration:
tags:
- { name: chill.export, alias: 'sum_activity_duration_linked_to_person' }
chill.activity.export.list_activity_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\ListActivity
Chill\ActivityBundle\Export\Export\LinkedToPerson\ListActivity:
tags:
- { name: chill.export, alias: 'list_activity_linked_to_person' }
@ -115,9 +112,16 @@ services:
tags:
- { name: chill.export_filter, alias: 'activity_userscope_filter' }
Chill\ActivityBundle\Export\Filter\UsersJobFilter:
tags:
- { name: chill.export_filter, alias: 'activity_usersjob_filter' }
Chill\ActivityBundle\Export\Filter\UsersScopeFilter:
tags:
- { name: chill.export_filter, alias: 'activity_usersscope_filter' }
## Aggregators
chill.activity.export.reason_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator
Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator:
tags:
- { name: chill.export_aggregator, alias: activity_reason_aggregator }

View File

@ -119,15 +119,15 @@ Activity Presences: Presences aux activités
# Crud
crud:
activity_type:
title_new: Nouveau type d'activité
title_edit: Edition d'un type d'activité
activity_type_category:
title_new: Nouvelle catégorie de type d'activité
title_edit: Edition d'une catégorie de type d'activité
activity_presence:
title_new: Nouvelle Présence aux activités
title_edit: Edition d'une Présence aux activités
activity_type:
title_new: Nouveau type d'activité
title_edit: Edition d'un type d'activité
activity_type_category:
title_new: Nouvelle catégorie de type d'activité
title_edit: Edition d'une catégorie de type d'activité
activity_presence:
title_new: Nouvelle Présence aux activités
title_edit: Edition d'une Présence aux activités
# activity reason admin
ActivityReason list: Liste des sujets
@ -322,3 +322,13 @@ docgen:
A basic context for activity: Contexte pour les échanges
Accompanying period with a list of activities: Parcours d'accompagnement avec liste des échanges
Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les échanges pour un parcours. Les échanges ne sont pas filtrés.
export:
filter:
activity:
by_usersjob:
Filter by users job: Filtrer les activités par métier d'au moins un utilisateur participant
'Filtered activity by users job: only %jobs%': 'Filtré par métier d''au moins un utilisateur participant: seulement %jobs%'
by_usersscope:
Filter by users scope: Filtrer les activités par services d'au moins un utilisateur participant
'Filtered activity by users scope: only %scopes%': 'Filtré par service d''au moins un utilisateur participant: seulement %scopes%'

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Command;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MachineTokenStorage;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Command;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\EventsOnUserSubscriptionCreator;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Command;
use Chill\CalendarBundle\Service\ShortMessageNotification\BulkCalendarShortMessageSender;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Command;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -15,18 +15,23 @@ use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Form\CalendarType;
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface;
use Chill\CalendarBundle\Repository\CalendarACLAwareRepositoryInterface;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use DateTimeImmutable;
use Exception;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Form;
@ -40,9 +45,11 @@ use Symfony\Component\Serializer\SerializerInterface;
class CalendarController extends AbstractController
{
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private CalendarACLAwareRepositoryInterface $calendarACLAwareRepository;
private CalendarRepository $calendarRepository;
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private FilterOrderHelperFactoryInterface $filterOrderHelperFactory;
@ -50,29 +57,35 @@ class CalendarController extends AbstractController
private PaginatorFactory $paginator;
private PersonRepository $personRepository;
private RemoteCalendarConnectorInterface $remoteCalendarConnector;
private SerializerInterface $serializer;
private UserRepository $userRepository;
private UserRepositoryInterface $userRepository;
public function __construct(
CalendarRepository $calendarRepository,
CalendarACLAwareRepositoryInterface $calendarACLAwareRepository,
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
LoggerInterface $logger,
PaginatorFactory $paginator,
RemoteCalendarConnectorInterface $remoteCalendarConnector,
SerializerInterface $serializer,
UserRepository $userRepository
PersonRepository $personRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
UserRepositoryInterface $userRepository
) {
$this->calendarRepository = $calendarRepository;
$this->calendarACLAwareRepository = $calendarACLAwareRepository;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->filterOrderHelperFactory = $filterOrderHelperFactory;
$this->logger = $logger;
$this->paginator = $paginator;
$this->remoteCalendarConnector = $remoteCalendarConnector;
$this->serializer = $serializer;
$this->personRepository = $personRepository;
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
$this->userRepository = $userRepository;
}
@ -83,19 +96,21 @@ class CalendarController extends AbstractController
*/
public function deleteAction(Request $request, Calendar $entity)
{
$view = null;
$em = $this->getDoctrine()->getManager();
$accompanyingPeriod = $entity->getAccompanyingPeriod();
$user = null; // TODO legacy code ? remove it ?
[$person, $accompanyingPeriod] = [$entity->getPerson(), $entity->getAccompanyingPeriod()];
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/confirm_deleteByAccompanyingCourse.html.twig';
} elseif ($user instanceof User) {
$view = '@ChillCalendar/Calendar/confirm_deleteByUser.html.twig';
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]);
} elseif ($person instanceof Person) {
$view = '@ChillCalendar/Calendar/confirm_deleteByPerson.html.twig';
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]);
} else {
throw new RuntimeException('nor person or accompanying period');
}
$form = $this->createDeleteForm($entity->getId(), $user, $accompanyingPeriod);
$form = $this->createDeleteForm($entity);
if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request);
@ -112,20 +127,15 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')
->trans('The calendar item has been successfully removed.'));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod);
return $this->redirectToRoute('chill_calendar_calendar_list_by_period', $params);
return new RedirectResponse($redirectRoute);
}
}
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, [
'calendar' => $entity,
'delete_form' => $form->createView(),
'accompanyingCourse' => $accompanyingPeriod,
'person' => $person,
]);
}
@ -140,19 +150,27 @@ class CalendarController extends AbstractController
return $this->remoteCalendarConnector->getMakeReadyResponse($request->getUri());
}
$view = null;
$em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request);
[$person, $accompanyingPeriod] = [$entity->getPerson(), $entity->getAccompanyingPeriod()];
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/editByAccompanyingCourse.html.twig';
} elseif ($user instanceof User) {
throw new Exception('to analyze');
$view = '@ChillCalendar/Calendar/editByUser.html.twig';
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]);
} elseif ($person instanceof Person) {
$view = '@ChillCalendar/Calendar/editByPerson.html.twig';
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]);
} else {
throw new RuntimeException('no person nor accompanying period');
}
$form = $this->createForm(CalendarType::class, $entity)
->add('save', SubmitType::class);
if (0 < $this->docGeneratorTemplateRepository->countByEntity(Calendar::class)) {
$form->add('save_and_create_doc', SubmitType::class);
}
$form = $this->createForm(CalendarType::class, $entity);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
@ -160,29 +178,24 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!'));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod);
if ($form->get('save_and_create_doc')->isClicked()) {
return $this->redirectToRoute('chill_calendar_calendardoc_pick_template', ['id' => $entity->getId()]);
}
return $this->redirectToRoute('chill_calendar_calendar_list_by_period', $params);
return new RedirectResponse($redirectRoute);
}
if ($form->isSubmitted() && !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
}
$deleteForm = $this->createDeleteForm($entity->getId(), $user, $accompanyingPeriod);
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
$entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
'entity' => $entity,
'form' => $form->createView(),
'delete_form' => $deleteForm->createView(),
'accompanyingCourse' => $accompanyingPeriod,
// 'user' => $user,
'accompanyingCourse' => $entity->getAccompanyingPeriod(),
'person' => $entity->getPerson(),
'entity_json' => $entity_array,
]);
}
@ -214,6 +227,38 @@ class CalendarController extends AbstractController
'accompanyingCourse' => $accompanyingPeriod,
'paginator' => $paginator,
'filterOrder' => $filterOrder,
'hasDocs' => 0 < $this->docGeneratorTemplateRepository->countByEntity(Calendar::class),
]);
}
/**
* Lists all Calendar entities on a person.
*
* @Route("/{_locale}/calendar/calendar/by-person/{id}", name="chill_calendar_calendar_list_by_person")
*/
public function listActionByPerson(Person $person): Response
{
$filterOrder = $this->buildListFilterOrder();
['from' => $from, 'to' => $to] = $filterOrder->getDateRangeData('startDate');
$total = $this->calendarACLAwareRepository
->countByPerson($person, $from, $to);
$paginator = $this->paginator->create($total);
$calendarItems = $this->calendarACLAwareRepository->findByPerson(
$person,
$from,
$to,
['startDate' => 'DESC'],
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage()
);
return $this->render('@ChillCalendar/Calendar/listByPerson.html.twig', [
'calendarItems' => $calendarItems,
'person' => $person,
'paginator' => $paginator,
'filterOrder' => $filterOrder,
'hasDocs' => 0 < $this->docGeneratorTemplateRepository->countByEntity(Calendar::class),
]);
}
@ -253,30 +298,31 @@ class CalendarController extends AbstractController
$view = null;
$em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request);
[$person, $accompanyingPeriod] = $this->getEntity($request);
$entity = new Calendar();
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig';
$entity->setAccompanyingPeriod($accompanyingPeriod);
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]);
} elseif ($person) {
$view = '@ChillCalendar/Calendar/newByPerson.html.twig';
$entity->setPerson($person)->addPerson($person);
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]);
}
// elseif ($user instanceof User) {
// $view = '@ChillCalendar/Calendar/newUser.html.twig';
// }
$entity = new Calendar();
if ($request->query->has('mainUser')) {
$entity->setMainUser($this->userRepository->find($request->query->getInt('mainUser')));
}
// if ($user instanceof User) {
// $entity->setPerson($user);
// }
$form = $this->createForm(CalendarType::class, $entity)
->add('save', SubmitType::class);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$entity->setAccompanyingPeriod($accompanyingPeriod);
if (0 < $this->docGeneratorTemplateRepository->countByEntity(Calendar::class)) {
$form->add('save_and_create_doc', SubmitType::class);
}
$form = $this->createForm(CalendarType::class, $entity);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
@ -285,9 +331,11 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item created!'));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod);
if ($form->get('save_and_create_doc')->isClicked()) {
return $this->redirectToRoute('chill_calendar_calendardoc_pick_template', ['id' => $entity->getId()]);
}
return $this->redirectToRoute('chill_calendar_calendar_list_by_period', $params);
return new RedirectResponse($redirectRoute);
}
if ($form->isSubmitted() && !$form->isValid()) {
@ -301,7 +349,8 @@ class CalendarController extends AbstractController
$entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
'user' => $user,
'context' => $entity->getContext(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity,
'form' => $form->createView(),
@ -316,6 +365,7 @@ class CalendarController extends AbstractController
*/
public function showAction(Request $request, int $id): Response
{
throw new Exception('not implemented');
$view = null;
$em = $this->getDoctrine()->getManager();
@ -450,49 +500,45 @@ class CalendarController extends AbstractController
/**
* Creates a form to delete a Calendar entity by id.
*/
private function createDeleteForm(int $id, ?User $user, ?AccompanyingPeriod $accompanyingPeriod): FormInterface
private function createDeleteForm(Calendar $calendar): FormInterface
{
$params = $this->buildParamsToUrl($user, $accompanyingPeriod);
$params['id'] = $id;
return $this->createFormBuilder()
->setAction($this->generateUrl('chill_calendar_calendar_delete', $params))
->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()]))
->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}
/**
* @return array{0: ?Person, 1: ?AccompanyingPeriod}
*/
private function getEntity(Request $request): array
{
$em = $this->getDoctrine()->getManager();
$user = $accompanyingPeriod = null;
$person = $accompanyingPeriod = null;
if ($request->query->has('user_id')) {
$user_id = $request->get('user_id');
$user = $em->getRepository(User::class)->find($user_id);
if ($request->query->has('person_id')) {
$person = $this->personRepository->find($request->query->getInt('person_id'));
if (null === $user) {
throw $this->createNotFoundException('User not found');
if (null === $person) {
throw $this->createNotFoundException('Person not found');
}
// TODO Add permission
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $user);
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person);
} elseif ($request->query->has('accompanying_period_id')) {
$accompanying_period_id = $request->get('accompanying_period_id');
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id);
$accompanyingPeriod = $this->accompanyingPeriodRepository->find($request->query->getInt('accompanying_period_id'));
if (null === $accompanyingPeriod) {
throw $this->createNotFoundException('Accompanying Period not found');
}
// TODO Add permission
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod);
} else {
throw $this->createNotFoundException('Person or Accompanying Period not found');
}
return [
$user, $accompanyingPeriod,
$person, $accompanyingPeriod,
];
}
}

View File

@ -0,0 +1,82 @@
<?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\CalendarBundle\Controller;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use RuntimeException;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Templating\EngineInterface;
class CalendarDocController
{
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private EngineInterface $engine;
private Security $security;
private SerializerInterface $serializer;
private UrlGeneratorInterface $urlGenerator;
public function __construct(Security $security, DocGeneratorTemplateRepository $docGeneratorTemplateRepository, UrlGeneratorInterface $urlGenerator, EngineInterface $engine)
{
$this->security = $security;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->urlGenerator = $urlGenerator;
$this->engine = $engine;
}
/**
* @Route("/{_locale}/calendar/docgen/pick/{id}", name="chill_calendar_calendardoc_pick_template")
*/
public function pickTemplate(Calendar $calendar): Response
{
if (!$this->security->isGranted(CalendarVoter::SEE, $calendar)) {
throw new AccessDeniedException('Not authorized to see this calendar');
}
if (0 === $number = $this->docGeneratorTemplateRepository->countByEntity(Calendar::class)) {
throw new RuntimeException('should not be redirected to this page if no template');
}
if (1 === $number) {
$templates = $this->docGeneratorTemplateRepository->findByEntity(Calendar::class);
return new RedirectResponse(
$this->urlGenerator->generate(
'chill_docgenerator_generate_from_template',
[
'template' => $templates[0]->getId(),
'entityClassName' => Calendar::class,
'entityId' => $calendar->getId(),
]
)
);
}
return new Response(
$this->engine->render('@ChillCalendar/CalendarDoc/pick_template.html.twig', [
'calendar' => $calendar,
'accompanyingCourse' => $calendar->getAccompanyingPeriod(),
])
);
}
}

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Controller;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Controller;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\OnBehalfOfUserTokenStorage;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Controller;
use Chill\CalendarBundle\Messenger\Message\MSGraphChangeNotificationMessage;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Controller;
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface;

View File

@ -44,6 +44,9 @@ use function in_array;
* uniqueConstraints={@ORM\UniqueConstraint(name="idx_calendar_remote", columns={"remoteId"}, options={"where": "remoteId <> ''"})}
* )
* @ORM\Entity
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
* "chill_calendar_calendar": Calendar::class
* })
*/
class Calendar implements TrackCreationInterface, TrackUpdateInterface
{
@ -89,7 +92,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod", inversedBy="calendars")
*/
private AccompanyingPeriod $accompanyingPeriod;
private ?AccompanyingPeriod $accompanyingPeriod = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\Activity")
@ -109,13 +112,24 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\Embedded(class=CommentEmbeddable::class, columnPrefix="comment_")
* @Serializer\Groups({"calendar:read", "read"})
* @Serializer\Groups({"calendar:read", "read", "docgen:read"})
*/
private CommentEmbeddable $comment;
/**
* @ORM\Column(type="integer", nullable=false, options={"default": 0})
*/
private int $dateTimeVersion = 0;
/**
* @var Collection<CalendarDoc::class>
* @ORM\OneToMany(targetEntity=CalendarDoc::class, mappedBy="calendar", orphanRemoval=true)
*/
private Collection $documents;
/**
* @ORM\Column(type="datetime_immutable", nullable=false)
* @Serializer\Groups({"calendar:read", "read", "calendar:light"})
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
* @Assert\NotNull(message="calendar.An end date is required")
*/
private ?DateTimeImmutable $endDate = null;
@ -124,7 +138,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"calendar:read", "read", "calendar:light"})
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
*/
private ?int $id = null;
@ -136,29 +150,35 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
* cascade={"persist", "remove", "merge", "detach"}
* )
* @ORM\JoinTable(name="chill_calendar.calendar_to_invites")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private Collection $invites;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Assert\NotNull(message="calendar.A location is required")
*/
private ?Location $location = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @Serializer\Groups({"calendar:read", "read", "calendar:light"})
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
* @Assert\NotNull(message="calendar.A main user is mandatory")
*/
private ?User $mainUser = null;
/**
* @ORM\ManyToOne(targetEntity=Person::class)
* @ORM\JoinColumn(nullable=true)
*/
private ?Person $person = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person", inversedBy="calendars")
* @ORM\JoinTable(name="chill_calendar.calendar_to_persons")
* @Serializer\Groups({"calendar:read", "read", "calendar:light"})
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
* @Assert\Count(min=1, minMessage="calendar.At least {{ limit }} person is required.")
*/
@ -173,13 +193,14 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\ManyToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
* @ORM\JoinTable(name="chill_calendar.calendar_to_thirdparties")
* @Serializer\Groups({"calendar:read", "read", "calendar:light"})
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
*/
private Collection $professionals;
/**
* @ORM\Column(type="boolean", nullable=true)
* @Serializer\Groups({"docgen:read"})
*/
private ?bool $sendSMS = false;
@ -190,7 +211,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\Column(type="datetime_immutable", nullable=false)
* @Serializer\Groups({"calendar:read", "read", "calendar:light"})
* @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
* @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
* @Assert\NotNull(message="calendar.A start date is required")
*/
@ -203,15 +224,34 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
*/
private string $status = self::STATUS_VALID;
/**
* @ORM\Column(type="boolean", nullable=true)
* @Serializer\Groups({"docgen:read"})
*/
private ?bool $urgent = false;
public function __construct()
{
$this->comment = new CommentEmbeddable();
$this->documents = new ArrayCollection();
$this->privateComment = new PrivateCommentEmbeddable();
$this->persons = new ArrayCollection();
$this->professionals = new ArrayCollection();
$this->invites = new ArrayCollection();
}
/**
* @internal use @{CalendarDoc::__construct} instead
*/
public function addDocument(CalendarDoc $calendarDoc): self
{
if ($this->documents->contains($calendarDoc)) {
$this->documents[] = $calendarDoc;
}
return $this;
}
/**
* @internal Use {@link (Calendar::addUser)} instead
*/
@ -277,6 +317,38 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this->comment;
}
/**
* @return 'person'|'accompanying_period'|null
*/
public function getContext(): ?string
{
if ($this->getAccompanyingPeriod() !== null) {
return 'accompanying_period';
}
if ($this->getPerson() !== null) {
return 'person';
}
return null;
}
/**
* Each time the date and time is update, this version is incremented.
*/
public function getDateTimeVersion(): int
{
return $this->dateTimeVersion;
}
public function getDocuments(): Collection
{
return $this->documents;
}
/**
* @Serializer\Groups({"docgen:read"})
*/
public function getDuration(): ?DateInterval
{
if ($this->getStartDate() === null || $this->getEndDate() === null) {
@ -329,6 +401,11 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this->mainUser;
}
public function getPerson(): ?Person
{
return $this->person;
}
/**
* @return Collection|Person[]
*/
@ -409,6 +486,11 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this->getProfessionals();
}
public function getUrgent(): ?bool
{
return $this->urgent;
}
/**
* @return Collection|User[]
* @Serializer\Groups({"calendar:read", "read"})
@ -454,6 +536,18 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
]));
}
/**
* @internal use @{CalendarDoc::setCalendar} with null instead
*/
public function removeDocument(CalendarDoc $calendarDoc): self
{
if ($calendarDoc->getCalendar() !== $this) {
throw new LogicException('cannot remove document of another calendar');
}
return $this;
}
/**
* @internal Use {@link (Calendar::removeUser)} instead
*/
@ -544,6 +638,10 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
public function setEndDate(DateTimeImmutable $endDate): self
{
if (null === $this->endDate || $this->endDate->getTimestamp() !== $endDate->getTimestamp()) {
$this->increaseaDatetimeVersion();
}
$this->endDate = $endDate;
return $this;
@ -568,6 +666,13 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this;
}
public function setPerson(?Person $person): Calendar
{
$this->person = $person;
return $this;
}
public function setPrivateComment(PrivateCommentEmbeddable $privateComment): self
{
$this->privateComment = $privateComment;
@ -591,6 +696,10 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
public function setStartDate(DateTimeImmutable $startDate): self
{
if (null === $this->startDate || $this->startDate->getTimestamp() !== $startDate->getTimestamp()) {
$this->increaseaDatetimeVersion();
}
$this->startDate = $startDate;
return $this;
@ -606,4 +715,16 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this;
}
public function setUrgent(bool $urgent): self
{
$this->urgent = $urgent;
return $this;
}
private function increaseaDatetimeVersion(): void
{
++$this->dateTimeVersion;
}
}

View File

@ -0,0 +1,135 @@
<?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\CalendarBundle\Entity;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(
* name="chill_calendar.calendar_doc",
* )
*/
class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**
* @ORM\ManyToOne(targetEntity=Calendar::class, inversedBy="documents")
* @ORM\JoinColumn(nullable=false)
*/
private Calendar $calendar;
/**
* @ORM\Column(type="integer", nullable=false, options={"default": 0})
*/
private int $datetimeVersion = 0;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=StoredObject::class, cascade={"persist"})
* @ORM\JoinColumn(nullable=false)
*/
private StoredObject $storedObject;
/**
* @ORM\Column(type="boolean", nullable=false, options={"default": false})
*/
private bool $trackDateTimeVersion = false;
public function __construct(Calendar $calendar, StoredObject $storedObject)
{
$this->setCalendar($calendar);
$this->storedObject = $storedObject;
$this->datetimeVersion = $calendar->getDateTimeVersion();
}
public function getCalendar(): Calendar
{
return $this->calendar;
}
public function getDatetimeVersion(): int
{
return $this->datetimeVersion;
}
public function getId(): ?int
{
return $this->id;
}
public function getStoredObject(): StoredObject
{
return $this->storedObject;
}
public function isTrackDateTimeVersion(): bool
{
return $this->trackDateTimeVersion;
}
/**
* @internal use @see{Calendar::removeDocument} instead
*
* @param Calendar $calendar
*/
public function setCalendar(?Calendar $calendar): CalendarDoc
{
if (null === $calendar) {
$this->calendar->removeDocument($this);
} else {
$calendar->addDocument($this);
}
$this->calendar = $calendar;
$this->datetimeVersion = $calendar->getDateTimeVersion();
return $this;
}
public function setDatetimeVersion(int $datetimeVersion): CalendarDoc
{
$this->datetimeVersion = $datetimeVersion;
return $this;
}
public function setStoredObject(StoredObject $storedObject): CalendarDoc
{
$this->storedObject = $storedObject;
return $this;
}
public function setTrackDateTimeVersion(bool $trackDateTimeVersion): CalendarDoc
{
$this->trackDateTimeVersion = $trackDateTimeVersion;
return $this;
}
}

View File

@ -73,14 +73,14 @@ class Invite implements TrackUpdateInterface, TrackCreationInterface
/**
* @ORM\Column(type="text", nullable=false, options={"default": "pending"})
* @Serializer\Groups(groups={"calendar:read", "read"})
* @Serializer\Groups(groups={"calendar:read", "read", "docgen:read"})
*/
private string $status = self::PENDING;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @ORM\JoinColumn(nullable=false)
* @Serializer\Groups(groups={"calendar:read", "read"})
* @Serializer\Groups(groups={"calendar:read", "read", "docgen:read"})
*/
private ?User $user = null;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

View File

@ -42,7 +42,7 @@ final class AgentAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.user', 'caluser');
$qb->join('cal.mainUser', 'caluser');
}
$qb->addSelect('caluser.id AS agent_aggregator');
@ -79,6 +79,6 @@ final class AgentAggregator implements AggregatorInterface
public function getTitle(): string
{
return 'Group appointments by agent';
return 'Group calendars by agent';
}
}

View File

@ -41,7 +41,7 @@ class CancelReasonAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
// TODO: still needs to take into account appointments without a cancel reason somehow
// TODO: still needs to take into account calendars without a cancel reason somehow
if (!in_array('calcancel', $qb->getAllAliases(), true)) {
$qb->join('cal.cancelReason', 'calcancel');
}
@ -82,6 +82,6 @@ class CancelReasonAggregator implements AggregatorInterface
public function getTitle(): string
{
return 'Group appointments by cancel reason';
return 'Group calendars by cancel reason';
}
}

View File

@ -42,7 +42,7 @@ final class JobAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.user', 'caluser');
$qb->join('cal.mainUser', 'caluser');
}
$qb->addSelect('IDENTITY(caluser.userJob) as job_aggregator');
@ -81,6 +81,6 @@ final class JobAggregator implements AggregatorInterface
public function getTitle(): string
{
return 'Group appointments by agent job';
return 'Group calendars by agent job';
}
}

View File

@ -73,6 +73,6 @@ final class LocationAggregator implements AggregatorInterface
public function getTitle(): string
{
return 'Group appointments by location';
return 'Group calendars by location';
}
}

View File

@ -81,6 +81,6 @@ final class LocationTypeAggregator implements AggregatorInterface
public function getTitle(): string
{
return 'Group appointments by location type';
return 'Group calendars by location type';
}
}

View File

@ -59,6 +59,6 @@ class MonthYearAggregator implements AggregatorInterface
public function getTitle(): string
{
return 'Group appointments by month and year';
return 'Group calendars by month and year';
}
}

View File

@ -42,7 +42,7 @@ final class ScopeAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.user', 'caluser');
$qb->join('cal.mainUser', 'caluser');
}
$qb->addSelect('IDENTITY(caluser.mainScope) as scope_aggregator');
@ -81,6 +81,6 @@ final class ScopeAggregator implements AggregatorInterface
public function getTitle(): string
{
return 'Group appointments by agent scope';
return 'Group calendars by agent scope';
}
}

View File

@ -0,0 +1,96 @@
<?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);
/*
* 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\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Closure;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class UrgencyAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('cal.urgent AS urgency_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('urgency_aggregator');
} else {
$qb->groupBy('urgency_aggregator');
}
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data): Closure
{
return function ($value): string {
if ('_header' === $value) {
return 'Urgency';
}
switch ($value) {
case true:
return $this->translator->trans('is urgent');
case false:
return $this->translator->trans('is not urgent');
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
}
};
}
public function getQueryKeys($data): array
{
return ['urgency_aggregator'];
}
public function getTitle(): string
{
return 'Group calendars by urgency';
}
}

View File

@ -21,10 +21,9 @@ use Closure;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Validator\Exception\LogicException;
class CountAppointments implements ExportInterface, GroupedExportInterface
class CountCalendars implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
@ -45,7 +44,7 @@ class CountAppointments implements ExportInterface, GroupedExportInterface
public function getDescription(): string
{
return 'Count appointments by various parameters.';
return 'Count calendars by various parameters.';
}
public function getGroup(): string
@ -79,7 +78,7 @@ class CountAppointments implements ExportInterface, GroupedExportInterface
public function getTitle(): string
{
return 'Count appointments';
return 'Count calendars';
}
public function getType(): string

View File

@ -22,7 +22,7 @@ use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterface
class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
@ -44,7 +44,7 @@ class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterf
public function getDescription(): string
{
return 'Get the average of appointment duration according to various filters';
return 'Get the average of calendar duration according to various filters';
}
public function getGroup(): string
@ -78,7 +78,7 @@ class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterf
public function getTitle(): string
{
return 'Average appointment duration';
return 'Average calendar duration';
}
public function getType(): string

View File

@ -22,7 +22,7 @@ use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterface
class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
@ -44,7 +44,7 @@ class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterf
public function getDescription(): string
{
return 'Get the sum of appointment durations according to various filters';
return 'Get the sum of calendar durations according to various filters';
}
public function getGroup(): string
@ -78,7 +78,7 @@ class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterf
public function getTitle(): string
{
return 'Sum of appointment durations';
return 'Sum of calendar durations';
}
public function getType(): string

View File

@ -37,7 +37,7 @@ class AgentFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('cal.user', ':agents');
$clause = $qb->expr()->in('cal.mainUser', ':agents');
if ($where instanceof Andx) {
$where->add($clause);
@ -76,12 +76,12 @@ class AgentFilter implements FilterInterface
return [
'Filtered by agent: only %agents%', [
'%agents' => implode(', ou ', $users),
'%agents' => implode(', ', $users),
], ];
}
public function getTitle(): string
{
return 'Filter appointments by agent';
return 'Filter calendars by agent';
}
}

View File

@ -66,7 +66,7 @@ class BetweenDatesFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by appointments between %dateFrom% and %dateTo%', [
return ['Filtered by calendars between %dateFrom% and %dateTo%', [
'%dateFrom%' => $data['date_from']->format('d-m-Y'),
'%dateTo%' => $data['date_to']->format('d-m-Y'),
]];
@ -74,6 +74,6 @@ class BetweenDatesFilter implements FilterInterface
public function getTitle(): string
{
return 'Filter appointments between certain dates';
return 'Filter calendars between certain dates';
}
}

View File

@ -0,0 +1,109 @@
<?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);
/*
* 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\CalendarBundle\Export\Filter;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class CalendarRangeFilter implements FilterInterface
{
private const CHOICES = [
'Not made within a calendar range' => true,
'Made within a calendar range' => false,
];
private const DEFAULT_CHOICE = false;
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
dump($data);
if ($data['hasCalendarRange']) {
$clause = $qb->expr()->isNotNull('cal.calendarRange');
} else {
$clause = $qb->expr()->isNull('cal.calendarRange');
}
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('hasCalendarRange', ChoiceType::class, [
'choices' => self::CHOICES,
'label' => 'has calendar range',
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function describeAction($data, $format = 'string'): array
{
foreach (self::CHOICES as $k => $v) {
if ($v === $data['hasCalendarRange']) {
$choice = $k;
} else {
$choice = 'Not made within a calendar range';
}
}
return [
'Filtered by calendar range: only %calendarRange%', [
'%calendarRange%' => $this->translator->trans($choice),
],
];
}
public function getTitle(): string
{
return 'Filter by calendar range';
}
}

View File

@ -44,7 +44,7 @@ class JobFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.user', 'caluser');
$qb->join('cal.mainUser', 'caluser');
}
$where = $qb->getDQLPart('where');
@ -90,12 +90,12 @@ class JobFilter implements FilterInterface
}
return ['Filtered by agent job: only %jobs%', [
'%jobs%' => implode(', ou ', $userJobs),
'%jobs%' => implode(', ', $userJobs),
]];
}
public function getTitle(): string
{
return 'Filter appointments by agent job';
return 'Filter calendars by agent job';
}
}

View File

@ -44,7 +44,7 @@ class ScopeFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.user', 'caluser');
$qb->join('cal.mainUser', 'caluser');
}
$where = $qb->getDQLPart('where');
@ -90,12 +90,12 @@ class ScopeFilter implements FilterInterface
}
return ['Filtered by agent scope: only %scopes%', [
'%scopes%' => implode(', ou ', $scopes),
'%scopes%' => implode(', ', $scopes),
]];
}
public function getTitle()
{
return 'Filter appointments by agent scope';
return 'Filter calendars by agent scope';
}
}

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Form\DataTransformer;
use Chill\CalendarBundle\Repository\CalendarRangeRepository;

View File

@ -0,0 +1,52 @@
<?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\CalendarBundle\Menu;
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Translation\TranslatorInterface;
class PersonMenuBuilder implements LocalMenuBuilderInterface
{
protected TranslatorInterface $translator;
private Security $security;
public function __construct(
Security $security,
TranslatorInterface $translator
) {
$this->security = $security;
$this->translator = $translator;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
$person = $parameters['person'];
if ($this->security->isGranted(CalendarVoter::SEE, $person)) {
$menu->addChild($this->translator->trans('Calendar'), [
'route' => 'chill_calendar_calendar_list_by_person',
'routeParameters' => [
'id' => $person->getId(),
], ])
->setExtras(['order' => 198]);
}
}
public static function getMenuIds(): array
{
return ['person'];
}
}

View File

@ -12,51 +12,27 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\TaskBundle\Templating\UI\CountNotificationTask;
use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Translation\TranslatorInterface;
class UserMenuBuilder implements LocalMenuBuilderInterface
{
/**
* @var AuthorizationCheckerInterface
*/
public $authorizationChecker;
public TranslatorInterface $translator;
/**
* @var CountNotificationTask
*/
public $counter;
/**
* @var TokenStorageInterface
*/
public $tokenStorage;
/**
* @var TranslatorInterface
*/
public $translator;
private Security $security;
public function __construct(
CountNotificationTask $counter,
TokenStorageInterface $tokenStorage,
TranslatorInterface $translator,
AuthorizationCheckerInterface $authorizationChecker
Security $security,
TranslatorInterface $translator
) {
$this->counter = $counter;
$this->tokenStorage = $tokenStorage;
$this->security = $security;
$this->translator = $translator;
$this->authorizationChecker = $authorizationChecker;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
$user = $this->tokenStorage->getToken()->getUser();
if ($this->authorizationChecker->isGranted('ROLE_USER')) {
if ($this->security->isGranted('ROLE_USER')) {
$menu->addChild('My calendar list', [
'route' => 'chill_calendar_calendar_list_my',
])

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Doctrine;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Doctrine;
use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\CalendarRangeRemovedMessage;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\CalendarRangeMessage;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\CalendarRemovedMessage;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\InviteUpdateMessage;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\MSGraphChangeNotificationMessage;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\Invite;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Messenger\Message;
class MSGraphChangeNotificationMessage

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\Address;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\User;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\Location;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\User;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Redis\ChillRedis;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\User;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use LogicException;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteToLocalSync;
use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteToLocalSync;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Connector;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\DependencyInjection;
use Chill\CalendarBundle\Command\AzureGrantAdminConsentAndAcquireToken;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\RemoteCalendar\Model;
use DateTimeImmutable;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,20 +9,35 @@
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\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepositoryInterface;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
{
private AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository;
private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em)
{
public function __construct(
AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository,
EntityManagerInterface $em
) {
$this->accompanyingPeriodACLAwareRepository = $accompanyingPeriodACLAwareRepository;
$this->em = $em;
}
@ -49,6 +64,46 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb;
}
/**
* Base implementation. The list of allowed accompanying period is retrieved "manually" from @see{AccompanyingPeriodACLAwareRepository}.
*/
public function buildQueryByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder
{
// find the reachable accompanying periods for person
$periods = $this->accompanyingPeriodACLAwareRepository->findByPerson($person, AccompanyingPeriodVoter::SEE);
$qb = $this->em->createQueryBuilder()
->from(Calendar::class, 'c');
$qb
->where(
$qb->expr()->orX(
// the calendar where the person is the main person:
$qb->expr()->eq('c.person', ':person'),
// when the calendar is in a reachable period, and contains person
$qb->expr()->andX(
$qb->expr()->in('c.accompanyingPeriod', ':periods'),
$qb->expr()->isMemberOf(':person', 'c.persons')
)
)
)
->setParameter('person', $person)
->setParameter('periods', $periods);
// filter by date
if (null !== $startDate) {
$qb->andWhere($qb->expr()->gte('c.startDate', ':startDate'))
->setParameter('startDate', $startDate);
}
if (null !== $endDate) {
$qb->andWhere($qb->expr()->lte('c.endDate', ':endDate'))
->setParameter('endDate', $endDate);
}
return $qb;
}
public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
{
$qb = $this->buildQueryByAccompanyingPeriod($period, $startDate, $endDate)->select('count(c)');
@ -56,6 +111,14 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb->getQuery()->getSingleScalarResult();
}
public function countByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
{
return $this->buildQueryByPerson($person, $startDate, $endDate)
->select('COUNT(c)')
->getQuery()
->getSingleScalarResult();
}
/**
* @return array|Calendar[]
*/
@ -77,4 +140,24 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb->getQuery()->getResult();
}
public function findByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array
{
$qb = $this->buildQueryByPerson($person, $startDate, $endDate)
->select('c');
foreach ($orderBy as $sort => $order) {
$qb->addOrderBy('c.' . $sort, $order);
}
if (null !== $offset) {
$qb->setFirstResult($offset);
}
if (null !== $limit) {
$qb->setMaxResults($limit);
}
return $qb->getQuery()->getResult();
}
}

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,18 +9,43 @@
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\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
interface CalendarACLAwareRepositoryInterface
{
public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int;
/**
* Return the number or calendars associated with a person. See condition on @see{self::findByPerson}.
*/
public function countByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int;
/**
* @return array|Calendar[]
*/
public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array;
/**
* Return all the calendars which are associated with a person, either on @see{Calendar::person} or within.
*
* @see{Calendar::persons}. The calendar may be associated with a person, or an accompanyingPeriod.
*
* The method may assume that the user is allowed to see the person, but must check that the user is allowed
* to see the calendar's accompanyingPeriod, if any.
*
* @return array|Calendar[]
*/
public function findByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array;
}

View File

@ -0,0 +1,52 @@
<?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\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\CalendarDoc;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class CalendarDocRepository implements ObjectRepository, CalendarDocRepositoryInterface
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository($this->getClassName());
}
public function find($id): ?CalendarDoc
{
return $this->repository->find($id);
}
public function findAll(): array
{
return $this->repository->findAll();
}
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null)
{
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
}
public function findOneBy(array $criteria): ?CalendarDoc
{
return $this->findOneBy($criteria);
}
public function getClassName()
{
return CalendarDoc::class;
}
}

View File

@ -0,0 +1,33 @@
<?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\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\CalendarDoc;
interface CalendarDocRepositoryInterface
{
public function find($id): ?CalendarDoc;
/**
* @return array|CalendarDoc[]
*/
public function findAll(): array;
/**
* @return array|CalendarDoc[]
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null);
public function findOneBy(array $criteria): ?CalendarDoc;
public function getClassName();
}

View File

@ -48,6 +48,11 @@ class CalendarRepository implements ObjectRepository
->getSingleScalarResult();
}
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
{
return $this->repository->createQueryBuilder($alias, $indexBy);
}
public function find($id): ?Calendar
{
return $this->repository->find($id);

View File

@ -1,104 +1,118 @@
#services:
#
# Indicators
# chill.calendar.export.count_appointments:
# class: Chill\CalendarBundle\Export\Export\CountAppointments
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export, alias: count_appointments }
#
# chill.calendar.export.average_duration_appointments:
# class: Chill\CalendarBundle\Export\Export\StatAppointmentAvgDuration
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export, alias: average_duration_appointments }
#
# chill.calendar.export.sum_duration_appointments:
# class: Chill\CalendarBundle\Export\Export\StatAppointmentSumDuration
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export, alias: sum_duration_appointments }
#
# Filters
#
# chill.calendar.export.agent_filter:
# class: Chill\CalendarBundle\Export\Filter\AgentFilter
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_filter, alias: agent_filter }
#
# chill.calendar.export.job_filter:
# class: Chill\CalendarBundle\Export\Filter\JobFilter
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_filter, alias: job_filter }
#
# chill.calendar.export.scope_filter:
# class: Chill\CalendarBundle\Export\Filter\ScopeFilter
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_filter, alias: scope_filter }
#
# chill.calendar.export.between_dates_filter:
# class: Chill\CalendarBundle\Export\Filter\BetweenDatesFilter
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_filter, alias: between_dates_filter }
#
# Aggregator
#
# chill.calendar.export.agent_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\AgentAggregator
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_aggregator, alias: agent_aggregator }
#
# chill.calendar.export.job_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\JobAggregator
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_aggregator, alias: job_aggregator }
#
# chill.calendar.export.scope_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\ScopeAggregator
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_aggregator, alias: scope_aggregator }
#
# chill.calendar.export.location_type_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\LocationTypeAggregator
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_aggregator, alias: location_type_aggregator }
#
# chill.calendar.export.location_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\LocationAggregator
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_aggregator, alias: location_aggregator }
#
# chill.calendar.export.cancel_reason_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\CancelReasonAggregator
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_aggregator, alias: cancel_reason_aggregator }
#
# chill.calendar.export.month_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\MonthYearAggregator
# autowire: true
# autoconfigure: true
# tags:
# - { name: chill.export_aggregator, alias: month_aggregator }
services:
## Indicators
chill.calendar.export.count_calendars:
class: Chill\CalendarBundle\Export\Export\CountCalendars
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: count_calendars }
chill.calendar.export.average_duration_calendars:
class: Chill\CalendarBundle\Export\Export\StatCalendarAvgDuration
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: average_duration_calendars }
chill.calendar.export.sum_duration_calendars:
class: Chill\CalendarBundle\Export\Export\StatCalendarSumDuration
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: sum_duration_calendars }
## Filters
chill.calendar.export.agent_filter:
class: Chill\CalendarBundle\Export\Filter\AgentFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: agent_filter }
chill.calendar.export.job_filter:
class: Chill\CalendarBundle\Export\Filter\JobFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: job_filter }
chill.calendar.export.scope_filter:
class: Chill\CalendarBundle\Export\Filter\ScopeFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: scope_filter }
chill.calendar.export.between_dates_filter:
class: Chill\CalendarBundle\Export\Filter\BetweenDatesFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: between_dates_filter }
chill.calendar.export.calendar_range_filter:
class: Chill\CalendarBundle\Export\Filter\CalendarRangeFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: calendar_range_filter }
## Aggregator
chill.calendar.export.agent_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\AgentAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: agent_aggregator }
chill.calendar.export.job_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\JobAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: job_aggregator }
chill.calendar.export.scope_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\ScopeAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: scope_aggregator }
chill.calendar.export.location_type_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\LocationTypeAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: location_type_aggregator }
chill.calendar.export.location_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\LocationAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: location_aggregator }
chill.calendar.export.cancel_reason_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\CancelReasonAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: cancel_reason_aggregator }
chill.calendar.export.month_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\MonthYearAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: month_aggregator }
chill.calendar.export.urgency_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\UrgencyAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: urgency_aggregator }

View File

@ -0,0 +1,45 @@
{% if calendar.documents|length > 0 %}
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %}
<style lang="css">
--bs-btn-padding-y: .25rem; --bs-btn-padding-x: .5rem; --bs-btn-font-size: .75rem;
</style>
<div class="accompanying_course_work-list">
<table class="obj-res-eval my-3">
<thead>
<th class="eval">
<h4 class="title_label">{{ 'chill_calendar.Documents'|trans }}</h4>
</th>
</thead>
<tbody>
{% for d in calendar.documents %}
<tr>
<td class="eval">
<ul class="eval_title">
<li>
{{ mm.mimeIcon(d.storedObject.type) }}
{{ d.storedObject.title }}
<ul class="record_actions small inline">
{% if chill_document_is_editable(d.storedObject) %}
<li>
{{ d.storedObject|chill_document_edit_button }}
</li>
{% endif %}
<li>
{{ m.download_button(d.storedObject, d.storedObject.title) }}
</li>
</ul>
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}

View File

@ -0,0 +1,177 @@
{# list used in context of person or accompanyingPeriod #}
{{ filterOrder|chill_render_filter_order_helper }}
{% if calendarItems|length > 0 %}
<div class="flex-table list-records context-accompanyingCourse">
{% for calendar in calendarItems %}
<div class="item-bloc">
<div class="item-row main">
<div class="item-col">
<div class="wrap-header">
<div class="wl-row">
<div class="wl-col title">
<p class="date-label">
{% if context == 'person' and calendar.context == 'accompanying_period' %}
<a href="{{ chill_path_add_return_path('chill_person_accompanying_course_index', {'accompanying_period_id': calendar.accompanyingPeriod.id}) }}" style="text-decoration: none;">
<span class="badge bg-primary">
<i class="fa fa-random"></i> {{ calendar.accompanyingPeriod.id }}
</span>
</a>
{% endif %}
{% if calendar.endDate.diff(calendar.startDate).days >= 1 %}
{{ calendar.startDate|format_datetime('short', 'short') }}
- {{ calendar.endDate|format_datetime('short', 'short') }}
{% else %}
{{ calendar.startDate|format_datetime('short', 'short') }}
- {{ calendar.endDate|format_datetime('none', 'short') }}
{% endif %}
</p>
<div class="duration short-message">
<i class="fa fa-fw fa-hourglass-end"></i>
{{ calendar.duration|date('%H:%I') }}
{% if false == calendar.sendSMS or null == calendar.sendSMS %}
<!-- no sms will be send -->
{% else %}
{% if calendar.smsStatus == 'sms_sent' %}
<span title="{{ 'SMS already sent'|trans }}" class="badge bg-info">
<i class="fa fa-check "></i>
<i class="fa fa-envelope "></i>
</span>
{% else %}
<span title="{{ 'Will send SMS'|trans }}" class="badge bg-info">
<i class="fa fa-envelope "></i>
<i class="fa fa-hourglass-end "></i>
</span>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
<div class="item-col">
<ul class="list-content">
{% if calendar.mainUser is not empty %}
<span class="badge-user">{{ calendar.mainUser|chill_entity_render_box }}</span>
{% endif %}
</ul>
</div>
</div>
</div>
{% if calendar.comment.comment is not empty
or calendar.users|length > 0
or calendar.thirdParties|length > 0
or calendar.users|length > 0 %}
<div class="item-row details separator">
<div class="item-col">
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {
'context': calendar.context == 'person' ? 'calendar_person' : 'calendar_accompanyingCourse',
'render': 'wrap-list',
'entity': calendar
} %}
</div>
</div>
{% endif %}
{% if calendar.comment.comment is not empty %}
<div class="item-row details separator">
<div class="item-col comment">
{{ calendar.comment|chill_entity_render_box( { 'limit_lines': 3, 'metadata': false } ) }}
</div>
</div>
{% endif %}
{% if calendar.location is not empty %}
<div class="item-row separator">
<div>
{% if calendar.location.address is not same as(null) and calendar.location.name is not empty %}
<i class="fa fa-map-marker"></i>{% endif %}
{% if calendar.location.name is not empty %}{{ calendar.location.name }}{% endif %}
{% if calendar.location.address is not same as(null) %}{{ calendar.location.address|chill_entity_render_box({'multiline': false, 'with_picto': (calendar.location.name is empty)}) }}{% else %}
<i class="fa fa-map-marker"></i>{% endif %}
{% if calendar.location.phonenumber1 is not empty %}<i
class="fa fa-phone"></i> {{ calendar.location.phonenumber1|chill_format_phonenumber }}{% endif %}
{% if calendar.location.phonenumber2 is not empty %}<i
class="fa fa-phone"></i> {{ calendar.location.phonenumber2|chill_format_phonenumber }}{% endif %}
</div>
</div>
{% endif %}
<div class="item-row separator column">
<div>
{{ include('@ChillCalendar/Calendar/_documents.twig.html') }}
</div>
</div>
<div class="item-row">
<ul class="record_actions">
{% if is_granted('CHILL_CALENDAR_CALENDAR_SEE', calendar) and hasDocs %}
<li>
<a class="btn btn-create"
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_pick_template', {'id': calendar.id }) }}">
{{ 'chill_calendar.Add a document'|trans }}
</a>
</li>
{% endif %}
{% if accompanyingCourse is defined and is_granted('CHILL_ACTIVITY_CREATE', accompanyingCourse) and calendar.activity is null %}
<li>
<a class="btn btn-create"
href="{{ chill_path_add_return_path('chill_calendar_calendar_to_activity', { 'id': calendar.id }) }}">
{{ 'Transform to activity'|trans }}
</a>
</li>
{% endif %}
{% if (calendar.isInvited(app.user)) %}
{% set invite = calendar.inviteForUser(app.user) %}
<li>
<div invite-answer data-status="{{ invite.status|e('html_attr') }}"
data-calendar-id="{{ calendar.id|e('html_attr') }}"></div>
</li>
{% endif %}
{% if false %}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_show', { 'id': calendar.id}) }}"
class="btn btn-show "></a>
</li>
{% endif %}
{# TOOD
{% if is_granted('CHILL_ACTIVITY_UPDATE', calendar) %}
#}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_edit', { 'id': calendar.id }) }}"
class="btn btn-update "></a>
</li>
{# TOOD
{% endif %}
{% if is_granted('CHILL_ACTIVITY_DELETE', calendar) %}
#}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_delete', { 'id': calendar.id } ) }}"
class="btn btn-delete "></a>
</li>
{#
{% endif %}
#}
</ul>
</div>
</div>
{% endfor %}
{% if calendarItems|length < paginator.getTotalItems %}
{{ chill_pagination(paginator) }}
{% endif %}
</div>
{% endif %}

View File

@ -0,0 +1,16 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
{% block title 'Remove calendar item'|trans %}
{% block content %}
{{ include('@ChillMain/Util/confirmation_template.html.twig',
{
'title' : 'Remove calendar item'|trans,
'confirm_question' : 'Are you sure you want to remove the calendar item?'|trans,
'cancel_route' : 'chill_calendar_calendar_list_by_period',
'cancel_parameters' : { 'id' : person.id },
'form' : delete_form
} ) }}
{% endblock %}

View File

@ -77,11 +77,14 @@
{{ 'Cancel'|trans|chill_return_path_label }}
</a>
</li>
<li>
<button class="btn btn-create" type="submit">
{{ 'Save'|trans }}
</button>
</li>
{% if form.save_and_create_doc is defined %}
<li>
{{ form_widget(form.save_and_create_doc, { 'attr' : { 'class' : 'btn btn-create' }, 'label': 'chill_calendar.Save and add a document'|trans }) }}
</li>
{% endif %}
<li>
{{ form_widget(form.save, { 'attr' : { 'class' : 'btn btn-create' }, 'label': 'Save'|trans }) }}
</li>
</ul>
{{ form_end(form) }}

View File

@ -0,0 +1,34 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
{% block title 'Update calendar'|trans %}
{% block content %}
<div class="calendar-edit">
{% include 'ChillCalendarBundle:Calendar:edit.html.twig' with {'context': 'person'} %}
</div>
{% endblock %}
{% block js %}
{{ parent() }}
<script type="text/javascript">
window.entity = {{ entity_json|json_encode|raw }};
window.startDate = {{ entity.startDate|date('Y-m-d H:i:s')|json_encode|raw }};
window.endDate = {{ entity.endDate|date('Y-m-d H:i:s')|json_encode|raw }};
window.mainUser = {{ entity.mainUser.id }};
</script>
{{ encore_entry_script_tags('vue_calendar') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('vue_calendar') }}
{{ encore_entry_link_tags('page_calendar') }}
{% endblock %}
{% block block_post_menu %}
<div id="calendarControls"></div>
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
@ -10,186 +10,44 @@
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_answer') }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_answer') }}
{{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %}
{% block content %}
<h1>{{ 'Calendar list' |trans }}</h1>
{{ filterOrder|chill_render_filter_order_helper }}
<h1>{{ 'Calendar list' |trans }}</h1>
{% if calendarItems|length == 0 %}
<p class="chill-no-data-statement">
{{ "There is no calendar items."|trans }}
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-create button-small"></a>
</p>
{% else %}
<p class="chill-no-data-statement">
{{ "There is no calendar items."|trans }}
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}"
class="btn btn-create button-small"></a>
</p>
{% else %}
{{ include('@ChillCalendar/Calendar/_list.html.twig', {context: 'accompanying_course'}) }}
{% endif %}
<div class="flex-table list-records context-accompanyingCourse">
{% for calendar in calendarItems %}
<div class="item-bloc">
<div class="item-row main">
<div class="item-col">
<div class="wrap-header">
<div class="wl-row">
<div class="wl-col title">
{% if calendar.endDate.diff(calendar.startDate).days >= 1 %}
<p class="date-label">{{ calendar.startDate|format_datetime('short', 'short') }} - {{ calendar.endDate|format_datetime('short', 'short') }}</p>
{% else %}
<p class="date-label">{{ calendar.startDate|format_datetime('short', 'short') }} - {{ calendar.endDate|format_datetime('none', 'short') }}</p>
{% endif %}
<div class="duration short-message">
<p>
<i class="fa fa-fw fa-hourglass-end"></i>
{{ calendar.duration|date('%H:%I')}}
</p>
</div>
</div>
</div>
</div>
<div class="item-col">
<ul class="list-content">
{% if calendar.mainUser is not empty %}
<span class="badge-user">{{ calendar.mainUser|chill_entity_render_box }}</span>
{% endif %}
</ul>
</div>
</div>
</div>
{%
if calendar.comment.comment is not empty
or calendar.users|length > 0
or calendar.thirdParties|length > 0
or calendar.users|length > 0
%}
<div class="item-row details separator">
<div class="item-col">
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {
'context': 'calendar_accompanyingCourse',
'render': 'wrap-list',
'entity': calendar
} %}
</div>
</div>
{% endif %}
{% if calendar.comment.comment is not empty %}
<div class="item-row details separator">
<div class="item-col comment">
{{ calendar.comment|chill_entity_render_box( { 'limit_lines': 3, 'metadata': false } ) }}
</div>
</div>
{% endif %}
{% if calendar.location is not empty %}
<div class="item-row separator">
<div>
{% if calendar.location.address is not same as(null) and calendar.location.name is not empty %}<i class="fa fa-map-marker"></i>{% endif %}
{% if calendar.location.name is not empty %}{{ calendar.location.name }}{% endif %}
{% if calendar.location.address is not same as(null) %}{{ calendar.location.address|chill_entity_render_box({'multiline': false, 'with_picto': (calendar.location.name is empty)}) }}{% else %}<i class="fa fa-map-marker"></i>{% endif %}
{% if calendar.location.phonenumber1 is not empty %}<i class="fa fa-phone"></i> {{ calendar.location.phonenumber1|chill_format_phonenumber }}{% endif %}
{% if calendar.location.phonenumber2 is not empty %}<i class="fa fa-phone"></i> {{ calendar.location.phonenumber2|chill_format_phonenumber }}{% endif %}
</div>
</div>
{% endif %}
<div class="item-row separator">
<div>
{% if false == calendar.sendSMS or null == calendar.sendSMS %}
<span title="{{ 'Will not send SMS'|trans }}" class="fa-stack">
<i class="fa fa-envelope fa-stack-1x"></i>
<i class="fa fa-ban text-danger fa-stack-2x"></i>
</span>
{% else %}
{% if calendar.smsStatus == 'sms_sent' %}
<span title="{{ 'SMS already sent'|trans }}">
<i class="fa fa-envelope"></i>
<i class="fa fa-check"></i>
</span>
{% else %}
<span title="{{ 'Will send SMS'|trans }}">
<i class="fa fa-bell"></i>
<i class="fa fa-envelope"></i>
</span>
{% endif %}
{% endif %}
</div>
<ul class="record_actions">
{% if is_granted('CHILL_ACTIVITY_CREATE', accompanyingCourse) %}
<li>
<a class="btn btn-create" href="{{ chill_path_add_return_path('chill_calendar_calendar_to_activity', { 'id': calendar.id }) }}">
{{ 'Transform to activity'|trans }}
</a>
</li>
{% endif %}
{% if (calendar.isInvited(app.user)) %}
{% set invite = calendar.inviteForUser(app.user) %}
<li>
<div invite-answer data-status="{{ invite.status|e('html_attr') }}" data-calendar-id="{{ calendar.id|e('html_attr') }}"></div>
</li>
{% endif %}
{% if false %}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_show', { 'id': calendar.id, 'user_id': user_id, 'accompanying_period_id': accompanying_course_id }) }}" class="btn btn-show "></a>
</li>
{% endif %}
{# TOOD
{% if is_granted('CHILL_ACTIVITY_UPDATE', calendar) %}
#}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_edit', { 'id': calendar.id, 'user_id': user_id, 'accompanying_period_id': accompanying_course_id }) }}" class="btn btn-update "></a>
</li>
{# TOOD
{% endif %}
{% if is_granted('CHILL_ACTIVITY_DELETE', calendar) %}
#}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_delete', { 'id': calendar.id, 'user_id' : user_id, 'accompanying_period_id': accompanying_course_id } ) }}" class="btn btn-delete "></a>
</li>
{#
{% endif %}
#}
</ul>
</div>
</div>
{% endfor %}
{% if calendarItems|length < paginator.getTotalItems %}
{{ chill_pagination(paginator) }}
<ul class="record_actions sticky-form-buttons">
{% if accompanyingCourse.user is not same as(null) %}
<li>
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id, 'mainUser': accompanyingCourse.user.id }) }}"
class="btn btn-create">
{{ 'chill_calendar.Create for referrer'|trans }}
</a>
</li>
{% endif %}
</div>
{% endif %}
<ul class="record_actions sticky-form-buttons">
{% if accompanyingCourse.user is not same as(null) %}
<li>
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id, 'mainUser': accompanyingCourse.user.id }) }}" class="btn btn-create">
{{ 'chill_calendar.Create for referrer'|trans }}
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}"
class="btn btn-create">
{{ 'Create'|trans }}
</a>
</li>
{% endif %}
<li>
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-create">
{{ 'Create'|trans }}
</a>
</li>
</ul>
</ul>
{% endblock %}

View File

@ -0,0 +1,52 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
{% block title %}{{ 'Calendar list' |trans }}{% endblock title %}
{% set user_id = null %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_answer') }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_answer') }}
{{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %}
{% block content %}
<h1>{{ 'Calendar list' |trans }}</h1>
{% if calendarItems|length == 0 %}
<p class="chill-no-data-statement">
{{ "There is no calendar items."|trans }}
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'person_id': person.id}) }}"
class="btn btn-create btn-sm">
{{ 'Create'|trans }}
</a>
</p>
{% else %}
{{ include ('@ChillCalendar/Calendar/_list.html.twig', {context: 'person'}) }}
{% endif %}
<ul class="record_actions sticky-form-buttons">
<li>
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'person_id': person.id, 'mainUser': app.user.id }) }}"
class="btn btn-create">
{{ 'chill_calendar.Create for me'|trans }}
</a>
</li>
<li>
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'person_id': person.id}) }}"
class="btn btn-create">
{{ 'Create'|trans }}
</a>
</li>
</ul>
{% endblock %}

View File

@ -69,7 +69,7 @@
<a
class="btn btn-cancel"
{%- if context == 'person' -%}
href="{{ chill_return_path_or('chill_calendar_calendar_list', { 'person_id': person.id } )}}"
href="{{ chill_return_path_or('chill_calendar_calendar_list_by_person', { 'id': person.id } )}}"
{%- else -%}
href="{{ chill_return_path_or('chill_calendar_calendar_list_by_period', { 'id': accompanyingCourse.id } )}}"
{%- endif -%}
@ -77,10 +77,13 @@
{{ 'Cancel'|trans|chill_return_path_label }}
</a>
</li>
{% if form.save_and_create_doc is defined %}
<li>
{{ form_widget(form.save_and_create_doc, { 'attr' : { 'class' : 'btn btn-create' }, 'label': 'chill_calendar.Create and add a document'|trans }) }}
</li>
{% endif %}
<li>
<button class="btn btn-create" type="submit">
{{ 'Create'|trans }}
</button>
{{ form_widget(form.save, { 'attr' : { 'class' : 'btn btn-create' }, 'label': 'Create'|trans }) }}
</li>
</ul>
{{ form_end(form) }}

View File

@ -0,0 +1,34 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_calendar_calendar_new' %}
{% block title 'Calendar item creation' |trans %}
{% block content %}
<div class="calendar-new">
<div id="calendar"></div> {# <=== vue component #}
{% include 'ChillCalendarBundle:Calendar:new.html.twig' with {'context': 'person'} %}
</div>
{% endblock %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_pickentity_type') }}
<script type="text/javascript">
window.entity = {{ entity_json|json_encode|raw }};
{% if app.user.currentLocation is not null %}window.default_location_id = {{ app.user.currentLocation.id }};{% endif %};
</script>
{{ encore_entry_script_tags('vue_calendar') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('vue_calendar') }}
{{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %}
{% block block_post_menu %}
<div id="calendarControls"></div>
{% endblock %}

View File

@ -0,0 +1,24 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
{% block title %}{{ 'chill_calendar.Add a document' |trans }}{% endblock title %}
{% set user_id = null %}
{% set accompanying_course_id = accompanyingCourse.id %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_docgen_picktemplate') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_docgen_picktemplate') }}
{% endblock %}
{% block content %}
<div data-docgen-template-picker="data-docgen-template-picker" data-entity-id="{{ calendar.id }}" data-entity-class="{{ 'Chill\\CalendarBundle\\Entity\\Calendar'|e('html_attr') }}"></div>
{% endblock %}

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Security\Voter;
use Chill\CalendarBundle\Entity\Calendar;
@ -17,7 +24,9 @@ use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use LogicException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
@ -44,6 +53,7 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
$this->voterHelper = $voterHelperFactory
->generate(self::class)
->addCheckFor(AccompanyingPeriod::class, [self::SEE])
->addCheckFor(Person::class, [self::SEE])
->addCheckFor(Calendar::class, [self::SEE, self::CREATE, self::EDIT, self::DELETE])
->build();
}
@ -82,6 +92,14 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
// we first check here that the user has read access to the period
return $this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject);
default:
throw new LogicException('subject not implemented');
}
} elseif ($subject instanceof Person) {
switch ($attribute) {
case self::SEE:
return $this->security->isGranted(PersonVoter::SEE, $subject);
default:
throw new LogicException('subject not implemented');
}
@ -96,6 +114,16 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
case self::DELETE:
return $this->security->isGranted(AccompanyingPeriodVoter::EDIT, $period);
}
} elseif (null !== $person = $subject->getPerson()) {
switch ($attribute) {
case self::SEE:
case self::EDIT:
case self::CREATE:
return $this->security->isGranted(PersonVoter::SEE, $person);
case self::DELETE:
return $this->security->isGranted(PersonVoter::UPDATE, $person);
}
}
}

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Security\Voter;
use Chill\CalendarBundle\Entity\Invite;

View File

@ -0,0 +1,249 @@
<?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\CalendarBundle\Service\DocGenerator;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Entity\CalendarDoc;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Templating\Entity\PersonRender;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use function count;
final class CalendarContext implements CalendarContextInterface
{
private BaseContextData $baseContextData;
private EntityManagerInterface $entityManager;
private NormalizerInterface $normalizer;
private PersonRender $personRender;
private ThirdPartyRender $thirdPartyRender;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
BaseContextData $baseContextData,
EntityManagerInterface $entityManager,
NormalizerInterface $normalizer,
PersonRender $personRender,
ThirdPartyRender $thirdPartyRender,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->baseContextData = $baseContextData;
$this->entityManager = $entityManager;
$this->normalizer = $normalizer;
$this->personRender = $personRender;
$this->thirdPartyRender = $thirdPartyRender;
$this->translatableStringHelper = $translatableStringHelper;
}
public function adminFormReverseTransform(array $data): array
{
return array_merge(
[
'trackDatetime' => true,
'askMainPerson' => true,
'mainPersonLabel' => 'docgen.calendar.Destinee',
'askThirdParty' => false,
'thirdPartyLabel' => 'Third party',
],
$data
);
}
public function adminFormTransform(array $data): array
{
return $data;
}
public function buildAdminForm(FormBuilderInterface $builder): void
{
$builder
->add('trackDatetime', CheckboxType::class, [
'required' => false,
'label' => 'docgen.calendar.Track changes on datetime and warn user if date time is updated after the doc generation',
])
->add('askMainPerson', CheckboxType::class, [
'required' => false,
'label' => 'docgen.calendar.Ask main person',
])
->add('mainPersonLabel', TextType::class, [
'required' => false,
'label' => 'docgen.calendar.Main person label',
])
->add('askThirdParty', CheckboxType::class, [
'required' => false,
'label' => 'docgen.calendar.Ask third party',
])
->add('thirdPartyLabel', TextType::class, [
'required' => false,
'label' => 'docgen.calendar.Third party label',
]);
}
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
{
$options = $this->getOptions($template);
$builder->add('title', TextType::class, [
'required' => true,
'label' => 'docgen.calendar.title of the generated document',
'data' => $this->translatableStringHelper->localize($template->getName()),
]);
if ($options['askMainPerson']) {
$builder->add('mainPerson', EntityType::class, [
'class' => Person::class,
'multiple' => false,
'label' => $options['mainPersonLabel'] ?? 'docgen.calendar.Main person label',
'required' => false,
'choices' => $entity->getPersons(),
'choice_label' => fn (Person $p) => $this->personRender->renderString($p, []),
'expanded' => false,
]);
}
if ($options['askThirdParty']) {
$builder->add('thirdParty', EntityType::class, [
'class' => ThirdParty::class,
'multiple' => false,
'label' => $options['thirdPartyLabel'] ?? 'Third party',
'choices' => $entity->getProfessionals(),
'choice_label' => fn (ThirdParty $tp) => $this->thirdPartyRender->renderString($tp, []),
'expanded' => false,
]);
}
}
/**
* @param array{mainPerson?: Person, thirdParty?: ThirdParty, title: string} $contextGenerationData
* @param mixed $entity
*/
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
{
$options = $this->getOptions($template);
$data = array_merge(
$this->baseContextData->getData(),
[
'calendar' => $this->normalizer->normalize($entity, 'docgen', ['docgen:expects' => Calendar::class, 'groups' => ['docgen:read']]),
]
);
if ($options['askMainPerson']) {
$data['mainPerson'] = $this->normalizer->normalize($contextGenerationData['mainPerson'] ?? null, 'docgen', [
'docgen:expects' => Person::class,
'groups' => ['docgen:read'],
'docgen:person:with-household' => true,
'docgen:person:with-relations' => true,
'docgen:person:with-budget' => true,
]);
}
if ($options['askThirdParty']) {
$data['thirdParty'] = $this->normalizer->normalize($contextGenerationData['thirdParty'] ?? null, 'docgen', [
'docgen:expects' => ThirdParty::class,
'groups' => ['docgen:read'],
]);
}
return $data;
}
public function getDescription(): string
{
return 'docgen.calendar.A base context for generating document on calendar';
}
public function getEntityClass(): string
{
return Calendar::class;
}
public function getFormData(DocGeneratorTemplate $template, $entity): array
{
$options = $this->getOptions($template);
$data = [];
if ($options['askMainPerson']) {
$data['mainPerson'] = null;
if (1 === count($entity->getPersons())) {
$data['mainPerson'] = $entity->getPersons()->first();
}
}
if ($options['askThirdParty']) {
$data['thirdParty'] = null;
if (1 === count($entity->getProfessionals())) {
$data['thirdParty'] = $entity->getProfessionals()->first();
}
}
return $data;
}
public static function getKey(): string
{
return self::class;
}
public function getName(): string
{
return 'docgen.calendar.Base context for calendar';
}
public function hasAdminForm(): bool
{
return true;
}
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
{
return true;
}
/**
* @param array{mainPerson?: Person, thirdParty?: ThirdParty, title: string} $contextGenerationData
*/
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$options = $this->getOptions($template);
$storedObject->setTitle($contextGenerationData['title']);
$doc = new CalendarDoc($entity, $storedObject);
$doc->setTrackDateTimeVersion($options['trackDatetime']);
$this->entityManager->persist($doc);
}
/**
* @return array{askMainPerson: bool, mainPersonLabel: ?string, askThirdParty: bool, thirdPartyLabel: ?string, trackDateTime: bool} $options
*/
private function getOptions(DocGeneratorTemplate $template): array
{
return $template->getOptions();
}
}

View File

@ -0,0 +1,63 @@
<?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\CalendarBundle\Service\DocGenerator;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocStoreBundle\Entity\StoredObject;
use Symfony\Component\Form\FormBuilderInterface;
interface CalendarContextInterface extends DocGeneratorContextWithPublicFormInterface, DocGeneratorContextWithAdminFormInterface
{
public function adminFormReverseTransform(array $data): array;
public function adminFormTransform(array $data): array;
public function buildAdminForm(FormBuilderInterface $builder): void;
/**
* @param Calendar $entity
*/
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void;
/**
* @param Calendar $entity
*/
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array;
public function getDescription(): string;
public function getEntityClass(): string;
/**
* @param Calendar $entity
*/
public function getFormData(DocGeneratorTemplate $template, $entity): array;
public static function getKey(): string;
public function getName(): string;
public function hasAdminForm(): bool;
/**
* @param Calendar $entity
*/
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool;
/**
* @param Calendar $entity
*/
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void;
}

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Service\ShortMessageNotification;
use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php
/**
* Chill is a software for social workers
* 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.
@ -9,6 +9,13 @@
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\CalendarBundle\Service\ShortMessageNotification;
use Chill\CalendarBundle\Entity\Calendar;

Some files were not shown because too many files have changed in this diff Show More