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_. **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 .. code-block:: bash
@ -280,6 +280,30 @@ Exemple, for running test inside `main` bundle:
# run tests # run tests
bin/phpunit src/Bundle/path/to/your/test 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 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\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use function array_key_exists; use function array_key_exists;

View File

@ -62,7 +62,7 @@ class ByCreatorFilter implements FilterInterface
} }
return ['Filtered activity by creator: only %users%', [ 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\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Form\Type\PickSocialActionType;
use Chill\PersonBundle\Templating\Entity\SocialActionRender; use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use function in_array; use function in_array;
@ -37,22 +36,17 @@ class BySocialActionFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where');
if (!in_array('actsocialaction', $qb->getAllAliases(), true)) { if (!in_array('actsocialaction', $qb->getAllAliases(), true)) {
$qb->join('activity.socialActions', 'actsocialaction'); $qb->join('activity.socialActions', 'actsocialaction');
} }
$clause = $qb->expr()->in('actsocialaction.id', ':socialactions'); $clause = $qb->expr()->in('actsocialaction.id', ':socialactions');
if ($where instanceof Andx) { $qb->andWhere($clause)
$where->add($clause); ->setParameter(
} else { 'socialactions',
$where = $qb->expr()->andX($clause); SocialAction::getDescendantsWithThisForActions($data['accepted_socialactions'])
} );
$qb->add('where', $where);
$qb->setParameter('socialactions', $data['accepted_socialactions']);
} }
public function applyOn(): string public function applyOn(): string
@ -62,13 +56,8 @@ class BySocialActionFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('accepted_socialactions', EntityType::class, [ $builder->add('accepted_socialactions', PickSocialActionType::class, [
'class' => SocialAction::class,
'choice_label' => function (SocialAction $sa) {
return $this->actionRender->renderString($sa, []);
},
'multiple' => true, 'multiple' => true,
'expanded' => true,
]); ]);
} }
@ -76,12 +65,14 @@ class BySocialActionFilter implements FilterInterface
{ {
$actions = []; $actions = [];
foreach ($data['accepted_socialactions'] as $sa) { foreach ($data['accepted_socialactions'] as $action) {
$actions[] = $this->actionRender->renderString($sa, []); $actions[] = $this->actionRender->renderString($action, [
'show_and_children' => true,
]);
} }
return ['Filtered activity by linked socialaction: only %actions%', [ 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\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Form\Type\PickSocialIssueType;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender; use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use function in_array; use function in_array;
@ -37,22 +36,17 @@ class BySocialIssueFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where');
if (!in_array('actsocialissue', $qb->getAllAliases(), true)) { if (!in_array('actsocialissue', $qb->getAllAliases(), true)) {
$qb->join('activity.socialIssues', 'actsocialissue'); $qb->join('activity.socialIssues', 'actsocialissue');
} }
$clause = $qb->expr()->in('actsocialissue.id', ':socialissues'); $clause = $qb->expr()->in('actsocialissue.id', ':socialissues');
if ($where instanceof Andx) { $qb->andWhere($clause)
$where->add($clause); ->setParameter(
} else { 'socialissues',
$where = $qb->expr()->andX($clause); SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues'])
} );
$qb->add('where', $where);
$qb->setParameter('socialissues', $data['accepted_socialissues']);
} }
public function applyOn(): string public function applyOn(): string
@ -62,13 +56,8 @@ class BySocialIssueFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('accepted_socialissues', EntityType::class, [ $builder->add('accepted_socialissues', PickSocialIssueType::class, [
'class' => SocialIssue::class,
'choice_label' => function (SocialIssue $si) {
return $this->issueRender->renderString($si, []);
},
'multiple' => true, 'multiple' => true,
'expanded' => true,
]); ]);
} }
@ -76,12 +65,14 @@ class BySocialIssueFilter implements FilterInterface
{ {
$issues = []; $issues = [];
foreach ($data['accepted_socialissues'] as $si) { foreach ($data['accepted_socialissues'] as $issue) {
$issues[] = $this->issueRender->renderString($si, []); $issues[] = $this->issueRender->renderString($issue, [
'show_and_children' => true,
]);
} }
return ['Filtered activity by linked socialissue: only %issues%', [ 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\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\Select2LocationTypeType; use Chill\MainBundle\Form\Type\PickLocationTypeType;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
@ -60,7 +60,7 @@ class LocationTypeFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('accepted_locationtype', Select2LocationTypeType::class, [ $builder->add('accepted_locationtype', PickLocationTypeType::class, [
'multiple' => true, 'multiple' => true,
//'label' => false, //'label' => false,
]); ]);
@ -77,7 +77,7 @@ class LocationTypeFilter implements FilterInterface
} }
return ['Filtered activity by locationtype: only %types%', [ 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%', [ 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%', [ 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%', [ 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 %} {% endmacro %}
{% set blocks = [] %} {% 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' %} {% if context == 'person' %}
{% set blocks = blocks|merge([{ {% set blocks = blocks|merge([{
'title': 'Others persons'|trans, 'title': 'Others persons'|trans,
@ -54,7 +54,7 @@
}]) %} }]) %}
{% endif %} {% endif %}
{% 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([{ {% set blocks = blocks|merge([{
'title': 'Third parties'|trans, 'title': 'Third parties'|trans,
'items': entity.thirdParties, 'items': entity.thirdParties,
@ -63,7 +63,7 @@
'key' : 'id', 'key' : 'id',
}]) %} }]) %}
{% endif %} {% 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([{ {% set blocks = blocks|merge([{
'title': 'Users concerned'|trans, 'title': 'Users concerned'|trans,
'items': entity.users, 'items': entity.users,
@ -143,7 +143,7 @@
{% if bloc.type == 'user' %} {% if bloc.type == 'user' %}
<span class="badge-user"> <span class="badge-user">
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }} {{ 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) %} {% set invite = entity.inviteForUser(item) %}
{% if invite is not null %} {% if invite is not null %}
{{ invite.invite_span(invite) }} {{ invite.invite_span(invite) }}

View File

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

View File

@ -119,15 +119,15 @@ Activity Presences: Presences aux activités
# Crud # Crud
crud: crud:
activity_type: activity_type:
title_new: Nouveau type d'activité title_new: Nouveau type d'activité
title_edit: Edition d'un type d'activité title_edit: Edition d'un type d'activité
activity_type_category: activity_type_category:
title_new: Nouvelle catégorie de type d'activité title_new: Nouvelle catégorie de type d'activité
title_edit: Edition d'une catégorie de type d'activité title_edit: Edition d'une catégorie de type d'activité
activity_presence: activity_presence:
title_new: Nouvelle Présence aux activités title_new: Nouvelle Présence aux activités
title_edit: Edition d'une Présence aux activités title_edit: Edition d'une Présence aux activités
# activity reason admin # activity reason admin
ActivityReason list: Liste des sujets ActivityReason list: Liste des sujets
@ -322,3 +322,13 @@ docgen:
A basic context for activity: Contexte pour les échanges 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: 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. 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 <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Command;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MachineTokenStorage; use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MachineTokenStorage;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Command;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\EventsOnUserSubscriptionCreator; use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\EventsOnUserSubscriptionCreator;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Command;
use Chill\CalendarBundle\Service\ShortMessageNotification\BulkCalendarShortMessageSender; use Chill\CalendarBundle\Service\ShortMessageNotification\BulkCalendarShortMessageSender;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Command;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -15,18 +15,23 @@ use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Form\CalendarType; use Chill\CalendarBundle\Form\CalendarType;
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface; use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface;
use Chill\CalendarBundle\Repository\CalendarACLAwareRepositoryInterface; use Chill\CalendarBundle\Repository\CalendarACLAwareRepositoryInterface;
use Chill\CalendarBundle\Repository\CalendarRepository; use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactory; 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\FilterOrderHelper;
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface; use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; 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 Chill\ThirdPartyBundle\Entity\ThirdParty;
use DateTimeImmutable; use DateTimeImmutable;
use Exception; use Exception;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Form; use Symfony\Component\Form\Form;
@ -40,9 +45,11 @@ use Symfony\Component\Serializer\SerializerInterface;
class CalendarController extends AbstractController class CalendarController extends AbstractController
{ {
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private CalendarACLAwareRepositoryInterface $calendarACLAwareRepository; private CalendarACLAwareRepositoryInterface $calendarACLAwareRepository;
private CalendarRepository $calendarRepository; private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private FilterOrderHelperFactoryInterface $filterOrderHelperFactory; private FilterOrderHelperFactoryInterface $filterOrderHelperFactory;
@ -50,29 +57,35 @@ class CalendarController extends AbstractController
private PaginatorFactory $paginator; private PaginatorFactory $paginator;
private PersonRepository $personRepository;
private RemoteCalendarConnectorInterface $remoteCalendarConnector; private RemoteCalendarConnectorInterface $remoteCalendarConnector;
private SerializerInterface $serializer; private SerializerInterface $serializer;
private UserRepository $userRepository; private UserRepositoryInterface $userRepository;
public function __construct( public function __construct(
CalendarRepository $calendarRepository,
CalendarACLAwareRepositoryInterface $calendarACLAwareRepository, CalendarACLAwareRepositoryInterface $calendarACLAwareRepository,
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
FilterOrderHelperFactoryInterface $filterOrderHelperFactory, FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
LoggerInterface $logger, LoggerInterface $logger,
PaginatorFactory $paginator, PaginatorFactory $paginator,
RemoteCalendarConnectorInterface $remoteCalendarConnector, RemoteCalendarConnectorInterface $remoteCalendarConnector,
SerializerInterface $serializer, SerializerInterface $serializer,
UserRepository $userRepository PersonRepository $personRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
UserRepositoryInterface $userRepository
) { ) {
$this->calendarRepository = $calendarRepository;
$this->calendarACLAwareRepository = $calendarACLAwareRepository; $this->calendarACLAwareRepository = $calendarACLAwareRepository;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->filterOrderHelperFactory = $filterOrderHelperFactory; $this->filterOrderHelperFactory = $filterOrderHelperFactory;
$this->logger = $logger; $this->logger = $logger;
$this->paginator = $paginator; $this->paginator = $paginator;
$this->remoteCalendarConnector = $remoteCalendarConnector; $this->remoteCalendarConnector = $remoteCalendarConnector;
$this->serializer = $serializer; $this->serializer = $serializer;
$this->personRepository = $personRepository;
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
$this->userRepository = $userRepository; $this->userRepository = $userRepository;
} }
@ -83,19 +96,21 @@ class CalendarController extends AbstractController
*/ */
public function deleteAction(Request $request, Calendar $entity) public function deleteAction(Request $request, Calendar $entity)
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$accompanyingPeriod = $entity->getAccompanyingPeriod(); [$person, $accompanyingPeriod] = [$entity->getPerson(), $entity->getAccompanyingPeriod()];
$user = null; // TODO legacy code ? remove it ?
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/confirm_deleteByAccompanyingCourse.html.twig'; $view = '@ChillCalendar/Calendar/confirm_deleteByAccompanyingCourse.html.twig';
} elseif ($user instanceof User) { $redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]);
$view = '@ChillCalendar/Calendar/confirm_deleteByUser.html.twig'; } 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) { if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request); $form->handleRequest($request);
@ -112,20 +127,15 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator') $this->addFlash('success', $this->get('translator')
->trans('The calendar item has been successfully removed.')); ->trans('The calendar item has been successfully removed.'));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); return new RedirectResponse($redirectRoute);
return $this->redirectToRoute('chill_calendar_calendar_list_by_period', $params);
} }
} }
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, [ return $this->render($view, [
'calendar' => $entity, 'calendar' => $entity,
'delete_form' => $form->createView(), 'delete_form' => $form->createView(),
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'person' => $person,
]); ]);
} }
@ -140,19 +150,27 @@ class CalendarController extends AbstractController
return $this->remoteCalendarConnector->getMakeReadyResponse($request->getUri()); return $this->remoteCalendarConnector->getMakeReadyResponse($request->getUri());
} }
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = [$entity->getPerson(), $entity->getAccompanyingPeriod()];
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/editByAccompanyingCourse.html.twig'; $view = '@ChillCalendar/Calendar/editByAccompanyingCourse.html.twig';
} elseif ($user instanceof User) { $redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]);
throw new Exception('to analyze'); } elseif ($person instanceof Person) {
$view = '@ChillCalendar/Calendar/editByUser.html.twig'; $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); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
@ -160,29 +178,24 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!')); $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()) { if ($form->isSubmitted() && !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors')); $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']); $entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [ return $this->render($view, [
'entity' => $entity, 'entity' => $entity,
'form' => $form->createView(), 'form' => $form->createView(),
'delete_form' => $deleteForm->createView(), 'accompanyingCourse' => $entity->getAccompanyingPeriod(),
'accompanyingCourse' => $accompanyingPeriod, 'person' => $entity->getPerson(),
// 'user' => $user,
'entity_json' => $entity_array, 'entity_json' => $entity_array,
]); ]);
} }
@ -214,6 +227,38 @@ class CalendarController extends AbstractController
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'paginator' => $paginator, 'paginator' => $paginator,
'filterOrder' => $filterOrder, '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; $view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
$entity = new Calendar();
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig'; $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')) { if ($request->query->has('mainUser')) {
$entity->setMainUser($this->userRepository->find($request->query->getInt('mainUser'))); $entity->setMainUser($this->userRepository->find($request->query->getInt('mainUser')));
} }
// if ($user instanceof User) { $form = $this->createForm(CalendarType::class, $entity)
// $entity->setPerson($user); ->add('save', SubmitType::class);
// }
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if (0 < $this->docGeneratorTemplateRepository->countByEntity(Calendar::class)) {
$entity->setAccompanyingPeriod($accompanyingPeriod); $form->add('save_and_create_doc', SubmitType::class);
} }
$form = $this->createForm(CalendarType::class, $entity);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
@ -285,9 +331,11 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item created!')); $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()) { if ($form->isSubmitted() && !$form->isValid()) {
@ -301,7 +349,8 @@ class CalendarController extends AbstractController
$entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); $entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [ return $this->render($view, [
'user' => $user, 'context' => $entity->getContext(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity, 'entity' => $entity,
'form' => $form->createView(), 'form' => $form->createView(),
@ -316,6 +365,7 @@ class CalendarController extends AbstractController
*/ */
public function showAction(Request $request, int $id): Response public function showAction(Request $request, int $id): Response
{ {
throw new Exception('not implemented');
$view = null; $view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
@ -450,49 +500,45 @@ class CalendarController extends AbstractController
/** /**
* Creates a form to delete a Calendar entity by id. * 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() return $this->createFormBuilder()
->setAction($this->generateUrl('chill_calendar_calendar_delete', $params)) ->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()]))
->setMethod('DELETE') ->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete']) ->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm(); ->getForm();
} }
/**
* @return array{0: ?Person, 1: ?AccompanyingPeriod}
*/
private function getEntity(Request $request): array private function getEntity(Request $request): array
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$user = $accompanyingPeriod = null; $person = $accompanyingPeriod = null;
if ($request->query->has('user_id')) { if ($request->query->has('person_id')) {
$user_id = $request->get('user_id'); $person = $this->personRepository->find($request->query->getInt('person_id'));
$user = $em->getRepository(User::class)->find($user_id);
if (null === $user) { if (null === $person) {
throw $this->createNotFoundException('User not found'); throw $this->createNotFoundException('Person not found');
} }
// TODO Add permission $this->denyAccessUnlessGranted(PersonVoter::SEE, $person);
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $user);
} elseif ($request->query->has('accompanying_period_id')) { } elseif ($request->query->has('accompanying_period_id')) {
$accompanying_period_id = $request->get('accompanying_period_id'); $accompanyingPeriod = $this->accompanyingPeriodRepository->find($request->query->getInt('accompanying_period_id'));
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id);
if (null === $accompanyingPeriod) { if (null === $accompanyingPeriod) {
throw $this->createNotFoundException('Accompanying Period not found'); throw $this->createNotFoundException('Accompanying Period not found');
} }
// TODO Add permission $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod);
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
} else { } else {
throw $this->createNotFoundException('Person or Accompanying Period not found'); throw $this->createNotFoundException('Person or Accompanying Period not found');
} }
return [ 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 <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Controller;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Controller;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\OnBehalfOfUserTokenStorage; use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\OnBehalfOfUserTokenStorage;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Controller;
use Chill\CalendarBundle\Messenger\Message\MSGraphChangeNotificationMessage; use Chill\CalendarBundle\Messenger\Message\MSGraphChangeNotificationMessage;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Controller;
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface; 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 <> ''"})} * uniqueConstraints={@ORM\UniqueConstraint(name="idx_calendar_remote", columns={"remoteId"}, options={"where": "remoteId <> ''"})}
* ) * )
* @ORM\Entity * @ORM\Entity
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
* "chill_calendar_calendar": Calendar::class
* })
*/ */
class Calendar implements TrackCreationInterface, TrackUpdateInterface class Calendar implements TrackCreationInterface, TrackUpdateInterface
{ {
@ -89,7 +92,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
/** /**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod", inversedBy="calendars") * @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod", inversedBy="calendars")
*/ */
private AccompanyingPeriod $accompanyingPeriod; private ?AccompanyingPeriod $accompanyingPeriod = null;
/** /**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\Activity") * @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\Activity")
@ -109,13 +112,24 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
/** /**
* @ORM\Embedded(class=CommentEmbeddable::class, columnPrefix="comment_") * @ORM\Embedded(class=CommentEmbeddable::class, columnPrefix="comment_")
* @Serializer\Groups({"calendar:read", "read"}) * @Serializer\Groups({"calendar:read", "read", "docgen:read"})
*/ */
private CommentEmbeddable $comment; 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) * @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") * @Assert\NotNull(message="calendar.An end date is required")
*/ */
private ?DateTimeImmutable $endDate = null; private ?DateTimeImmutable $endDate = null;
@ -124,7 +138,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
* @ORM\Id * @ORM\Id
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
* @Serializer\Groups({"calendar:read", "read", "calendar:light"}) * @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"})
*/ */
private ?int $id = null; private ?int $id = null;
@ -136,29 +150,35 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
* cascade={"persist", "remove", "merge", "detach"} * cascade={"persist", "remove", "merge", "detach"}
* ) * )
* @ORM\JoinTable(name="chill_calendar.calendar_to_invites") * @ORM\JoinTable(name="chill_calendar.calendar_to_invites")
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "docgen:read"})
*/ */
private Collection $invites; private Collection $invites;
/** /**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location") * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location")
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read", "docgen:read"})
* @Assert\NotNull(message="calendar.A location is required") * @Assert\NotNull(message="calendar.A location is required")
*/ */
private ?Location $location = null; private ?Location $location = null;
/** /**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User") * @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"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
* @Assert\NotNull(message="calendar.A main user is mandatory") * @Assert\NotNull(message="calendar.A main user is mandatory")
*/ */
private ?User $mainUser = null; 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\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person", inversedBy="calendars")
* @ORM\JoinTable(name="chill_calendar.calendar_to_persons") * @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"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
* @Assert\Count(min=1, minMessage="calendar.At least {{ limit }} person is required.") * @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\ManyToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
* @ORM\JoinTable(name="chill_calendar.calendar_to_thirdparties") * @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"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
*/ */
private Collection $professionals; private Collection $professionals;
/** /**
* @ORM\Column(type="boolean", nullable=true) * @ORM\Column(type="boolean", nullable=true)
* @Serializer\Groups({"docgen:read"})
*/ */
private ?bool $sendSMS = false; private ?bool $sendSMS = false;
@ -190,7 +211,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
/** /**
* @ORM\Column(type="datetime_immutable", nullable=false) * @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"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"})
* @Assert\NotNull(message="calendar.A start date is required") * @Assert\NotNull(message="calendar.A start date is required")
*/ */
@ -203,15 +224,34 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
*/ */
private string $status = self::STATUS_VALID; private string $status = self::STATUS_VALID;
/**
* @ORM\Column(type="boolean", nullable=true)
* @Serializer\Groups({"docgen:read"})
*/
private ?bool $urgent = false;
public function __construct() public function __construct()
{ {
$this->comment = new CommentEmbeddable(); $this->comment = new CommentEmbeddable();
$this->documents = new ArrayCollection();
$this->privateComment = new PrivateCommentEmbeddable(); $this->privateComment = new PrivateCommentEmbeddable();
$this->persons = new ArrayCollection(); $this->persons = new ArrayCollection();
$this->professionals = new ArrayCollection(); $this->professionals = new ArrayCollection();
$this->invites = 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 * @internal Use {@link (Calendar::addUser)} instead
*/ */
@ -277,6 +317,38 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this->comment; 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 public function getDuration(): ?DateInterval
{ {
if ($this->getStartDate() === null || $this->getEndDate() === null) { if ($this->getStartDate() === null || $this->getEndDate() === null) {
@ -329,6 +401,11 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this->mainUser; return $this->mainUser;
} }
public function getPerson(): ?Person
{
return $this->person;
}
/** /**
* @return Collection|Person[] * @return Collection|Person[]
*/ */
@ -409,6 +486,11 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this->getProfessionals(); return $this->getProfessionals();
} }
public function getUrgent(): ?bool
{
return $this->urgent;
}
/** /**
* @return Collection|User[] * @return Collection|User[]
* @Serializer\Groups({"calendar:read", "read"}) * @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 * @internal Use {@link (Calendar::removeUser)} instead
*/ */
@ -544,6 +638,10 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
public function setEndDate(DateTimeImmutable $endDate): self public function setEndDate(DateTimeImmutable $endDate): self
{ {
if (null === $this->endDate || $this->endDate->getTimestamp() !== $endDate->getTimestamp()) {
$this->increaseaDatetimeVersion();
}
$this->endDate = $endDate; $this->endDate = $endDate;
return $this; return $this;
@ -568,6 +666,13 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this; return $this;
} }
public function setPerson(?Person $person): Calendar
{
$this->person = $person;
return $this;
}
public function setPrivateComment(PrivateCommentEmbeddable $privateComment): self public function setPrivateComment(PrivateCommentEmbeddable $privateComment): self
{ {
$this->privateComment = $privateComment; $this->privateComment = $privateComment;
@ -591,6 +696,10 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
public function setStartDate(DateTimeImmutable $startDate): self public function setStartDate(DateTimeImmutable $startDate): self
{ {
if (null === $this->startDate || $this->startDate->getTimestamp() !== $startDate->getTimestamp()) {
$this->increaseaDatetimeVersion();
}
$this->startDate = $startDate; $this->startDate = $startDate;
return $this; return $this;
@ -606,4 +715,16 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface
return $this; 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"}) * @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; private string $status = self::PENDING;
/** /**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User") * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @ORM\JoinColumn(nullable=false) * @ORM\JoinColumn(nullable=false)
* @Serializer\Groups(groups={"calendar:read", "read"}) * @Serializer\Groups(groups={"calendar:read", "read", "docgen:read"})
*/ */
private ?User $user = null; private ?User $user = null;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;

View File

@ -42,7 +42,7 @@ final class AgentAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
if (!in_array('caluser', $qb->getAllAliases(), true)) { if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.user', 'caluser'); $qb->join('cal.mainUser', 'caluser');
} }
$qb->addSelect('caluser.id AS agent_aggregator'); $qb->addSelect('caluser.id AS agent_aggregator');
@ -79,6 +79,6 @@ final class AgentAggregator implements AggregatorInterface
public function getTitle(): string 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) 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)) { if (!in_array('calcancel', $qb->getAllAliases(), true)) {
$qb->join('cal.cancelReason', 'calcancel'); $qb->join('cal.cancelReason', 'calcancel');
} }
@ -82,6 +82,6 @@ class CancelReasonAggregator implements AggregatorInterface
public function getTitle(): string 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) public function alterQuery(QueryBuilder $qb, $data)
{ {
if (!in_array('caluser', $qb->getAllAliases(), true)) { 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'); $qb->addSelect('IDENTITY(caluser.userJob) as job_aggregator');
@ -81,6 +81,6 @@ final class JobAggregator implements AggregatorInterface
public function getTitle(): string 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 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 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 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) public function alterQuery(QueryBuilder $qb, $data)
{ {
if (!in_array('caluser', $qb->getAllAliases(), true)) { 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'); $qb->addSelect('IDENTITY(caluser.mainScope) as scope_aggregator');
@ -81,6 +81,6 @@ final class ScopeAggregator implements AggregatorInterface
public function getTitle(): string 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\AbstractQuery;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Process\Exception\LogicException; use Symfony\Component\Validator\Exception\LogicException;
use Symfony\Component\Security\Core\Role\Role;
class CountAppointments implements ExportInterface, GroupedExportInterface class CountCalendars implements ExportInterface, GroupedExportInterface
{ {
private CalendarRepository $calendarRepository; private CalendarRepository $calendarRepository;
@ -45,7 +44,7 @@ class CountAppointments implements ExportInterface, GroupedExportInterface
public function getDescription(): string public function getDescription(): string
{ {
return 'Count appointments by various parameters.'; return 'Count calendars by various parameters.';
} }
public function getGroup(): string public function getGroup(): string
@ -79,7 +78,7 @@ class CountAppointments implements ExportInterface, GroupedExportInterface
public function getTitle(): string public function getTitle(): string
{ {
return 'Count appointments'; return 'Count calendars';
} }
public function getType(): string public function getType(): string

View File

@ -22,7 +22,7 @@ use Doctrine\ORM\QueryBuilder;
use LogicException; use LogicException;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterface class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface
{ {
private CalendarRepository $calendarRepository; private CalendarRepository $calendarRepository;
@ -44,7 +44,7 @@ class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterf
public function getDescription(): string 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 public function getGroup(): string
@ -78,7 +78,7 @@ class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterf
public function getTitle(): string public function getTitle(): string
{ {
return 'Average appointment duration'; return 'Average calendar duration';
} }
public function getType(): string public function getType(): string

View File

@ -22,7 +22,7 @@ use Doctrine\ORM\QueryBuilder;
use LogicException; use LogicException;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterface class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface
{ {
private CalendarRepository $calendarRepository; private CalendarRepository $calendarRepository;
@ -44,7 +44,7 @@ class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterf
public function getDescription(): string 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 public function getGroup(): string
@ -78,7 +78,7 @@ class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterf
public function getTitle(): string public function getTitle(): string
{ {
return 'Sum of appointment durations'; return 'Sum of calendar durations';
} }
public function getType(): string public function getType(): string

View File

@ -37,7 +37,7 @@ class AgentFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('cal.user', ':agents'); $clause = $qb->expr()->in('cal.mainUser', ':agents');
if ($where instanceof Andx) { if ($where instanceof Andx) {
$where->add($clause); $where->add($clause);
@ -76,12 +76,12 @@ class AgentFilter implements FilterInterface
return [ return [
'Filtered by agent: only %agents%', [ 'Filtered by agent: only %agents%', [
'%agents' => implode(', ou ', $users), '%agents' => implode(', ', $users),
], ]; ], ];
} }
public function getTitle(): string 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 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'), '%dateFrom%' => $data['date_from']->format('d-m-Y'),
'%dateTo%' => $data['date_to']->format('d-m-Y'), '%dateTo%' => $data['date_to']->format('d-m-Y'),
]]; ]];
@ -74,6 +74,6 @@ class BetweenDatesFilter implements FilterInterface
public function getTitle(): string 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) public function alterQuery(QueryBuilder $qb, $data)
{ {
if (!in_array('caluser', $qb->getAllAliases(), true)) { if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.user', 'caluser'); $qb->join('cal.mainUser', 'caluser');
} }
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@ -90,12 +90,12 @@ class JobFilter implements FilterInterface
} }
return ['Filtered by agent job: only %jobs%', [ return ['Filtered by agent job: only %jobs%', [
'%jobs%' => implode(', ou ', $userJobs), '%jobs%' => implode(', ', $userJobs),
]]; ]];
} }
public function getTitle(): string 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) public function alterQuery(QueryBuilder $qb, $data)
{ {
if (!in_array('caluser', $qb->getAllAliases(), true)) { if (!in_array('caluser', $qb->getAllAliases(), true)) {
$qb->join('cal.user', 'caluser'); $qb->join('cal.mainUser', 'caluser');
} }
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@ -90,12 +90,12 @@ class ScopeFilter implements FilterInterface
} }
return ['Filtered by agent scope: only %scopes%', [ return ['Filtered by agent scope: only %scopes%', [
'%scopes%' => implode(', ou ', $scopes), '%scopes%' => implode(', ', $scopes),
]]; ]];
} }
public function getTitle() public function getTitle()
{ {
return 'Filter appointments by agent scope'; return 'Filter calendars by agent scope';
} }
} }

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Form\DataTransformer;
use Chill\CalendarBundle\Repository\CalendarRangeRepository; 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; namespace Chill\CalendarBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\TaskBundle\Templating\UI\CountNotificationTask;
use Knp\Menu\MenuItem; use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
class UserMenuBuilder implements LocalMenuBuilderInterface class UserMenuBuilder implements LocalMenuBuilderInterface
{ {
/** public TranslatorInterface $translator;
* @var AuthorizationCheckerInterface
*/
public $authorizationChecker;
/** private Security $security;
* @var CountNotificationTask
*/
public $counter;
/**
* @var TokenStorageInterface
*/
public $tokenStorage;
/**
* @var TranslatorInterface
*/
public $translator;
public function __construct( public function __construct(
CountNotificationTask $counter, Security $security,
TokenStorageInterface $tokenStorage, TranslatorInterface $translator
TranslatorInterface $translator,
AuthorizationCheckerInterface $authorizationChecker
) { ) {
$this->counter = $counter; $this->security = $security;
$this->tokenStorage = $tokenStorage;
$this->translator = $translator; $this->translator = $translator;
$this->authorizationChecker = $authorizationChecker;
} }
public function buildMenu($menuId, MenuItem $menu, array $parameters) public function buildMenu($menuId, MenuItem $menu, array $parameters)
{ {
$user = $this->tokenStorage->getToken()->getUser(); if ($this->security->isGranted('ROLE_USER')) {
if ($this->authorizationChecker->isGranted('ROLE_USER')) {
$menu->addChild('My calendar list', [ $menu->addChild('My calendar list', [
'route' => 'chill_calendar_calendar_list_my', 'route' => 'chill_calendar_calendar_list_my',
]) ])

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Doctrine;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Doctrine;
use Chill\CalendarBundle\Entity\CalendarRange; use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\CalendarRangeRemovedMessage; use Chill\CalendarBundle\Messenger\Message\CalendarRangeRemovedMessage;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\CalendarRangeMessage; use Chill\CalendarBundle\Messenger\Message\CalendarRangeMessage;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\CalendarRemovedMessage; use Chill\CalendarBundle\Messenger\Message\CalendarRemovedMessage;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Entity\CalendarRange; use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\InviteUpdateMessage; use Chill\CalendarBundle\Messenger\Message\InviteUpdateMessage;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Handler;
use Chill\CalendarBundle\Messenger\Message\MSGraphChangeNotificationMessage; use Chill\CalendarBundle\Messenger\Message\MSGraphChangeNotificationMessage;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\CalendarRange; use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\CalendarRange; use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Message;
use Chill\CalendarBundle\Entity\Invite; use Chill\CalendarBundle\Entity\Invite;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Messenger\Message;
class MSGraphChangeNotificationMessage class MSGraphChangeNotificationMessage

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\Address;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Location;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait; use League\OAuth2\Client\Tool\BearerAuthorizationTrait;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Redis\ChillRedis; use Chill\MainBundle\Redis\ChillRedis;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait; use League\OAuth2\Client\Tool\BearerAuthorizationTrait;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use LogicException; use LogicException;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteToLocalSync;
use Chill\CalendarBundle\Entity\CalendarRange; use Chill\CalendarBundle\Entity\CalendarRange;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\RemoteToLocalSync;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Connector;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\DependencyInjection;
use Chill\CalendarBundle\Command\AzureGrantAdminConsentAndAcquireToken; use Chill\CalendarBundle\Command\AzureGrantAdminConsentAndAcquireToken;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\RemoteCalendar\Model;
use DateTimeImmutable; use DateTimeImmutable;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,20 +9,35 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepositoryInterface;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use DateTimeImmutable; use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
{ {
private AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository;
private EntityManagerInterface $em; private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em) public function __construct(
{ AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository,
EntityManagerInterface $em
) {
$this->accompanyingPeriodACLAwareRepository = $accompanyingPeriodACLAwareRepository;
$this->em = $em; $this->em = $em;
} }
@ -49,6 +64,46 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb; 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 public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int
{ {
$qb = $this->buildQueryByAccompanyingPeriod($period, $startDate, $endDate)->select('count(c)'); $qb = $this->buildQueryByAccompanyingPeriod($period, $startDate, $endDate)->select('count(c)');
@ -56,6 +111,14 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb->getQuery()->getSingleScalarResult(); 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[] * @return array|Calendar[]
*/ */
@ -77,4 +140,24 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
return $qb->getQuery()->getResult(); 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 <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,18 +9,43 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable; use DateTimeImmutable;
interface CalendarACLAwareRepositoryInterface interface CalendarACLAwareRepositoryInterface
{ {
public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int; 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[] * @return array|Calendar[]
*/ */
public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array; 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(); ->getSingleScalarResult();
} }
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
{
return $this->repository->createQueryBuilder($alias, $indexBy);
}
public function find($id): ?Calendar public function find($id): ?Calendar
{ {
return $this->repository->find($id); return $this->repository->find($id);

View File

@ -1,104 +1,118 @@
#services: services:
#
# Indicators ## Indicators
# chill.calendar.export.count_appointments: chill.calendar.export.count_calendars:
# class: Chill\CalendarBundle\Export\Export\CountAppointments class: Chill\CalendarBundle\Export\Export\CountCalendars
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export, alias: count_appointments } - { name: chill.export, alias: count_calendars }
#
# chill.calendar.export.average_duration_appointments: chill.calendar.export.average_duration_calendars:
# class: Chill\CalendarBundle\Export\Export\StatAppointmentAvgDuration class: Chill\CalendarBundle\Export\Export\StatCalendarAvgDuration
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export, alias: average_duration_appointments } - { name: chill.export, alias: average_duration_calendars }
#
# chill.calendar.export.sum_duration_appointments: chill.calendar.export.sum_duration_calendars:
# class: Chill\CalendarBundle\Export\Export\StatAppointmentSumDuration class: Chill\CalendarBundle\Export\Export\StatCalendarSumDuration
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export, alias: sum_duration_appointments } - { name: chill.export, alias: sum_duration_calendars }
#
# Filters ## Filters
#
# chill.calendar.export.agent_filter: chill.calendar.export.agent_filter:
# class: Chill\CalendarBundle\Export\Filter\AgentFilter class: Chill\CalendarBundle\Export\Filter\AgentFilter
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_filter, alias: agent_filter } - { name: chill.export_filter, alias: agent_filter }
#
# chill.calendar.export.job_filter: chill.calendar.export.job_filter:
# class: Chill\CalendarBundle\Export\Filter\JobFilter class: Chill\CalendarBundle\Export\Filter\JobFilter
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_filter, alias: job_filter } - { name: chill.export_filter, alias: job_filter }
#
# chill.calendar.export.scope_filter: chill.calendar.export.scope_filter:
# class: Chill\CalendarBundle\Export\Filter\ScopeFilter class: Chill\CalendarBundle\Export\Filter\ScopeFilter
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_filter, alias: scope_filter } - { name: chill.export_filter, alias: scope_filter }
#
# chill.calendar.export.between_dates_filter: chill.calendar.export.between_dates_filter:
# class: Chill\CalendarBundle\Export\Filter\BetweenDatesFilter class: Chill\CalendarBundle\Export\Filter\BetweenDatesFilter
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_filter, alias: between_dates_filter } - { name: chill.export_filter, alias: between_dates_filter }
#
# Aggregator chill.calendar.export.calendar_range_filter:
# class: Chill\CalendarBundle\Export\Filter\CalendarRangeFilter
# chill.calendar.export.agent_aggregator: autowire: true
# class: Chill\CalendarBundle\Export\Aggregator\AgentAggregator autoconfigure: true
# autowire: true tags:
# autoconfigure: true - { name: chill.export_filter, alias: calendar_range_filter }
# tags:
# - { name: chill.export_aggregator, alias: agent_aggregator } ## Aggregator
#
# chill.calendar.export.job_aggregator: chill.calendar.export.agent_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\JobAggregator class: Chill\CalendarBundle\Export\Aggregator\AgentAggregator
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_aggregator, alias: job_aggregator } - { name: chill.export_aggregator, alias: agent_aggregator }
#
# chill.calendar.export.scope_aggregator: chill.calendar.export.job_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\ScopeAggregator class: Chill\CalendarBundle\Export\Aggregator\JobAggregator
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_aggregator, alias: scope_aggregator } - { name: chill.export_aggregator, alias: job_aggregator }
#
# chill.calendar.export.location_type_aggregator: chill.calendar.export.scope_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\LocationTypeAggregator class: Chill\CalendarBundle\Export\Aggregator\ScopeAggregator
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_aggregator, alias: location_type_aggregator } - { name: chill.export_aggregator, alias: scope_aggregator }
#
# chill.calendar.export.location_aggregator: chill.calendar.export.location_type_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\LocationAggregator class: Chill\CalendarBundle\Export\Aggregator\LocationTypeAggregator
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_aggregator, alias: location_aggregator } - { name: chill.export_aggregator, alias: location_type_aggregator }
#
# chill.calendar.export.cancel_reason_aggregator: chill.calendar.export.location_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\CancelReasonAggregator class: Chill\CalendarBundle\Export\Aggregator\LocationAggregator
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_aggregator, alias: cancel_reason_aggregator } - { name: chill.export_aggregator, alias: location_aggregator }
#
# chill.calendar.export.month_aggregator: chill.calendar.export.cancel_reason_aggregator:
# class: Chill\CalendarBundle\Export\Aggregator\MonthYearAggregator class: Chill\CalendarBundle\Export\Aggregator\CancelReasonAggregator
# autowire: true autowire: true
# autoconfigure: true autoconfigure: true
# tags: tags:
# - { name: chill.export_aggregator, alias: month_aggregator } - { 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 }} {{ 'Cancel'|trans|chill_return_path_label }}
</a> </a>
</li> </li>
<li> {% if form.save_and_create_doc is defined %}
<button class="btn btn-create" type="submit"> <li>
{{ 'Save'|trans }} {{ form_widget(form.save_and_create_doc, { 'attr' : { 'class' : 'btn btn-create' }, 'label': 'chill_calendar.Save and add a document'|trans }) }}
</button> </li>
</li> {% endif %}
<li>
{{ form_widget(form.save, { 'attr' : { 'class' : 'btn btn-create' }, 'label': 'Save'|trans }) }}
</li>
</ul> </ul>
{{ form_end(form) }} {{ 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' %} {% set activeRouteKey = 'chill_calendar_calendar_list' %}
@ -10,186 +10,44 @@
{% block js %} {% block js %}
{{ parent() }} {{ parent() }}
{{ encore_entry_script_tags('mod_answer') }} {{ encore_entry_script_tags('mod_answer') }}
{{ encore_entry_script_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
{{ encore_entry_link_tags('mod_answer') }} {{ encore_entry_link_tags('mod_answer') }}
{{ encore_entry_link_tags('mod_async_upload') }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>{{ 'Calendar list' |trans }}</h1> <h1>{{ 'Calendar list' |trans }}</h1>
{{ filterOrder|chill_render_filter_order_helper }}
{% if calendarItems|length == 0 %} {% if calendarItems|length == 0 %}
<p class="chill-no-data-statement"> <p class="chill-no-data-statement">
{{ "There is no calendar items."|trans }} {{ "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> <a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}"
</p> class="btn btn-create button-small"></a>
{% else %} </p>
{% else %}
{{ include('@ChillCalendar/Calendar/_list.html.twig', {context: 'accompanying_course'}) }}
{% endif %}
<div class="flex-table list-records context-accompanyingCourse"> <ul class="record_actions sticky-form-buttons">
{% if accompanyingCourse.user is not same as(null) %}
{% for calendar in calendarItems %} <li>
<a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id, 'mainUser': accompanyingCourse.user.id }) }}"
<div class="item-bloc"> class="btn btn-create">
<div class="item-row main"> {{ 'chill_calendar.Create for referrer'|trans }}
<div class="item-col"> </a>
<div class="wrap-header"> </li>
<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) }}
{% endif %} {% endif %}
</div>
{% endif %}
<ul class="record_actions sticky-form-buttons">
{% if accompanyingCourse.user is not same as(null) %}
<li> <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"> <a href="{{ path('chill_calendar_calendar_new', {'user_id': user_id, 'accompanying_period_id': accompanying_course_id}) }}"
{{ 'chill_calendar.Create for referrer'|trans }} class="btn btn-create">
{{ 'Create'|trans }}
</a> </a>
</li> </li>
{% endif %} </ul>
<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>
{% endblock %} {% 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 <a
class="btn btn-cancel" class="btn btn-cancel"
{%- if context == 'person' -%} {%- 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 -%} {%- else -%}
href="{{ chill_return_path_or('chill_calendar_calendar_list_by_period', { 'id': accompanyingCourse.id } )}}" href="{{ chill_return_path_or('chill_calendar_calendar_list_by_period', { 'id': accompanyingCourse.id } )}}"
{%- endif -%} {%- endif -%}
@ -77,10 +77,13 @@
{{ 'Cancel'|trans|chill_return_path_label }} {{ 'Cancel'|trans|chill_return_path_label }}
</a> </a>
</li> </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> <li>
<button class="btn btn-create" type="submit"> {{ form_widget(form.save, { 'attr' : { 'class' : 'btn btn-create' }, 'label': 'Create'|trans }) }}
{{ 'Create'|trans }}
</button>
</li> </li>
</ul> </ul>
{{ form_end(form) }} {{ 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 <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Security\Voter;
use Chill\CalendarBundle\Entity\Calendar; 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\Authorization\VoterHelperInterface;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use LogicException; use LogicException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
@ -44,6 +53,7 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
$this->voterHelper = $voterHelperFactory $this->voterHelper = $voterHelperFactory
->generate(self::class) ->generate(self::class)
->addCheckFor(AccompanyingPeriod::class, [self::SEE]) ->addCheckFor(AccompanyingPeriod::class, [self::SEE])
->addCheckFor(Person::class, [self::SEE])
->addCheckFor(Calendar::class, [self::SEE, self::CREATE, self::EDIT, self::DELETE]) ->addCheckFor(Calendar::class, [self::SEE, self::CREATE, self::EDIT, self::DELETE])
->build(); ->build();
} }
@ -82,6 +92,14 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
// we first check here that the user has read access to the period // we first check here that the user has read access to the period
return $this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject); 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: default:
throw new LogicException('subject not implemented'); throw new LogicException('subject not implemented');
} }
@ -96,6 +114,16 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
case self::DELETE: case self::DELETE:
return $this->security->isGranted(AccompanyingPeriodVoter::EDIT, $period); 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 <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Security\Voter;
use Chill\CalendarBundle\Entity\Invite; 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 <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Service\ShortMessageNotification;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Chill is a software for social workers * Chill is a software for social workers.
* *
* For the full copyright and license information, please view * For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code. * the LICENSE file that was distributed with this source code.
@ -9,6 +9,13 @@
declare(strict_types=1); 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; namespace Chill\CalendarBundle\Service\ShortMessageNotification;
use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\Calendar;

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