From fb51e44e45a8bf5395e58b49419416dee3308665 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 11 Jan 2023 17:16:37 +0100 Subject: [PATCH 01/62] FEATURE [absence][in_progress] add absence property to user, create form, controller, template, migration and menu entry --- .../Controller/AbsenceController.php | 42 +++++++++++++++++++ src/Bundle/ChillMainBundle/Entity/User.php | 23 +++++++++- .../ChillMainBundle/Form/AbsenceType.php | 36 ++++++++++++++++ .../Resources/views/Menu/absence.html.twig | 10 +++++ .../Routing/MenuBuilder/UserMenuBuilder.php | 9 ++++ src/Bundle/ChillMainBundle/config/routes.yaml | 4 ++ .../migrations/Version20230111160610.php | 36 ++++++++++++++++ 7 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillMainBundle/Controller/AbsenceController.php create mode 100644 src/Bundle/ChillMainBundle/Form/AbsenceType.php create mode 100644 src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20230111160610.php diff --git a/src/Bundle/ChillMainBundle/Controller/AbsenceController.php b/src/Bundle/ChillMainBundle/Controller/AbsenceController.php new file mode 100644 index 000000000..97ca2979c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/AbsenceController.php @@ -0,0 +1,42 @@ +getUser(); + $form = $this->createForm(AbsenceType::class, $user); + $form->add('submit', SubmitType::class, ['label' => 'Create']); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($user); + $em->flush(); + + return $this->redirect($this->generateUrl('chill_main_homepage')); + } + + return $this->render('@ChillMain/Menu/absence.html.twig', [ + 'user' => $user, + 'form' => $form->createView(), + ]); + } +} diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index 80a0b5b2a..10620dc0f 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -11,14 +11,15 @@ declare(strict_types=1); namespace Chill\MainBundle\Entity; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use RuntimeException; use Symfony\Component\Security\Core\User\AdvancedUserInterface; use Symfony\Component\Serializer\Annotation as Serializer; -use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use function in_array; /** @@ -40,6 +41,11 @@ class User implements AdvancedUserInterface */ protected ?int $id = null; + /** + * @ORM\Column(type="datetime", nullable=true) + */ + private ?DateTimeImmutable $absenceStart = null; + /** * Array where SAML attributes's data are stored. * @@ -175,6 +181,11 @@ class User implements AdvancedUserInterface { } + public function getAbsenceStart(): ?DateTimeImmutable + { + return $this->absenceStart; + } + /** * Get attributes. * @@ -295,6 +306,11 @@ class User implements AdvancedUserInterface return $this->usernameCanonical; } + public function isAbsent(): bool + { + return null !== $this->getAbsenceStart() ? true : false; + } + /** * @return bool */ @@ -359,6 +375,11 @@ class User implements AdvancedUserInterface } } + public function setAbsenceStart(?DateTimeImmutable $absenceStart): void + { + $this->absenceStart = $absenceStart; + } + public function setAttributeByDomain(string $domain, string $key, $value): self { $this->attributes[$domain][$key] = $value; diff --git a/src/Bundle/ChillMainBundle/Form/AbsenceType.php b/src/Bundle/ChillMainBundle/Form/AbsenceType.php new file mode 100644 index 000000000..1f43093e5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/AbsenceType.php @@ -0,0 +1,36 @@ +add('absenceStart', ChillDateTimeType::class, [ + 'required' => true, + ]); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => User::class, + ]); + } +} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig new file mode 100644 index 000000000..37b4bdd2f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig @@ -0,0 +1,10 @@ +{% if user.absenceStart is not null %} + +

Votre absence est indiqué à partier de {{ user.absenceStart|format_datetime() }}

+ +{% endif %} + +{{ form_start(form) }} +{{ form_row(form.absenceStart) }} +{{ form_row(form.submit, { 'attr' : { 'class' : 'btn btn-chill-green' } } ) }} +{{ form_end(form) }} diff --git a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php index e0e8a782f..e91aa040b 100644 --- a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php +++ b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/UserMenuBuilder.php @@ -78,6 +78,15 @@ class UserMenuBuilder implements LocalMenuBuilderInterface $nbNotifications = $this->notificationByUserCounter->countUnreadByUser($user); + //TODO add an icon? How exactly? For example a clock icon... + $menu + ->addChild($this->translator->trans('absence.Set absence date'), [ + 'route' => 'chill_absence_user', + ]) + ->setExtras([ + 'order' => -8888888, + ]); + $menu ->addChild( $this->translator->trans('notification.My notifications with counter', ['nb' => $nbNotifications]), diff --git a/src/Bundle/ChillMainBundle/config/routes.yaml b/src/Bundle/ChillMainBundle/config/routes.yaml index d25f2aaff..d1fde6f5c 100644 --- a/src/Bundle/ChillMainBundle/config/routes.yaml +++ b/src/Bundle/ChillMainBundle/config/routes.yaml @@ -94,3 +94,7 @@ login_check: logout: path: /logout + +chill_absence_user: + path: /{_locale}/absence + controller: Chill\MainBundle\Controller\AbsenceController::setAbsence diff --git a/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php b/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php new file mode 100644 index 000000000..1c5f74897 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php @@ -0,0 +1,36 @@ +addSql('ALTER TABLE users DROP absenceStart'); + } + + public function getDescription(): string + { + return 'Add absence property to user'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE users ADD absenceStart TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + } +} From b2924ede704863778e7d79543999c666b7c6114c Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 12 Jan 2023 11:10:41 +0100 Subject: [PATCH 02/62] FEAUTURE [routing][absence] use routing annotation instead of config file --- .../ChillMainBundle/Controller/AbsenceController.php | 10 ++++++++-- src/Bundle/ChillMainBundle/config/routes.yaml | 4 ---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/AbsenceController.php b/src/Bundle/ChillMainBundle/Controller/AbsenceController.php index 97ca2979c..7241e15be 100644 --- a/src/Bundle/ChillMainBundle/Controller/AbsenceController.php +++ b/src/Bundle/ChillMainBundle/Controller/AbsenceController.php @@ -13,16 +13,22 @@ namespace Chill\MainBundle\Controller; use Chill\MainBundle\Form\AbsenceType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Annotation\Route; class AbsenceController extends AbstractController { + /** + * @Route( + * "/{_locale}/absence", + * name="chill_absence_user", + * methods={"GET", "POST"} + * ) + */ public function setAbsence(Request $request) { $user = $this->getUser(); $form = $this->createForm(AbsenceType::class, $user); - $form->add('submit', SubmitType::class, ['label' => 'Create']); $form->handleRequest($request); diff --git a/src/Bundle/ChillMainBundle/config/routes.yaml b/src/Bundle/ChillMainBundle/config/routes.yaml index d1fde6f5c..d25f2aaff 100644 --- a/src/Bundle/ChillMainBundle/config/routes.yaml +++ b/src/Bundle/ChillMainBundle/config/routes.yaml @@ -94,7 +94,3 @@ login_check: logout: path: /logout - -chill_absence_user: - path: /{_locale}/absence - controller: Chill\MainBundle\Controller\AbsenceController::setAbsence From b93b78615bd4866439a33a9bce7e79931eee9ee9 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 12 Jan 2023 11:11:43 +0100 Subject: [PATCH 03/62] FIX [migration][absence] fix the typing in db for absence datetime immuatable --- src/Bundle/ChillMainBundle/Entity/User.php | 2 +- src/Bundle/ChillMainBundle/Form/AbsenceType.php | 1 + src/Bundle/ChillMainBundle/migrations/Version20230111160610.php | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index 10620dc0f..c38dd97c6 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -42,7 +42,7 @@ class User implements AdvancedUserInterface protected ?int $id = null; /** - * @ORM\Column(type="datetime", nullable=true) + * @ORM\Column(type="datetime_immutable", nullable=true) */ private ?DateTimeImmutable $absenceStart = null; diff --git a/src/Bundle/ChillMainBundle/Form/AbsenceType.php b/src/Bundle/ChillMainBundle/Form/AbsenceType.php index 1f43093e5..8600fd019 100644 --- a/src/Bundle/ChillMainBundle/Form/AbsenceType.php +++ b/src/Bundle/ChillMainBundle/Form/AbsenceType.php @@ -24,6 +24,7 @@ class AbsenceType extends AbstractType $builder ->add('absenceStart', ChillDateTimeType::class, [ 'required' => true, + 'input' => 'datetime_immutable', ]); } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php b/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php index 1c5f74897..3bdffa6ea 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php @@ -22,6 +22,7 @@ final class Version20230111160610 extends AbstractMigration public function down(Schema $schema): void { $this->addSql('ALTER TABLE users DROP absenceStart'); + $this->addSql('COMMENT ON COLUMN users.absenceStart IS \'(DC2Type:datetime_immutable)\''); } public function getDescription(): string From 68998c9156ec6764681b56d8e679c12d539ca07f Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 12 Jan 2023 11:50:06 +0100 Subject: [PATCH 04/62] FEATURE [translations][absence] add translations --- src/Bundle/ChillMainBundle/Form/AbsenceType.php | 9 +++++++++ src/Bundle/ChillMainBundle/config/services/form.yaml | 4 ++++ src/Bundle/ChillMainBundle/translations/messages.fr.yml | 7 +++++++ 3 files changed, 20 insertions(+) diff --git a/src/Bundle/ChillMainBundle/Form/AbsenceType.php b/src/Bundle/ChillMainBundle/Form/AbsenceType.php index 8600fd019..e5e92398e 100644 --- a/src/Bundle/ChillMainBundle/Form/AbsenceType.php +++ b/src/Bundle/ChillMainBundle/Form/AbsenceType.php @@ -16,15 +16,24 @@ use Chill\MainBundle\Form\Type\ChillDateTimeType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\Translation\TranslatorInterface; class AbsenceType extends AbstractType { + private TranslatorInterface $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('absenceStart', ChillDateTimeType::class, [ 'required' => true, 'input' => 'datetime_immutable', + 'label' => $this->translator->trans('absence.Absence start'), ]); } diff --git a/src/Bundle/ChillMainBundle/config/services/form.yaml b/src/Bundle/ChillMainBundle/config/services/form.yaml index 58f01883f..c8a46132f 100644 --- a/src/Bundle/ChillMainBundle/config/services/form.yaml +++ b/src/Bundle/ChillMainBundle/config/services/form.yaml @@ -138,6 +138,10 @@ services: autowire: true autoconfigure: true + Chill\MainBundle\Form\AbsenceType: + autowire: true + autoconfigure: true + Chill\MainBundle\Form\DataTransformer\IdToLocationDataTransformer: ~ Chill\MainBundle\Form\DataTransformer\IdToUserDataTransformer: ~ Chill\MainBundle\Form\DataTransformer\IdToUsersDataTransformer: ~ diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index b19a7a52f..b80171c8c 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -573,3 +573,10 @@ saved_export: Export is deleted: L'export est supprimé Saved export is saved!: L'export est enregistré Created on %date%: Créé le %date% + + +absence: + My absence: Mon absence + Unset absence: Supprimer la date d'absence + Set absence date: Indiquer date d'absence + Absence start: Absence à partir de From 44ef21f940b03e43d1b3d030d474d97d25511546 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 12 Jan 2023 11:51:42 +0100 Subject: [PATCH 05/62] FEATURE [delete][absence] add functionality to unset absence --- .../Controller/AbsenceController.php | 25 +++++++++- .../Resources/views/Menu/absence.html.twig | 50 ++++++++++++++++--- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/AbsenceController.php b/src/Bundle/ChillMainBundle/Controller/AbsenceController.php index 7241e15be..9c3dc679d 100644 --- a/src/Bundle/ChillMainBundle/Controller/AbsenceController.php +++ b/src/Bundle/ChillMainBundle/Controller/AbsenceController.php @@ -37,7 +37,7 @@ class AbsenceController extends AbstractController $em->persist($user); $em->flush(); - return $this->redirect($this->generateUrl('chill_main_homepage')); + return $this->redirect($this->generateUrl('chill_absence_user')); } return $this->render('@ChillMain/Menu/absence.html.twig', [ @@ -45,4 +45,27 @@ class AbsenceController extends AbstractController 'form' => $form->createView(), ]); } + + /** + * @Route( + * "/{_locale}/absence/unset", + * name="chill_unset_absence_user", + * methods={"GET", "POST", "DELETE"} + * ) + */ + public function unsetAbsence(Request $request) + { + $user = $this->getUser(); + $form = $this->createForm(AbsenceType::class, $user); + + $user->setAbsenceStart(null); + $em = $this->getDoctrine()->getManager(); + $em->persist($user); + $em->flush(); + + return $this->render('@ChillMain/Menu/absence.html.twig', [ + 'user' => $user, + 'form' => $form->createView(), + ]); + } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig index 37b4bdd2f..8b27773e4 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig @@ -1,10 +1,46 @@ -{% if user.absenceStart is not null %} +{% extends '@ChillMain/Admin/layout.html.twig' %} -

Votre absence est indiqué à partier de {{ user.absenceStart|format_datetime() }}

+{% block title %} + {{ 'My absence'|trans }} +{% endblock title %} -{% endif %} +{% block content %} -{{ form_start(form) }} -{{ form_row(form.absenceStart) }} -{{ form_row(form.submit, { 'attr' : { 'class' : 'btn btn-chill-green' } } ) }} -{{ form_end(form) }} +
+

{{ 'absence.My absence'|trans }}

+ + {% if user.absenceStart is not null %} +
+

Votre absence est indiqué à partier de {{ user.absenceStart|format_datetime() }}.

+ +
+ {% else %} +
+

Aucune absence indiquer.

+
+ {% endif %} + +
+ {{ form_start(form) }} + {{ form_row(form.absenceStart) }} + +
    +
  • + +
  • +
+ + {{ form_end(form) }} +
+
+ + + +{% endblock %} From 2c5c815f68e7691b2903399768e4d4def74ee51b Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 12 Jan 2023 12:02:52 +0100 Subject: [PATCH 06/62] FEATURE [admin][absence] add possibility for admin to set absence of a user --- src/Bundle/ChillMainBundle/Form/UserType.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Form/UserType.php b/src/Bundle/ChillMainBundle/Form/UserType.php index b738cc8c7..532bc7b5b 100644 --- a/src/Bundle/ChillMainBundle/Form/UserType.php +++ b/src/Bundle/ChillMainBundle/Form/UserType.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\UserJob; +use Chill\MainBundle\Form\Type\ChillDateTimeType; use Chill\MainBundle\Form\Type\PickCivilityType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Doctrine\ORM\EntityRepository; @@ -31,6 +32,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Regex; +use Symfony\Contracts\Translation\TranslatorInterface; class UserType extends AbstractType { @@ -38,12 +40,16 @@ class UserType extends AbstractType private TranslatableStringHelper $translatableStringHelper; + private TranslatorInterface $translator; + public function __construct( TranslatableStringHelper $translatableStringHelper, - ParameterBagInterface $parameterBag + ParameterBagInterface $parameterBag, + TranslatorInterface $translator ) { $this->translatableStringHelper = $translatableStringHelper; $this->parameterBag = $parameterBag; + $this->translator = $translator; } public function buildForm(FormBuilderInterface $builder, array $options) @@ -110,6 +116,11 @@ class UserType extends AbstractType return $qb; }, + ]) + ->add('absenceStart', ChillDateTimeType::class, [ + 'required' => false, + 'input' => 'datetime_immutable', + 'label' => $this->translator->trans('absence.Absence start'), ]); // @phpstan-ignore-next-line From 5bbe5af12488944d587790273e6b8d09ecb5058a Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 12 Jan 2023 13:30:28 +0100 Subject: [PATCH 07/62] FEATURE [absence][render] add absence tag to renderbox and renderstring --- .../Resources/views/Entity/user.html.twig | 5 +++++ .../Templating/Entity/UserRender.php | 16 ++++++++++++++-- .../ChillMainBundle/translations/messages.fr.yml | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig index 77dc959a2..12a1a618c 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig @@ -6,4 +6,9 @@ {%- if opts['main_scope'] and user.mainScope is not null %} ({{ user.mainScope.name|localize_translatable_string }}) {%- endif -%} + {%- if opts['absence'] and user.absenceStart is not null %} + {%- if date(user.absenceStart) < date() %} + ({{ 'absence.Absent'|trans }}) + {%- endif %} + {%- endif -%} diff --git a/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php b/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php index 141de2649..2cef950d5 100644 --- a/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php +++ b/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php @@ -13,8 +13,10 @@ namespace Chill\MainBundle\Templating\Entity; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Templating\TranslatableStringHelper; -use Symfony\Component\Templating\EngineInterface; +use DateTimeImmutable; +use Symfony\Component\Templating\EngineInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use function array_merge; class UserRender implements ChillEntityRenderInterface @@ -22,16 +24,20 @@ class UserRender implements ChillEntityRenderInterface public const DEFAULT_OPTIONS = [ 'main_scope' => true, 'user_job' => true, + 'absence' => true, ]; private EngineInterface $engine; private TranslatableStringHelper $translatableStringHelper; - public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine) + private TranslatorInterface $translator; + + public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine, TranslatorInterface $translator) { $this->translatableStringHelper = $translatableStringHelper; $this->engine = $engine; + $this->translator = $translator; } public function renderBox($entity, array $options): string @@ -63,6 +69,12 @@ class UserRender implements ChillEntityRenderInterface ->localize($entity->getMainScope()->getName()) . ')'; } + $current_date = new DateTimeImmutable(); + + if (null !== $entity->getAbsenceStart() && $entity->getAbsenceStart() < $current_date && $opts['main_scope']) { + $str .= ' (' . $this->translator->trans('absence.Absent') . ')'; + } + return $str; } diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index b80171c8c..c7d376c48 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -580,3 +580,4 @@ absence: Unset absence: Supprimer la date d'absence Set absence date: Indiquer date d'absence Absence start: Absence à partir de + Absent: Absent From 6c1108b8aa020c7464a767ead127260788f22f4f Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 12 Jan 2023 13:50:29 +0100 Subject: [PATCH 08/62] FEATURE: [homepage][absence] display message to let user know they are still marked as absent --- .../Resources/views/Homepage/index.html.twig | 11 ++++++++--- .../ChillMainBundle/translations/messages.fr.yml | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig index aec38f3c3..5dc88faf3 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig @@ -1,15 +1,20 @@
- + {# vue component #} + {% if app.user.absenceStart is not null and date(app.user.absenceStart) < date() %} +

{{'absence.You are marked as being absent'|trans }}

+ {% endif %} +
- + {% include '@ChillMain/Homepage/fast_actions.html.twig' %}
+ {% block css %} {{ encore_entry_link_tags('page_homepage_widget') }} {% endblock %} {% block js %} {{ encore_entry_script_tags('page_homepage_widget') }} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index c7d376c48..cffb0d6f9 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -581,3 +581,4 @@ absence: Set absence date: Indiquer date d'absence Absence start: Absence à partir de Absent: Absent + You are marked as being absent: Vous êtes marquer comme absent. From de9d53936f805501c57ec8e6380f4aee946306a3 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 12 Jan 2023 19:51:43 +0100 Subject: [PATCH 09/62] FEATURE [styling][absence] give styling --- .../Resources/public/chill/scss/render_box.scss | 2 +- .../ChillMainBundle/Resources/views/Entity/user.html.twig | 6 +++++- .../Resources/views/Homepage/index.html.twig | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss index 4d0bfcf8e..92becc089 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss @@ -106,7 +106,7 @@ section.chill-entity { // used for comment-embeddable &.entity-comment-embeddable { width: 100%; - + /* already defined !! div.metadata { font-size: smaller; diff --git a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig index 12a1a618c..779be6740 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig @@ -8,7 +8,11 @@ {%- endif -%} {%- if opts['absence'] and user.absenceStart is not null %} {%- if date(user.absenceStart) < date() %} - ({{ 'absence.Absent'|trans }}) + + + A + + {%- endif %} {%- endif -%} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig index 5dc88faf3..60f6c88ff 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig @@ -2,7 +2,7 @@ {# vue component #} {% if app.user.absenceStart is not null and date(app.user.absenceStart) < date() %} -

{{'absence.You are marked as being absent'|trans }}

+ {% endif %}
From 050a4feab586c8b0624c017692d267202aeedfaf Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 25 Jan 2023 11:55:13 +0100 Subject: [PATCH 10/62] Fix: [export][aggregator] group by user job, not by job --- .../AccompanyingCourseAggregators/JobAggregator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php index 5019d3a8b..1278bc21c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php @@ -40,11 +40,11 @@ final class JobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpjob', $qb->getAllAliases(), true)) { - $qb->leftJoin('acp.job', 'acpjob'); + if (!in_array('acpuser', $qb->getAllAliases(), true)) { + $qb->leftJoin('acp.user', 'acpuser'); } - $qb->addSelect('IDENTITY(acp.job) AS job_aggregator'); + $qb->addSelect('IDENTITY(acpuser.userJob) AS job_aggregator'); $qb->addGroupBy('job_aggregator'); } From 8e3a83de850d6798925512b70c85f69385d741be Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 25 Jan 2023 12:03:39 +0100 Subject: [PATCH 11/62] DX: [export] Rename JobAggregator to more logical UserJobAggregator --- .../{JobAggregator.php => UserJobAggregator.php} | 2 +- .../{JobAggregatorTest.php => UserJobAggregatorTest.php} | 6 +++--- .../config/services/exports_accompanying_course.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/{JobAggregator.php => UserJobAggregator.php} (97%) rename src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/{JobAggregatorTest.php => UserJobAggregatorTest.php} (90%) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserJobAggregator.php similarity index 97% rename from src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php rename to src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserJobAggregator.php index 1278bc21c..a8acdcf12 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/UserJobAggregator.php @@ -19,7 +19,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; -final class JobAggregator implements AggregatorInterface +final class UserJobAggregator implements AggregatorInterface { private UserJobRepository $jobRepository; diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/UserJobAggregatorTest.php similarity index 90% rename from src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php rename to src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/UserJobAggregatorTest.php index 18ef84c15..1ed748e32 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/UserJobAggregatorTest.php @@ -13,16 +13,16 @@ namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregato use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; -use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobAggregator; +use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\UserJobAggregator; use Doctrine\ORM\EntityManagerInterface; /** * @internal * @coversNothing */ -final class JobAggregatorTest extends AbstractAggregatorTest +final class UserJobAggregatorTest extends AbstractAggregatorTest { - private JobAggregator $aggregator; + private UserJobAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index 6d0e160fd..bb2cfa556 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -136,7 +136,7 @@ services: - { name: chill.export_aggregator, alias: accompanyingcourse_scope_aggregator } chill.person.export.aggregator_referrer_job: - class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobAggregator + class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\UserJobAggregator tags: - { name: chill.export_aggregator, alias: accompanyingcourse_referrer_job_aggregator } From 2f091a639b57121d1c3bef4607f32a953d8a285b Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 26 Jan 2023 11:52:45 +0100 Subject: [PATCH 12/62] remove unused private function --- .../Filter/AccompanyingCourseFilters/UserJobFilter.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php index b97f97dda..2b69c528b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/UserJobFilter.php @@ -126,12 +126,4 @@ class UserJobFilter implements FilterInterface { return 'Filter by user job'; } - - private function getUserJob(): UserJob - { - /** @var User $user */ - $user = $this->security->getUser(); - - return $user->getUserJob(); - } } From ded71c5997eb6bf8a82c78e486a1621aef4f8a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 26 Jan 2023 17:11:50 +0100 Subject: [PATCH 13/62] Fixed: [migration] fix the required comment for doctrine on new column --- src/Bundle/ChillMainBundle/migrations/Version20230111160610.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php b/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php index 3bdffa6ea..c00a3f521 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20230111160610.php @@ -22,7 +22,6 @@ final class Version20230111160610 extends AbstractMigration public function down(Schema $schema): void { $this->addSql('ALTER TABLE users DROP absenceStart'); - $this->addSql('COMMENT ON COLUMN users.absenceStart IS \'(DC2Type:datetime_immutable)\''); } public function getDescription(): string @@ -33,5 +32,6 @@ final class Version20230111160610 extends AbstractMigration public function up(Schema $schema): void { $this->addSql('ALTER TABLE users ADD absenceStart TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('COMMENT ON COLUMN users.absenceStart IS \'(DC2Type:datetime_immutable)\''); } } From 86b5f4dfac0a5bc6ac530dac68e00db2c0d58c7f Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 27 Jan 2023 10:44:25 +0100 Subject: [PATCH 14/62] FIX [template][translations] fixes to template and translations --- .../Resources/views/Menu/absence.html.twig | 8 ++++---- src/Bundle/ChillMainBundle/translations/messages.fr.yml | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig index 8b27773e4..d8508d8fd 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Menu/absence.html.twig @@ -1,7 +1,7 @@ {% extends '@ChillMain/Admin/layout.html.twig' %} {% block title %} - {{ 'My absence'|trans }} + {{ 'absence.My absence'|trans }} {% endblock title %} {% block content %} @@ -11,7 +11,7 @@ {% if user.absenceStart is not null %}
-

Votre absence est indiqué à partier de {{ user.absenceStart|format_datetime() }}.

+

{{ 'absence.You are listed as absent, as of'|trans }} {{ user.absenceStart|format_date('long') }}

  • {% else %}
    -

    Aucune absence indiquer.

    +

    {{ 'absence.No absence listed'|trans }}

    {% endif %} @@ -32,7 +32,7 @@
    diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 8218bf827..94041b78d 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -589,6 +589,8 @@ absence: My absence: Mon absence Unset absence: Supprimer la date d'absence Set absence date: Indiquer date d'absence - Absence start: Absence à partir de + Absence start: Absence à partir du Absent: Absent - You are marked as being absent: Vous êtes marquer comme absent. + You are marked as being absent: Vous êtes indiqué absent. + You are listed as absent, as of: Votre absence est indiqué à partir du + No absence listed: Aucune absence indiquée. From f76c031ff311eea32ec79942640cb3ec35333b37 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 27 Jan 2023 10:51:59 +0100 Subject: [PATCH 15/62] FIX [translations][types] remove redundant translator and change datetime field to date field --- src/Bundle/ChillMainBundle/Form/AbsenceType.php | 12 +++--------- src/Bundle/ChillMainBundle/Form/UserType.php | 11 ++++------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Form/AbsenceType.php b/src/Bundle/ChillMainBundle/Form/AbsenceType.php index e5e92398e..e23e350c9 100644 --- a/src/Bundle/ChillMainBundle/Form/AbsenceType.php +++ b/src/Bundle/ChillMainBundle/Form/AbsenceType.php @@ -13,6 +13,7 @@ namespace Chill\MainBundle\Form; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Form\Type\ChillDateTimeType; +use Chill\MainBundle\Form\Type\ChillDateType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -20,20 +21,13 @@ use Symfony\Contracts\Translation\TranslatorInterface; class AbsenceType extends AbstractType { - private TranslatorInterface $translator; - - public function __construct(TranslatorInterface $translator) - { - $this->translator = $translator; - } - public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('absenceStart', ChillDateTimeType::class, [ + ->add('absenceStart', ChillDateType::class, [ 'required' => true, 'input' => 'datetime_immutable', - 'label' => $this->translator->trans('absence.Absence start'), + 'label' => 'absence.Absence start', ]); } diff --git a/src/Bundle/ChillMainBundle/Form/UserType.php b/src/Bundle/ChillMainBundle/Form/UserType.php index 532bc7b5b..f37c48a92 100644 --- a/src/Bundle/ChillMainBundle/Form/UserType.php +++ b/src/Bundle/ChillMainBundle/Form/UserType.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Form\Type\ChillDateTimeType; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\PickCivilityType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Doctrine\ORM\EntityRepository; @@ -40,16 +41,12 @@ class UserType extends AbstractType private TranslatableStringHelper $translatableStringHelper; - private TranslatorInterface $translator; - public function __construct( TranslatableStringHelper $translatableStringHelper, - ParameterBagInterface $parameterBag, - TranslatorInterface $translator + ParameterBagInterface $parameterBag ) { $this->translatableStringHelper = $translatableStringHelper; $this->parameterBag = $parameterBag; - $this->translator = $translator; } public function buildForm(FormBuilderInterface $builder, array $options) @@ -117,10 +114,10 @@ class UserType extends AbstractType return $qb; }, ]) - ->add('absenceStart', ChillDateTimeType::class, [ + ->add('absenceStart', ChillDateType::class, [ 'required' => false, 'input' => 'datetime_immutable', - 'label' => $this->translator->trans('absence.Absence start'), + 'label' => 'absence.Absence start', ]); // @phpstan-ignore-next-line From bb7d072cc8af6b83574c10de87f6dfe3a7b7e80f Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 27 Jan 2023 11:10:17 +0100 Subject: [PATCH 16/62] FIX [render] use isAbsent method to render user as absent or not + place homepage msg above searchbar --- src/Bundle/ChillMainBundle/Entity/User.php | 2 +- .../ChillMainBundle/Resources/views/Entity/user.html.twig | 2 +- .../ChillMainBundle/Resources/views/Homepage/index.html.twig | 5 ----- src/Bundle/ChillMainBundle/Resources/views/layout.html.twig | 5 +++++ src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index 43a63c52d..24e88e7ba 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -304,7 +304,7 @@ class User implements UserInterface public function isAbsent(): bool { - return null !== $this->getAbsenceStart() ? true : false; + return (null !== $this->getAbsenceStart() && $this->getAbsenceStart() <= new DateTimeImmutable('now')) ? true : false; } /** diff --git a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig index 779be6740..6d6e095d5 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig @@ -6,7 +6,7 @@ {%- if opts['main_scope'] and user.mainScope is not null %} ({{ user.mainScope.name|localize_translatable_string }}) {%- endif -%} - {%- if opts['absence'] and user.absenceStart is not null %} + {%- if opts['absence'] and user.isAbsent %} {%- if date(user.absenceStart) < date() %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig index 60f6c88ff..98af21171 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig @@ -1,10 +1,5 @@
    - {# vue component #} - {% if app.user.absenceStart is not null and date(app.user.absenceStart) < date() %} - - {% endif %} -
    {% include '@ChillMain/Homepage/fast_actions.html.twig' %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig b/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig index 319adad81..9b4007266 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig @@ -69,6 +69,11 @@ {% block content %}
  • +
  • @@ -23,7 +23,7 @@ import ConvertButton from "./StoredObjectButton/ConvertButton.vue"; import DownloadButton from "./StoredObjectButton/DownloadButton.vue"; import WopiEditButton from "./StoredObjectButton/WopiEditButton.vue"; -import {is_extension_editable} from "./StoredObjectButton/helpers"; +import {is_extension_editable, is_extension_viewable} from "./StoredObjectButton/helpers"; import {StoredObject, WopiEditButtonExecutableBeforeLeaveFunction} from "../types"; interface DocumentActionButtonsGroupConfig { diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts index 58d2ebc71..d82efb111 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts @@ -1,97 +1,107 @@ -const SUPPORTED_MIMES = new Set([ - 'image/svg+xml', +const MIMES_EDIT = new Set([ 'application/vnd.ms-powerpoint', 'application/vnd.ms-excel', - 'application/vnd.sun.xml.writer', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.text-flat-xml', - 'application/vnd.sun.xml.calc', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.spreadsheet-flat-xml', - 'application/vnd.sun.xml.impress', 'application/vnd.oasis.opendocument.presentation', 'application/vnd.oasis.opendocument.presentation-flat-xml', - 'application/vnd.sun.xml.draw', 'application/vnd.oasis.opendocument.graphics', 'application/vnd.oasis.opendocument.graphics-flat-xml', 'application/vnd.oasis.opendocument.chart', - 'application/vnd.sun.xml.writer.global', - 'application/vnd.oasis.opendocument.text-master', - 'application/vnd.sun.xml.writer.template', - 'application/vnd.oasis.opendocument.text-template', - 'application/vnd.oasis.opendocument.text-master-template', - 'application/vnd.sun.xml.calc.template', - 'application/vnd.oasis.opendocument.spreadsheet-template', - 'application/vnd.sun.xml.impress.template', - 'application/vnd.oasis.opendocument.presentation-template', - 'application/vnd.sun.xml.draw.template', - 'application/vnd.oasis.opendocument.graphics-template', - 'application/msword', 'application/msword', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-word.document.macroEnabled.12', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'application/vnd.ms-word.template.macroEnabled.12', - 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - 'application/vnd.ms-excel.template.macroEnabled.12', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'application/vnd.ms-excel.sheet.macroEnabled.12', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', - 'application/vnd.openxmlformats-officedocument.presentationml.template', - 'application/vnd.ms-powerpoint.template.macroEnabled.12', - 'application/vnd.wordperfect', - 'application/x-aportisdoc', - 'application/x-hwp', - 'application/vnd.ms-works', - 'application/x-mswrite', 'application/x-dif-document', 'text/spreadsheet', 'text/csv', 'application/x-dbase', - 'application/vnd.lotus-1-2-3', - 'image/cgm', - 'image/vnd.dxf', - 'image/x-emf', - 'image/x-wmf', - 'application/coreldraw', - 'application/vnd.visio2013', - 'application/vnd.visio', - 'application/vnd.ms-visio.drawing', - 'application/x-mspublisher', - 'application/x-sony-bbeb', - 'application/x-gnumeric', - 'application/macwriteii', - 'application/x-iwork-numbers-sffnumbers', - 'application/vnd.oasis.opendocument.text-web', - 'application/x-pagemaker', 'text/rtf', 'text/plain', - 'application/x-fictionbook+xml', - 'application/clarisworks', - 'image/x-wpg', - 'application/x-iwork-pages-sffpages', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - 'application/x-iwork-keynote-sffkey', - 'application/x-abiword', - 'image/x-freehand', - 'application/vnd.sun.xml.chart', - 'application/x-t602', - 'image/bmp', - 'image/png', - 'image/gif', - 'image/tiff', - 'image/jpg', - 'image/jpeg', - 'application/pdf', ]); + + +const MIMES_VIEW = new Set([ + ...MIMES_EDIT, + [ + 'image/svg+xml', + 'application/vnd.sun.xml.writer', + 'application/vnd.sun.xml.calc', + 'application/vnd.sun.xml.impress', + 'application/vnd.sun.xml.draw', + 'application/vnd.sun.xml.writer.global', + 'application/vnd.sun.xml.writer.template', + 'application/vnd.sun.xml.calc.template', + 'application/vnd.sun.xml.impress.template', + 'application/vnd.sun.xml.draw.template', + 'application/vnd.oasis.opendocument.text-master', + 'application/vnd.oasis.opendocument.text-template', + 'application/vnd.oasis.opendocument.text-master-template', + 'application/vnd.oasis.opendocument.spreadsheet-template', + 'application/vnd.oasis.opendocument.presentation-template', + 'application/vnd.oasis.opendocument.graphics-template', + 'application/vnd.ms-word.template.macroEnabled.12', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'application/vnd.ms-excel.template.macroEnabled.12', + 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'application/vnd.ms-powerpoint.template.macroEnabled.12', + 'application/vnd.wordperfect', + 'application/x-aportisdoc', + 'application/x-hwp', + 'application/vnd.ms-works', + 'application/x-mswrite', + 'application/vnd.lotus-1-2-3', + 'image/cgm', + 'image/vnd.dxf', + 'image/x-emf', + 'image/x-wmf', + 'application/coreldraw', + 'application/vnd.visio2013', + 'application/vnd.visio', + 'application/vnd.ms-visio.drawing', + 'application/x-mspublisher', + 'application/x-sony-bbeb', + 'application/x-gnumeric', + 'application/macwriteii', + 'application/x-iwork-numbers-sffnumbers', + 'application/vnd.oasis.opendocument.text-web', + 'application/x-pagemaker', + 'application/x-fictionbook+xml', + 'application/clarisworks', + 'image/x-wpg', + 'application/x-iwork-pages-sffpages', + 'application/x-iwork-keynote-sffkey', + 'application/x-abiword', + 'image/x-freehand', + 'application/vnd.sun.xml.chart', + 'application/x-t602', + 'image/bmp', + 'image/png', + 'image/gif', + 'image/tiff', + 'image/jpg', + 'image/jpeg', + 'application/pdf', + ] +]) + function is_extension_editable(mimeType: string): boolean { - return SUPPORTED_MIMES.has(mimeType); + return MIMES_EDIT.has(mimeType); +} + +function is_extension_viewable(mimeType: string): boolean { + return MIMES_VIEW.has(mimeType); } function build_convert_link(uuid: string) { @@ -165,4 +175,5 @@ export { download_and_decrypt_doc, download_doc, is_extension_editable, + is_extension_viewable, }; From 5e58d36e798d9fd99e1086e0d6982d69758a7f87 Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Wed, 8 Feb 2023 14:28:21 +0000 Subject: [PATCH 30/62] Feature: exports for aside activities --- .../Aggregator/ByActivityTypeAggregator.php | 6 +- .../Export/Aggregator/ByUserJobAggregator.php | 89 +++++++ .../Aggregator/ByUserScopeAggregator.php | 89 +++++++ .../Export/AvgAsideActivityDuration.php | 102 ++++++++ .../src/Export/Export/CountAsideActivity.php | 6 +- .../src/Export/Export/ListAsideActivity.php | 236 ++++++++++++++++++ .../Export/SumAsideActivityDuration.php | 102 ++++++++ .../Export/Filter/ByActivityTypeFilter.php | 4 +- .../src/Export/Filter/ByDateFilter.php | 11 +- .../src/Export/Filter/ByUserFilter.php | 75 ++++++ .../src/Export/Filter/ByUserJobFilter.php | 81 ++++++ .../src/Export/Filter/ByUserScopeFilter.php | 88 +++++++ .../src/config/services.yaml | 30 --- .../src/config/services/export.yaml | 39 ++- .../src/translations/messages.fr.yml | 70 ++++-- .../Export/ExportInterface.php | 4 +- .../Export/Helper/DateTimeHelper.php | 4 + 17 files changed, 962 insertions(+), 74 deletions(-) create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByUserJobAggregator.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByUserScopeAggregator.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Export/AvgAsideActivityDuration.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Export/SumAsideActivityDuration.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserFilter.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserJobFilter.php create mode 100644 src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserScopeFilter.php diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php index 6c91c9336..32418e3c3 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -53,19 +53,15 @@ class ByActivityTypeAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - $this->asideActivityCategoryRepository->findBy(['id' => $values]); - return function ($value): string { if ('_header' === $value) { return 'export.aggregator.Aside activity type'; } - if (null === $value) { + if (null === $value || null === $t = $this->asideActivityCategoryRepository->find($value)) { return ''; } - $t = $this->asideActivityCategoryRepository->find($value); - return $this->translatableStringHelper->localize($t->getTitle()); }; } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByUserJobAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByUserJobAggregator.php new file mode 100644 index 000000000..d2fe7c2f2 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByUserJobAggregator.php @@ -0,0 +1,89 @@ +userJobRepository = $userJobRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('aside_user', $qb->getAllAliases(), true)) { + $qb->leftJoin('aside.agent', 'aside_user'); + } + + $qb + ->addSelect('IDENTITY(aside_user.userJob) AS aside_activity_user_job_aggregator') + ->addGroupBy('aside_activity_user_job_aggregator'); + } + + public function applyOn() + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // nothing to add in the form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Users \'s job'; + } + + if (null === $value || '' === $value) { + return ''; + } + + $j = $this->userJobRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['aside_activity_user_job_aggregator']; + } + + public function getTitle() + { + return 'export.aggregator.Aggregate by user job'; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByUserScopeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByUserScopeAggregator.php new file mode 100644 index 000000000..6c06ee756 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByUserScopeAggregator.php @@ -0,0 +1,89 @@ +scopeRepository = $scopeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + if (!in_array('aside_user', $qb->getAllAliases(), true)) { + $qb->leftJoin('aside.agent', 'aside_user'); + } + + $qb + ->addSelect('IDENTITY(aside_user.mainScope) AS aside_activity_user_scope_aggregator') + ->addGroupBy('aside_activity_user_scope_aggregator'); + } + + public function applyOn() + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + // nothing to add in the form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Users \'s scope'; + } + + if (null === $value || '' === $value) { + return ''; + } + + $s = $this->scopeRepository->find($value); + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['aside_activity_user_scope_aggregator']; + } + + public function getTitle() + { + return 'export.aggregator.Aggregate by user scope'; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/AvgAsideActivityDuration.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/AvgAsideActivityDuration.php new file mode 100644 index 000000000..f3db629cb --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/AvgAsideActivityDuration.php @@ -0,0 +1,102 @@ +repository = $repository; + } + + public function buildForm(FormBuilderInterface $builder) + { + } + + public function getAllowedFormattersTypes(): array + { + return [FormatterInterface::TYPE_TABULAR]; + } + + public function getDescription(): string + { + return 'export.Average aside activities duration'; + } + + public function getGroup(): string + { + return 'export.Exports of aside activities'; + } + + public function getLabels($key, array $values, $data) + { + if ('export_avg_aside_activity_duration' !== $key) { + throw new LogicException("the key {$key} is not used by this export"); + } + + return static fn ($value) => '_header' === $value ? 'Average duration aside activities' : $value; + } + + public function getQueryKeys($data): array + { + return ['export_avg_aside_activity_duration']; + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); + } + + public function getTitle(): string + { + return 'export.Average aside activities duration'; + } + + public function getType(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $qb = $this->repository->createQueryBuilder('aside'); + + $qb + ->select('AVG(aside.duration) as export_avg_aside_activity_duration') + ->andWhere($qb->expr()->isNotNull('aside.duration')); + + return $qb; + } + + public function requiredRole(): string + { + return AsideActivityVoter::STATS; + } + + public function supportsModifiers(): array + { + return [Declarations::ASIDE_ACTIVITY_TYPE]; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php index c3e99f129..87aad1659 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php @@ -11,12 +11,12 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Export; +use Chill\AsideActivityBundle\Export\Declarations; use Chill\AsideActivityBundle\Repository\AsideActivityRepository; use Chill\AsideActivityBundle\Security\AsideActivityVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; -use ChillAsideActivityBundle\Export\Declarations; use Doctrine\ORM\Query; use LogicException; use Symfony\Component\Form\FormBuilderInterface; @@ -100,6 +100,8 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface public function supportsModifiers(): array { - return []; + return [ + Declarations::ASIDE_ACTIVITY_TYPE, + ]; } } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php new file mode 100644 index 000000000..bf370f71b --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php @@ -0,0 +1,236 @@ +em = $em; + $this->dateTimeHelper = $dateTimeHelper; + $this->userHelper = $userHelper; + $this->scopeRepository = $scopeRepository; + $this->centerRepository = $centerRepository; + $this->asideActivityCategoryRepository = $asideActivityCategoryRepository; + $this->categoryRender = $categoryRender; + $this->translatableStringHelper = $translatableStringHelper; + } + + public function buildForm(FormBuilderInterface $builder) + { + } + + public function getAllowedFormattersTypes() + { + return [FormatterInterface::TYPE_LIST]; + } + + public function getDescription() + { + return 'export.aside_activity.List of aside activities'; + } + + public function getTitle() + { + return 'export.aside_activity.List of aside activities'; + } + + public function getGroup(): string + { + return 'export.Exports of aside activities'; + } + + public function getLabels($key, array $values, $data) + { + switch ($key) { + case 'id': + case 'note': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.aside_activity.' . $key; + } + + return $value ?? ''; + }; + case 'duration': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.aside_activity.' . $key; + } + + if (null === $value) { + return ''; + } + + if ($value instanceof \DateTimeInterface) { + return $value->format('H:i:s'); + } + + return $value; + }; + + case 'createdAt': + case 'updatedAt': + case 'date': + return $this->dateTimeHelper->getLabel('export.aside_activity.'.$key); + + case 'agent_id': + case 'creator_id': + return $this->userHelper->getLabel($key, $values, 'export.aside_activity.' . $key); + + case 'aside_activity_type': + return function ($value) { + if ('_header' === $value) { + return 'export.aside_activity.aside_activity_type'; + } + + if (null === $value || '' === $value || null === $c = $this->asideActivityCategoryRepository->find($value)) { + return ''; + } + + return $this->categoryRender->renderString($c, []); + }; + + case 'main_scope': + return function ($value) { + if ('_header' === $value) { + return 'export.aside_activity.main_scope'; + } + + if (null === $value || '' === $value || null === $c = $this->scopeRepository->find($value)) { + return ''; + } + + return $this->translatableStringHelper->localize($c->getName()); + }; + + case 'main_center': + return function ($value) { + if ('_header' === $value) { + return 'export.aside_activity.main_center'; + } + + /** @var Center $c */ + if (null === $value || '' === $value || null === $c = $this->centerRepository->find($value)) { + return ''; + } + + return $c->getName(); + }; + + default: + throw new \LogicException('this key is not supported : ' . $key); + } + } + + public function getQueryKeys($data) + { + return [ + 'id', + 'createdAt', + 'updatedAt', + 'agent_id', + 'creator_id', + 'main_scope', + 'main_center', + 'aside_activity_type', + 'date', + 'duration', + 'note' + ]; + } + + /** + * @param QueryBuilder $query + * @param array $data + */ + public function getResult($query, $data): array + { + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY); + } + + public function getType(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $qb = $this->em->createQueryBuilder() + ->from(AsideActivity::class, 'aside') + ->leftJoin('aside.agent', 'agent') + ; + + $qb + ->addSelect('aside.id AS id') + ->addSelect('aside.createdAt AS createdAt') + ->addSelect('aside.updatedAt AS updatedAt') + ->addSelect('IDENTITY(aside.agent) AS agent_id') + ->addSelect('IDENTITY(aside.createdBy) AS creator_id') + ->addSelect('IDENTITY(agent.mainScope) AS main_scope') + ->addSelect('IDENTITY(agent.mainCenter) AS main_center') + ->addSelect('IDENTITY(aside.type) AS aside_activity_type') + ->addSelect('aside.date') + ->addSelect('aside.duration') + ->addSelect('aside.note') + ; + + return $qb; + } + + public function requiredRole(): string + { + return AsideActivityVoter::STATS; + } + + public function supportsModifiers() + { + return [Declarations::ASIDE_ACTIVITY_TYPE]; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/SumAsideActivityDuration.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/SumAsideActivityDuration.php new file mode 100644 index 000000000..af17a2591 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/SumAsideActivityDuration.php @@ -0,0 +1,102 @@ +repository = $repository; + } + + public function buildForm(FormBuilderInterface $builder) + { + } + + public function getAllowedFormattersTypes(): array + { + return [FormatterInterface::TYPE_TABULAR]; + } + + public function getDescription(): string + { + return 'export.Sum aside activities duration'; + } + + public function getGroup(): string + { + return 'export.Exports of aside activities'; + } + + public function getLabels($key, array $values, $data) + { + if ('export_sum_aside_activity_duration' !== $key) { + throw new LogicException("the key {$key} is not used by this export"); + } + + return static fn ($value) => '_header' === $value ? 'Sum duration aside activities' : $value; + } + + public function getQueryKeys($data): array + { + return ['export_sum_aside_activity_duration']; + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); + } + + public function getTitle(): string + { + return 'export.Sum aside activities duration'; + } + + public function getType(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $qb = $this->repository + ->createQueryBuilder('aside'); + + $qb->select('SUM(aside.duration) as export_sum_aside_activity_duration') + ->andWhere($qb->expr()->isNotNull('aside.duration')); + + return $qb; + } + + public function requiredRole(): string + { + return AsideActivityVoter::STATS; + } + + public function supportsModifiers(): array + { + return [Declarations::ASIDE_ACTIVITY_TYPE]; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php index 85b327795..3ad8e0e93 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -80,8 +80,8 @@ class ByActivityTypeFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { $types = array_map( - fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getName()), - $this->asideActivityTypeRepository->findBy(['id' => $data['types']->toArray()]) + fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getTitle()), + $data['types']->toArray() ); return ['export.filter.Filtered by aside activity type: only %type%', [ diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php index 099c87fc0..2d49b3d57 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -46,25 +46,18 @@ class ByDateFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->between( 'aside.date', ':date_from', ':date_to' ); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } + $qb->andWhere($clause); - $qb->add('where', $where); $qb->setParameter( 'date_from', $this->rollingDateConverter->convert($data['date_from']) - ); - $qb->setParameter( + )->setParameter( 'date_to', $this->rollingDateConverter->convert($data['date_to']) ); diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserFilter.php new file mode 100644 index 000000000..c2a3b4c54 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserFilter.php @@ -0,0 +1,75 @@ +userRender = $userRender; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $clause = $qb->expr()->in('aside.agent', ':users'); + + $qb + ->andWhere($clause) + ->setParameter('users', $data['accepted_users']); + } + + public function applyOn(): string + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_users', PickUserDynamicType::class, [ + 'multiple' => true, + 'label' => 'Creators', + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $users = []; + + foreach ($data['accepted_users'] as $u) { + $users[] = $this->userRender->renderString($u, []); + } + + return ['export.filter.Filtered aside activity by user: only %users%', [ + '%users%' => implode(', ', $users), + ]]; + } + + public function getTitle(): string + { + return 'export.filter.Filter aside activity by user'; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserJobFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserJobFilter.php new file mode 100644 index 000000000..86194b123 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserJobFilter.php @@ -0,0 +1,81 @@ +translatableStringHelper = $translatableStringHelper; + } + + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AsideActivity::class . ' aside_activity_user_job_filter_act + JOIN aside_activity_user_job_filter_act.agent aside_activity_user_job_filter_user WHERE aside_activity_user_job_filter_user.userJob IN (:aside_activity_user_job_filter_jobs) AND aside_activity_user_job_filter_act = aside' + ) + ) + ->setParameter('aside_activity_user_job_filter_jobs', $data['jobs']); + } + + public function applyOn() + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + 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.Filtered aside activities by user jobs: only %jobs%', [ + '%jobs%' => implode( + ', ', + array_map( + fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), + $data['jobs']->toArray() + ) + ), + ]]; + } + + public function getTitle() + { + return 'export.filter.Filter by user jobs'; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserScopeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserScopeFilter.php new file mode 100644 index 000000000..4342e11eb --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByUserScopeFilter.php @@ -0,0 +1,88 @@ +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 ' . AsideActivity::class . ' aside_activity_user_scope_filter_act + JOIN aside_activity_user_scope_filter_act.agent aside_activity_user_scope_filter_user WHERE aside_activity_user_scope_filter_user.mainScope IN (:aside_activity_user_scope_filter_scopes) AND aside_activity_user_scope_filter_act = aside ' + ) + ) + ->setParameter('aside_activity_user_scope_filter_scopes', $data['scopes']); + } + + public function applyOn() + { + return Declarations::ASIDE_ACTIVITY_TYPE; + } + + 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.Filtered aside activities by user scope: only %scopes%', [ + '%scopes%' => implode( + ', ', + array_map( + fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()), + $data['scopes']->toArray() + ) + ), + ]]; + } + + public function getTitle() + { + return 'export.filter.Filter by user scope'; + } +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml index 2a7c30d7c..34bb6da33 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml @@ -20,33 +20,3 @@ services: resource: "../Controller" autowire: true autoconfigure: true - - - ## Exports - - # indicators - Chill\AsideActivityBundle\Export\Export\CountAsideActivity: - autowire: true - autoconfigure: true - tags: - - { name: chill.export, alias: count_asideactivity } - - # filters - Chill\AsideActivityBundle\Export\Filter\ByDateFilter: - autowire: true - autoconfigure: true - tags: - - { name: chill.export_filter, alias: asideactivity_bydate_filter } - - Chill\AsideActivityBundle\Export\Filter\ByActivityTypeFilter: - autowire: true - autoconfigure: true - tags: - - { name: chill.export_filter, alias: asideactivity_activitytype_filter } - - # aggregators - Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator: - autowire: true - autoconfigure: true - tags: - - { name: chill.export_aggregator, alias: asideactivity_activitytype_aggregator } diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml index 1b6b05e1c..a29413e15 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml @@ -3,11 +3,23 @@ services: autowire: true autoconfigure: true + Chill\AsideActivityBundle\Export\Export\ListAsideActivity: + tags: + - { name: chill.export, alias: 'list_aside_activity' } + ## Indicators Chill\AsideActivityBundle\Export\Export\CountAsideActivity: tags: - { name: chill.export, alias: 'count_aside_activity' } + Chill\AsideActivityBundle\Export\Export\SumAsideActivityDuration: + tags: + - { name: chill.export, alias: 'sum_aside_activity_duration' } + + Chill\AsideActivityBundle\Export\Export\AvgAsideActivityDuration: + tags: + - { name: chill.export, alias: 'avg_aside_activity_duration' } + ## Filters chill.aside_activity.export.date_filter: class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter @@ -19,9 +31,34 @@ services: tags: - { name: chill.export_filter, alias: 'aside_activity_type_filter' } + chill.aside_activity.export.user_job_filter: + class: Chill\AsideActivityBundle\Export\Filter\ByUserJobFilter + tags: + - { name: chill.export_filter, alias: 'aside_activity_user_job_filter' } + + chill.aside_activity.export.user_scope_filter: + class: Chill\AsideActivityBundle\Export\Filter\ByUserScopeFilter + tags: + - { name: chill.export_filter, alias: 'aside_activity_user_scope_filter' } + + chill.aside_activity.export.user_filter: + class: Chill\AsideActivityBundle\Export\Filter\ByUserFilter + tags: + - { name: chill.export_filter, alias: 'aside_activity_user_filter' } + ## Aggregators chill.aside_activity.export.type_aggregator: class: Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator tags: - - { name: chill.export_aggregator, alias: activity_type_aggregator } \ No newline at end of file + - { name: chill.export_aggregator, alias: activity_type_aggregator } + + chill.aside_activity.export.user_job_aggregator: + class: Chill\AsideActivityBundle\Export\Aggregator\ByUserJobAggregator + tags: + - { name: chill.export_aggregator, alias: aside_activity_user_job_aggregator } + + chill.aside_activity.export.user_scope_aggregator: + class: Chill\AsideActivityBundle\Export\Aggregator\ByUserScopeAggregator + tags: + - { name: chill.export_aggregator, alias: aside_activity_user_scope_aggregator } diff --git a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml index 2b4b6788a..0c807479f 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml +++ b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml @@ -29,16 +29,16 @@ location: Lieu # Crud crud: - aside_activity: - title_view: Détail de l'activité annexe - title_new: Nouvelle activité annexe - title_edit: Édition d'une activité annexe - title_delete: Supprimer une activité annexe - button_delete: Supprimer - confirm_message_delete: Êtes-vous sûr de vouloir supprimer cette activité annexe? - aside_activity_category: - title_new: Nouvelle catégorie d'activité annexe - title_edit: Édition d'une catégorie de type d'activité + aside_activity: + title_view: Détail de l'activité annexe + title_new: Nouvelle activité annexe + title_edit: Édition d'une activité annexe + title_delete: Supprimer une activité annexe + button_delete: Supprimer + confirm_message_delete: Êtes-vous sûr de vouloir supprimer cette activité annexe? + aside_activity_category: + title_new: Nouvelle catégorie d'activité annexe + title_edit: Édition d'une catégorie de type d'activité #forms Create a new aside activity type: Nouvelle categorie d'activité annexe @@ -169,19 +169,43 @@ Aside activity configuration: Configuration des activités annexes # exports export: - Exports of aside activities: Exports des activités annexes - Count aside activities: Nombre d'activités annexes - Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères - filter: - Filter by aside activity date: Filtrer les activités annexes par date - Filter by aside activity type: Filtrer les activités annexes par type d'activité - 'Filtered by aside activity type: only %type%': "Filtré par type d'activité annexe: uniquement %type%" - This date should be after the date given in "Implied in an aside activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités annexes après cette date" - Aside activities after this date: Actvitités annexes après cette date - Aside activities before this date: Actvitités annexes avant cette date - aggregator: - Group by aside activity type: Grouper les activités annexes par type d'activité - Aside activity type: Type d'activité annexe + aside_activity: + List of aside activities: Liste des activités annexes + createdAt: Création + updatedAt: Dernière mise à jour + agent_id: Utilisateur + creator_id: Créateur + main_scope: Service principal de l'utilisateur + main_center: Centre principal de l'utilisteur + aside_activity_type: Catégorie d'activité annexe + date: Date + duration: Durée + note: Note + + Exports of aside activities: Exports des activités annexes + Count aside activities: Nombre d'activités annexes + Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères + Average aside activities duration: Durée moyenne des activités annexes + Sum aside activities duration: Durée des activités annexes + filter: + Filter by aside activity date: Filtrer les activités annexes par date + Filter by aside activity type: Filtrer les activités annexes par type d'activité + 'Filtered by aside activity type: only %type%': "Filtré par type d'activité annexe: uniquement %type%" + Filtered by aside activities between %dateFrom% and %dateTo%: Filtré par date d'activité annexe, entre %dateFrom% et %dateTo% + This date should be after the date given in "Implied in an aside activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités annexes après cette date" + Aside activities after this date: Actvitités annexes après cette date + Aside activities before this date: Actvitités annexes avant cette date + 'Filtered aside activity by user: only %users%': "Filtré par utilisateur: uniquement %users%" + Filter aside activity by user: Filtrer par utilisateur + 'Filtered aside activities by user jobs: only %jobs%': "Filtré par métier des utilisateurs: uniquement %jobs%" + Filter by user jobs: Filtrer les activités annexes par métier des utilisateurs + 'Filtered aside activities by user scope: only %scopes%': "Filtré par service des utilisateur: uniquement %scopes%" + Filter by user scope: Filtrer les activités annexes par service d'utilisateur + aggregator: + Group by aside activity type: Grouper les activités annexes par type d'activité + Aside activity type: Type d'activité annexe + Aggregate by user job: Grouper les activités annexes par métier des utilisateurs + Aggregate by user scope: Grouper les activités annexes par service des utilisateurs # ROLES CHILL_ASIDE_ACTIVITY_STATS: Statistiques pour les activités annexes diff --git a/src/Bundle/ChillMainBundle/Export/ExportInterface.php b/src/Bundle/ChillMainBundle/Export/ExportInterface.php index be43ad47a..79b59d241 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportInterface.php +++ b/src/Bundle/ChillMainBundle/Export/ExportInterface.php @@ -83,9 +83,9 @@ interface ExportInterface extends ExportElementInterface * * @param string $key The column key, as added in the query * @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR') - * @param mixed $data The data from the export's form (as defined in `buildForm` + * @param mixed $data The data from the export's form (as defined in `buildForm`) * - * @return Closure where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }` + * @return pure-callable(null|string|int|float|'_header' $value):string|int|\DateTimeInterface where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }` */ public function getLabels($key, array $values, $data); diff --git a/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php index 86a2458b2..648632e15 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php @@ -35,6 +35,10 @@ class DateTimeHelper return ''; } + if ($value instanceof \DateTimeInterface) { + return $value; + } + // warning: won't work with DateTimeImmutable as we reset time a few lines later $date = DateTime::createFromFormat('Y-m-d', $value); $hasTime = false; From 68f7a832b4edde8d1e74face6cf6908133aecd2a Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 8 Feb 2023 14:42:16 +0000 Subject: [PATCH 31/62] Fixed: [activity] fetch all the available location, beyond the first page --- .../Controller/LocationController.php | 2 +- .../components/AdminLocation.vue | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/LocationController.php b/src/Bundle/ChillMainBundle/Controller/LocationController.php index f3c4db082..232c9c6cf 100644 --- a/src/Bundle/ChillMainBundle/Controller/LocationController.php +++ b/src/Bundle/ChillMainBundle/Controller/LocationController.php @@ -28,7 +28,7 @@ class LocationController extends CRUDController protected function customizeQuery(string $action, Request $request, $query): void { - $query->where('e.availableForUsers = true'); //TODO not working + $query->where('e.availableForUsers = "TRUE"'); } protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue index 9b3a6ade9..35b2f8847 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue @@ -34,7 +34,7 @@