diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a5bdafb5..fe5fe6478 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,16 @@ and this project adheres to ## Unreleased +* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche +* renomme date de début en date d'ouverture dans le formulaire parcours + + +## Test releases + +### test release 2021-01-31 + +[fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413 +[homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc. * [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409) * [person] accompanying course: close modal when edit participation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420) * [person] accompanying course: treat validation error when editing on-the-fly entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420) @@ -22,9 +32,9 @@ and this project adheres to * [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389) * [household member editor] allow to push to existing household * [person_resource]: Onthefly button added to view person/thirdparty and badge differentiation for a contact-thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/428) +* [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal -## Test releases ### test release 2021-01-28 @@ -40,7 +50,6 @@ and this project adheres to ### test release 2021-01-26 ->>>>>>> origin/master * [parcours] comments truncated if too long + link added (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/406) * [person]: possibility to add person resources (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/382) * [person ressources]: module added diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d996ff3b8..20970a799 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -80,11 +80,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php - - - message: "#^Foreach overwrites \\$action with its value variable\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php - - message: "#^Foreach overwrites \\$action with its value variable\\.$#" count: 1 diff --git a/phpstan-critical.neon b/phpstan-critical.neon index b214654bf..632356aa6 100644 --- a/phpstan-critical.neon +++ b/phpstan-critical.neon @@ -30,36 +30,6 @@ parameters: count: 2 path: src/Bundle/ChillPersonBundle/Household/MembersEditorFactory.php - - - message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php - - - - message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:countBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php - - - - message: "#^Undefined variable\\: \\$action$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php - - - - message: "#^Undefined variable\\: \\$limit$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php - - - - message: "#^Undefined variable\\: \\$offset$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php - - - - message: "#^Undefined variable\\: \\$orderBy$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php - - message: "#^Variable variables are not allowed\\.$#" count: 4 diff --git a/phpstan-types.neon b/phpstan-types.neon index 949ff774a..2cc55255a 100644 --- a/phpstan-types.neon +++ b/phpstan-types.neon @@ -400,11 +400,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php - - - message: "#^Method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid return type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\QueryBuilder\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 3 diff --git a/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss b/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss index 8b88f0e78..5c1c83d06 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss +++ b/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss @@ -34,6 +34,8 @@ p.date-label { font-size: 18pt; } div.dashboard, +h4.badge-title, +h3.badge-title, h2.badge-title { ul.list-content { font-size: 70%; diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig index 7ae24fa4b..f8b68eabd 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig @@ -2,10 +2,12 @@ {% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) %} {% if no_action is not defined or no_action == false %}
  • - {{ 'notification.Notify'|trans }} + }) }}"> + + {{ 'notification.Notify'|trans }}
  • {% endif %} {% if context == 'person' and activity.accompanyingPeriod is not empty %} diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig index a5e3bd1fd..ce4c22304 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig @@ -177,6 +177,13 @@ +
    + {% set notifications = chill_list_notifications('Chill\\ActivityBundle\\Entity\\Activity', entity.id) %} + {% if notifications is not empty %} + {{ notifications|raw }} + {% endif %} +
    + {% set person_id = null %} {% if person %} {% set person_id = person.id %} @@ -193,18 +200,21 @@ {{ 'Back to the list'|trans }} - {% if is_granted('CHILL_ACTIVITY_UPDATE', entity) %} -
  • - - {{ 'Edit'|trans }} +
  • + + {{ 'notification.Notify'|trans }}
  • + {% if is_granted('CHILL_ACTIVITY_UPDATE', entity) %} +
  • + {{ 'Edit'|trans }} +
  • {% endif %} {% if is_granted('CHILL_ACTIVITY_DELETE', entity) %}
  • - - {{ 'Delete'|trans }} - +
  • {% endif %} diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/showAccompanyingCourse.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/showAccompanyingCourse.html.twig index e8d84fc51..fbd7b20b4 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/showAccompanyingCourse.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/showAccompanyingCourse.html.twig @@ -25,19 +25,5 @@ {% endblock content %} {% block block_post_menu %} -
    - -
    - - - {{ 'notification.Notify'|trans }} - -
    - - {% set notifications = chill_list_notifications('Chill\\ActivityBundle\\Entity\\Activity', entity.id) %} - {% if notifications is not empty %} - {{ notifications|raw }} - {% endif %} - -
    +
    {% endblock %} diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/showPerson.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/showPerson.html.twig index 4723edf27..e593b0131 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/showPerson.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/showPerson.html.twig @@ -28,8 +28,7 @@
    - - + {{ 'notification.Notify'|trans }}
    diff --git a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.html.twig b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.html.twig index 4ec11a8e8..0a1942f6d 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.html.twig +++ b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/_workflow.html.twig @@ -21,24 +21,28 @@
    +{% set freezed = false %} +{% for step in entity_workflow.stepsChained %} + {% if loop.last %} + {% if step.previous is not null and step.previous.freezeAfter == true %} + {% set freezed = true %} + {% endif %} + {% endif %} +{% endfor %} + {% if display_action is defined and display_action == true %} {% endif %} \ No newline at end of file diff --git a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/show.html.twig b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/show.html.twig index 9d6175e12..610b5249b 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/show.html.twig +++ b/src/Bundle/ChillDocStoreBundle/Resources/views/AccompanyingCourseDocument/show.html.twig @@ -56,17 +56,18 @@ {% endif %} + {% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %} + {% if workflows_frame is not empty %} +
  • + {{ workflows_frame|raw }} +
  • + {% endif %} {% endblock %} {% block block_post_menu %} -
    - {% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %} - {% if workflows_frame is not empty %} - {{ workflows_frame|raw }} - {% endif %} -
    +
    {% endblock %} {% block js %} diff --git a/src/Bundle/ChillMainBundle/Controller/NotificationApiController.php b/src/Bundle/ChillMainBundle/Controller/NotificationApiController.php index 8751aa50a..c43015efd 100644 --- a/src/Bundle/ChillMainBundle/Controller/NotificationApiController.php +++ b/src/Bundle/ChillMainBundle/Controller/NotificationApiController.php @@ -13,13 +13,19 @@ namespace Chill\MainBundle\Controller; use Chill\MainBundle\Entity\Notification; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Pagination\PaginatorFactory; +use Chill\MainBundle\Repository\NotificationRepository; use Chill\MainBundle\Security\Authorization\NotificationVoter; +use Chill\MainBundle\Serializer\Model\Collection; +use Chill\MainBundle\Serializer\Model\Counter; use Doctrine\ORM\EntityManagerInterface; use RuntimeException; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Security; +use Symfony\Component\Serializer\SerializerInterface; use UnexpectedValueException; /** @@ -29,12 +35,26 @@ class NotificationApiController { private EntityManagerInterface $entityManager; + private NotificationRepository $notificationRepository; + + private PaginatorFactory $paginatorFactory; + private Security $security; - public function __construct(EntityManagerInterface $entityManager, Security $security) - { + private SerializerInterface $serializer; + + public function __construct( + EntityManagerInterface $entityManager, + NotificationRepository $notificationRepository, + PaginatorFactory $paginatorFactory, + Security $security, + SerializerInterface $serializer + ) { $this->entityManager = $entityManager; + $this->notificationRepository = $notificationRepository; + $this->paginatorFactory = $paginatorFactory; $this->security = $security; + $this->serializer = $serializer; } /** @@ -53,6 +73,37 @@ class NotificationApiController return $this->markAs('unread', $notification); } + /** + * @Route("/my/unread") + */ + public function myUnreadNotifications(Request $request): JsonResponse + { + $total = $this->notificationRepository->countUnreadByUser($this->security->getUser()); + + if ($request->query->getBoolean('countOnly')) { + return new JsonResponse( + $this->serializer->serialize(new Counter($total), 'json', ['groups' => ['read']]), + JsonResponse::HTTP_OK, + [], + true + ); + } + $paginator = $this->paginatorFactory->create($total); + $notifications = $this->notificationRepository->findUnreadByUser( + $this->security->getUser(), + $paginator->getItemsPerPage(), + $paginator->getCurrentPageFirstItemNumber() + ); + $collection = new Collection($notifications, $paginator); + + return new JsonResponse( + $this->serializer->serialize($collection, 'json', ['groups' => ['read']]), + JsonResponse::HTTP_OK, + [], + true + ); + } + private function markAs(string $target, Notification $notification): JsonResponse { if (!$this->security->isGranted(NotificationVoter::NOTIFICATION_TOGGLE_READ_STATUS, $notification)) { diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php index 0d7142e9f..c7a322338 100644 --- a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php +++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflow.php @@ -25,6 +25,7 @@ use Iterator; use RuntimeException; use Symfony\Component\Serializer\Annotation as Serializer; use function count; +use function is_array; /** * @ORM\Entity @@ -51,7 +52,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") - * @Serializer\Groups({"read"}) */ private ?int $id = null; @@ -73,6 +73,11 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface */ private Collection $steps; + /** + * @var null|array|EntityWorkflowStep[] + */ + private ?array $stepsChainedCache = null; + /** * @ORM\ManyToMany(targetEntity=User::class) * @ORM\JoinTable(name="chill_main_workflow_entity_subscriber_to_final") @@ -130,10 +135,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface if (!$this->steps->contains($step)) { $this->steps[] = $step; $step->setEntityWorkflow($this); - - if ($this->isFinalize()) { - $step->setFinalizeAfter(true); - } } return $this; @@ -254,27 +255,33 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface public function getStepsChained(): array { + if (is_array($this->stepsChainedCache)) { + return $this->stepsChainedCache; + } + $iterator = $this->steps->getIterator(); - $previous = $next = $current = null; + $current = null; $steps = []; $iterator->rewind(); - while ($iterator->valid()) { + do { $previous = $current; - $steps[] = $current = $iterator->current(); + $current = $iterator->current(); + $steps[] = $current; + $current->setPrevious($previous); $iterator->next(); if ($iterator->valid()) { - $next = $iterator->current(); + $current->setNext($iterator->current()); } else { - $next = null; + $current->setNext(null); } + } while ($iterator->valid()); - $current->setNext($next); - } + $this->stepsChainedCache = $steps; return $steps; } @@ -309,7 +316,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface return $this->workflowName; } - public function isFinalize(): bool + public function isFinal(): bool { $steps = $this->getStepsChained(); @@ -321,7 +328,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface /** @var EntityWorkflowStep $last */ $last = end($steps); - return $last->getPrevious()->isFinalizeAfter(); + return $last->isFinal(); } public function isFreeze(): bool diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php index 6bd39ae0c..c68247174 100644 --- a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php +++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php @@ -53,11 +53,6 @@ class EntityWorkflowStep */ private ?EntityWorkflow $entityWorkflow = null; - /** - * @ORM\Column(type="boolean", options={"default": false}) - */ - private bool $finalizeAfter = false; - /** * @ORM\Column(type="boolean", options={"default": false}) */ @@ -70,6 +65,11 @@ class EntityWorkflowStep */ private ?int $id = null; + /** + * @ORM\Column(type="boolean", options={"default": false}) + */ + private bool $isFinal = false; + /** * filled by @see{EntityWorkflow::getStepsChained}. */ @@ -187,9 +187,9 @@ class EntityWorkflowStep return $this->transitionByEmail; } - public function isFinalizeAfter(): bool + public function isFinal(): bool { - return $this->finalizeAfter; + return $this->isFinal; } public function isFreezeAfter(): bool @@ -244,16 +244,16 @@ class EntityWorkflowStep return $this; } - public function setFinalizeAfter(bool $finalizeAfter): EntityWorkflowStep + public function setFreezeAfter(bool $freezeAfter): EntityWorkflowStep { - $this->finalizeAfter = $finalizeAfter; + $this->freezeAfter = $freezeAfter; return $this; } - public function setFreezeAfter(bool $freezeAfter): EntityWorkflowStep + public function setIsFinal(bool $isFinal): EntityWorkflowStep { - $this->freezeAfter = $freezeAfter; + $this->isFinal = $isFinal; return $this; } diff --git a/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php b/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php index 6600e330a..18ac10c47 100644 --- a/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php +++ b/src/Bundle/ChillMainBundle/Form/WorkflowStepType.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep; use Chill\MainBundle\Form\Type\ChillTextareaType; use Chill\MainBundle\Form\Type\PickUserDynamicType; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Workflow\EntityWorkflowManager; use LogicException; use Symfony\Component\Form\AbstractType; @@ -24,6 +25,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\Transition; +use function array_key_exists; class WorkflowStepType extends AbstractType { @@ -31,10 +33,13 @@ class WorkflowStepType extends AbstractType private Registry $registry; - public function __construct(EntityWorkflowManager $entityWorkflowManager, Registry $registry) + private TranslatableStringHelperInterface $translatableStringHelper; + + public function __construct(EntityWorkflowManager $entityWorkflowManager, Registry $registry, TranslatableStringHelperInterface $translatableStringHelper) { $this->entityWorkflowManager = $entityWorkflowManager; $this->registry = $registry; + $this->translatableStringHelper = $translatableStringHelper; } public function buildForm(FormBuilderInterface $builder, array $options) @@ -42,6 +47,7 @@ class WorkflowStepType extends AbstractType /** @var \Chill\MainBundle\Entity\Workflow\EntityWorkflow $entityWorkflow */ $entityWorkflow = $options['entity_workflow']; $handler = $this->entityWorkflowManager->getHandler($entityWorkflow); + $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()); if (true === $options['transition']) { if (null === $options['entity_workflow']) { @@ -53,20 +59,49 @@ class WorkflowStepType extends AbstractType ->getEnabledTransitions($entityWorkflow); $choices = array_combine( - array_map(static function (Transition $transition) { return $transition->getName(); }, $transitions), + array_map( + static function (Transition $transition) { + return $transition->getName(); + }, + $transitions + ), $transitions ); $builder ->add('transition', ChoiceType::class, [ - 'label' => 'workflow.Transition', + 'label' => 'workflow.Transition to apply', 'mapped' => false, 'multiple' => false, 'expanded' => true, 'choices' => $choices, - 'choice_label' => static function (Transition $transition) { - return implode(', ', $transition->getTos()); - }, + 'choice_label' => function (Transition $transition) use ($workflow) { + $meta = $workflow->getMetadataStore()->getTransitionMetadata($transition); + + if (array_key_exists('label', $meta)) { + return $this->translatableStringHelper->localize($meta['label']); + } + + return $transition->getName(); + }, + 'choice_attr' => static function (Transition $transition) use ($workflow) { + $toFinal = true; + + foreach ($transition->getTos() as $to) { + $meta = $workflow->getMetadataStore()->getPlaceMetadata($to); + + if ( + !array_key_exists('isFinal', $meta) || false === $meta['isFinal'] + ) { + $toFinal = false; + } + } + + return [ + 'data-is-transition' => 'data-is-transition', + 'data-to-final' => $toFinal ? '1' : '0', + ]; + }, ]) ->add('future_dest_users', PickUserDynamicType::class, [ 'label' => 'workflow.dest for next steps', @@ -88,11 +123,6 @@ class WorkflowStepType extends AbstractType } $builder - ->add('finalizeAfter', CheckboxType::class, [ - 'required' => false, - 'label' => 'workflow.Finalize', - 'help' => 'workflow.The workflow will be finalized', - ]) ->add('comment', ChillTextareaType::class, [ 'required' => false, 'label' => 'Comment', diff --git a/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php b/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php index d5ec75699..1e52e128e 100644 --- a/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php +++ b/src/Bundle/ChillMainBundle/Notification/Templating/NotificationTwigExtensionRuntime.php @@ -11,17 +11,27 @@ declare(strict_types=1); namespace Chill\MainBundle\Notification\Templating; +use Chill\MainBundle\Entity\NotificationComment; +use Chill\MainBundle\Form\NotificationCommentType; use Chill\MainBundle\Notification\NotificationPresence; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Twig\Environment; use Twig\Extension\RuntimeExtensionInterface; class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface { + private FormFactoryInterface $formFactory; + private NotificationPresence $notificationPresence; - public function __construct(NotificationPresence $notificationPresence) + private UrlGeneratorInterface $urlGenerator; + + public function __construct(FormFactoryInterface $formFactory, NotificationPresence $notificationPresence, UrlGeneratorInterface $urlGenerator) { + $this->formFactory = $formFactory; $this->notificationPresence = $notificationPresence; + $this->urlGenerator = $urlGenerator; } public function counterNotificationFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string @@ -47,8 +57,24 @@ class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface return ''; } + $appendCommentForms = []; + + foreach ($notifications as $notification) { + $appendComment = new NotificationComment(); + $appendCommentForms[$notification->getId()] = $this->formFactory->create( + NotificationCommentType::class, + $appendComment, + [ + 'action' => $this->urlGenerator->generate( + 'chill_main_notification_show', + ['id' => $notification->getId()] + ), + ] + )->createView(); + } + return $environment->render('@ChillMain/Notification/extension_list_notifications_for.html.twig', [ - 'notifications' => $notifications, + 'notifications' => $notifications, 'appendCommentForms' => $appendCommentForms, ]); } } diff --git a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php index 71966c973..9cfffb2cf 100644 --- a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php @@ -193,6 +193,29 @@ final class NotificationRepository implements ObjectRepository return $this->repository->findOneBy($criteria, $orderBy); } + /** + * @return array|Notification[] + */ + public function findUnreadByUser(User $user, int $limit = 20, int $offset = 0): array + { + $rsm = new Query\ResultSetMappingBuilder($this->em); + $rsm->addRootEntityFromClassMetadata(Notification::class, 'cmn'); + + $sql = 'SELECT ' . $rsm->generateSelectClause(['cmn' => 'cmn']) . ' ' . + 'FROM chill_main_notification cmn ' . + 'WHERE ' . + 'EXISTS (select 1 FROM chill_main_notification_addresses_unread cmnau WHERE cmnau.user_id = :userId and cmnau.notification_id = cmn.id) ' . + 'ORDER BY cmn.date DESC ' . + 'LIMIT :limit OFFSET :offset'; + + $nq = $this->em->createNativeQuery($sql, $rsm) + ->setParameter('userId', $user->getId()) + ->setParameter('limit', $limit) + ->setParameter('offset', $offset); + + return $nq->getResult(); + } + public function getClassName() { return Notification::class; diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss index 39f8b3f8a..eac04acc2 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss @@ -474,6 +474,7 @@ div.workflow { // Override bootstrap popover styles div.popover { box-shadow: 0 0 10px -5px $dark; + z-index: 9999; .popover-arrow {} .popover-header {} .popover-body {} diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss index a95c4b993..2c3459d39 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss @@ -21,7 +21,7 @@ $chill-theme-buttons: ( "download": $gray-300, "cancel": $gray-300, "choose": $gray-300, - "notify": $gray-300, + "notify": $chill-blue, "search": $gray-300, "unlink": $chill-red, "tpchild": $chill-pink, @@ -136,3 +136,18 @@ $chill-theme-buttons: ( .btn-sm, .btn-group-sm > .btn { min-width: 36px; } + +// Homepage special fast action buttons +div.sticky-buttons { + position: fixed; + bottom: 3em; + right: 2em; + .btn-circle { + width: 50px; height: 50px; + border-radius: 50%; + text-align: center; + padding: 0.45rem 0.7rem; + display: block; + margin-bottom: 0.5rem; + } +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-pick/index.js b/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-pick/index.js index d6d657719..980f52fa8 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-pick/index.js +++ b/src/Bundle/ChillMainBundle/Resources/public/module/entity-workflow-pick/index.js @@ -1,49 +1,34 @@ import { createApp } from "vue"; -import PickWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue'; -import ListWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue'; +import ListWorkflowModalVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue'; +import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n"; -// pick workflow -document.querySelectorAll('[data-pick-workflow]') - .forEach(function(el) { - const app = { - components: { - PickWorkflowVue - }, - template: - '', - data() { - return { - relatedEntityClass: el.dataset.relatedEntityClass, - relatedEntityId: Number.parseInt(el.dataset.relatedEntityId), - workflowsAvailables: JSON.parse(el.dataset.workflowsAvailables), - } - } - }; - createApp(app).mount(el); - }) -; +const i18n = _createI18n({}); // list workflow document.querySelectorAll('[data-list-workflows]') .forEach(function (el) { const app = { components: { - ListWorkflowVue, + ListWorkflowModalVue, }, template: - '', + ':allowCreate="allowCreate" ' + + ':relatedEntityClass="relatedEntityClass" ' + + ':relatedEntityId="relatedEntityId" ' + + ':workflowsAvailables="workflowsAvailables" ' + + '>', data() { return { workflows: JSON.parse(el.dataset.workflows), + allowCreate: el.dataset.allowCreate === "1", + relatedEntityClass: el.dataset.relatedEntityClass, + relatedEntityId: Number.parseInt(el.dataset.relatedEntityId), + workflowsAvailables: JSON.parse(el.dataset.workflowsAvailables), } } }; - createApp(app).mount(el); + createApp(app).use(i18n).mount(el); }) ; \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js b/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js new file mode 100644 index 000000000..698302ebb --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js @@ -0,0 +1,16 @@ +import { createApp } from 'vue'; +import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'; +import { appMessages } from 'ChillMainAssets/vuejs/HomepageWidget/js/i18n'; +import { store } from 'ChillMainAssets/vuejs/HomepageWidget/js/store'; +import App from 'ChillMainAssets/vuejs/HomepageWidget/App'; + +const i18n = _createI18n(appMessages); + +const app = createApp({ + template: ``, +}) +.use(store) +.use(i18n) +.component('app', App) +.mount('#homepage_widget') +; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue new file mode 100644 index 000000000..da3b9ba71 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue @@ -0,0 +1,140 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyAccompanyingCourses.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyAccompanyingCourses.vue new file mode 100644 index 000000000..220588914 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyAccompanyingCourses.vue @@ -0,0 +1,60 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue new file mode 100644 index 000000000..65de954ee --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue @@ -0,0 +1,77 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyEvaluations.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyEvaluations.vue new file mode 100644 index 000000000..baaf926f9 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyEvaluations.vue @@ -0,0 +1,57 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue new file mode 100644 index 000000000..135532f0b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue @@ -0,0 +1,96 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTasks.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTasks.vue new file mode 100644 index 000000000..c8bdcb256 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTasks.vue @@ -0,0 +1,85 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorks.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorks.vue new file mode 100644 index 000000000..19e3a92be --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorks.vue @@ -0,0 +1,79 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue new file mode 100644 index 000000000..5587e7176 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabTable.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabTable.vue new file mode 100644 index 000000000..15a2fe632 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabTable.vue @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js new file mode 100644 index 000000000..724ca3d34 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js @@ -0,0 +1,54 @@ +const appMessages = { + fr: { + main_title: "Vue d'ensemble", + my_works: { + tab: "Mes actions", + description: "Liste des actions d'accompagnement dont je suis référent et qui arrivent à échéance.", + }, + my_evaluations: { + tab: "Mes évaluations", + description: "Liste des évaluations dont je suis référent et qui arrivent à échéance.", + }, + my_tasks: { + tab: "Mes tâches", + description_alert: "Liste des tâches auxquelles je suis assigné et dont la date de rappel est dépassée.", + description_warning: "Liste des tâches auxquelles je suis assigné et dont la date d'échéance est dépassée.", + }, + my_accompanying_courses: { + tab: "Mes parcours", + description: "Liste des parcours d'accompagnement que l'on vient de m'attribuer.", + }, + my_notifications: { + tab: "Mes notifications", + description: "Liste des notifications reçues et non lues.", + }, + Date: "Date", + From: "De", + Subject: "Objet", + Entity: "Associé à", + show_entity: "Voir {entity}", + the_activity: "l'échange", + the_course: "le parcours", + the_action: "l'action", + the_evaluation: "l'évaluation", + the_task: "la tâche", + StartDate: "Date d'ouverture", + SocialAction: "Action d'accompagnement", + no_data: "Aucun résultats", + no_dashboard: "Pas de tableaux de bord", + counter: { + unread_notifications: "notifications non lues", + assignated_courses: "parcours récents assignés", + assignated_actions: "actions assignées", + assignated_evaluations: "évaluations assignées", + alert_tasks: "tâches en rappel", + warning_tasks: "tâches à échéances", + } + } +}; + +Object.assign(appMessages.fr); + +export { + appMessages +}; \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js new file mode 100644 index 000000000..81896e5ec --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js @@ -0,0 +1,200 @@ +import 'es6-promise/auto'; +import { createStore } from 'vuex'; +import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; +import MyCustoms from "../MyCustoms"; +import MyWorks from "../MyWorks"; +import MyEvaluations from "../MyEvaluations"; +import MyTasks from "../MyTasks"; +import MyAccompanyingCourses from "../MyAccompanyingCourses"; +import MyNotifications from "../MyNotifications"; + +const debug = process.env.NODE_ENV !== 'production'; + +const isEmpty = (obj) => { + return obj + && Object.keys(obj).length <= 1 + && Object.getPrototypeOf(obj) === Object.prototype; +}; + +const store = createStore({ + strict: debug, + state: { + works: {}, + evaluations: {}, + tasks: { + warning: {}, + alert: {} + }, + accompanyingCourses: {}, + notifications: {}, + errorMsg: [], + loading: false + }, + getters: { + isWorksLoaded(state) { + return !isEmpty(state.works); + }, + isEvaluationsLoaded(state) { + return !isEmpty(state.evaluations); + }, + isTasksWarningLoaded(state) { + return !isEmpty(state.tasks.warning); + }, + isTasksAlertLoaded(state) { + return !isEmpty(state.tasks.alert); + }, + isAccompanyingCoursesLoaded(state) { + return !isEmpty(state.accompanyingCourses); + }, + isNotificationsLoaded(state) { + return !isEmpty(state.notifications); + }, + counter(state) { + return { + works: state.works.count, + evaluations: state.evaluations.count, + tasksWarning: state.tasks.warning.count, + tasksAlert: state.tasks.alert.count, + accompanyingCourses: state.accompanyingCourses.count, + notifications: state.notifications.count, + } + } + }, + mutations: { + addWorks(state, works) { + console.log('addWorks', works); + state.works = works; + }, + addEvaluations(state, evaluations) { + console.log('addEvaluations', evaluations); + state.evaluations = evaluations; + }, + addTasksWarning(state, tasks) { + console.log('addTasksWarning', tasks); + state.tasks.warning = tasks; + }, + addTasksAlert(state, tasks) { + console.log('addTasksAlert', tasks); + state.tasks.alert = tasks; + }, + addCourses(state, courses) { + console.log('addCourses', courses); + state.accompanyingCourses = courses; + }, + addNotifications(state, notifications) { + console.log('addNotifications', notifications); + state.notifications = notifications; + }, + setLoading(state, bool) { + state.loading = bool; + }, + catchError(state, error) { + state.errorMsg.push(error); + } + }, + actions: { + getByTab({ commit, getters }, { tab, param }) { + switch (tab) { + case 'MyCustoms': + break; + case 'MyWorks': + if (!getters.isWorksLoaded) { + commit('setLoading', true); + const url = `/api/1.0/person/accompanying-period/work/my-near-end${'?'+ param}`; + makeFetch('GET', url) + .then((response) => { + commit('addWorks', response); + commit('setLoading', false); + }) + .catch((error) => { + commit('catchError', error); + throw error; + }) + ; + } + break; + case 'MyEvaluations': + if (!getters.isEvaluationsLoaded) { + commit('setLoading', true); + const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${'?'+ param}`; + makeFetch('GET', url) + .then((response) => { + commit('addEvaluations', response); + commit('setLoading', false); + }) + .catch((error) => { + commit('catchError', error); + throw error; + }) + ; + } + break; + case 'MyTasks': + if (!(getters.isTasksWarningLoaded && getters.isTasksAlertLoaded)) { + commit('setLoading', true); + const + urlWarning = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=warning&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${'&'+ param}`, + urlAlert = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=alert&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${'&'+ param}` + ; + makeFetch('GET', urlWarning) + .then((response) => { + commit('addTasksWarning', response); + commit('setLoading', false); + }) + .catch((error) => { + commit('catchError', error); + throw error; + }) + ; + makeFetch('GET', urlAlert) + .then((response) => { + commit('addTasksAlert', response); + commit('setLoading', false); + }) + .catch((error) => { + commit('catchError', error); + throw error; + }) + ; + } + break; + case 'MyAccompanyingCourses': + if (!getters.isAccompanyingCoursesLoaded) { + commit('setLoading', true); + const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${'?'+ param}`; + makeFetch('GET', url) + .then((response) => { + commit('addCourses', response); + commit('setLoading', false); + }) + .catch((error) => { + commit('catchError', error); + throw error; + }) + ; + } + break; + case 'MyNotifications': + if (!getters.isNotificationsLoaded) { + commit('setLoading', true); + const url = `/api/1.0/main/notification/my/unread${'?'+ param}`; + makeFetch('GET', url) + .then((response) => { + commit('addNotifications', response); + commit('setLoading', false); + }) + .catch((error) => { + commit('catchError', error); + throw error; + }) + ; + } + break; + default: + throw 'tab '+ tab; + } + } + }, +}); + +export { store }; \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue index 4a3346972..25513620a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue @@ -1,26 +1,86 @@ \ No newline at end of file + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue new file mode 100644 index 000000000..a188a3a02 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue @@ -0,0 +1,111 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/Homepage/fast_actions.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Homepage/fast_actions.html.twig new file mode 100644 index 000000000..9138212e2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Homepage/fast_actions.html.twig @@ -0,0 +1,3 @@ +
    + {# Override this file to add fast actions buttons #} +
    \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig new file mode 100644 index 000000000..aec38f3c3 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig @@ -0,0 +1,15 @@ +
    + + {# vue component #} +
    + + {% 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 diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/_item_comments.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/_item_comments.html.twig new file mode 100644 index 000000000..b741abf4a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/_item_comments.html.twig @@ -0,0 +1,77 @@ +{% import '@ChillPerson/AccompanyingCourse/Comment/macro_showItem.html.twig' as m %} + +{% macro recordAction(comment) %} + {% if is_granted('CHILL_MAIN_NOTIFICATION_COMMENT_EDIT', comment) %} +
  • + +
  • + {% endif %} +{% endmacro %} + +
    +

    {{ 'notification.comments_list'|trans }}

    + + {% if notification.comments|length > 0 %} +
    + {% for comment in notification.comments %} + + {% if editedCommentForm is null or editedCommentId != comment.id %} + {{ m.show_comment(comment, { + 'recordAction': _self.recordAction(comment) + }) }} + {% else %} +
    +
    + + + {{ form_start(editedCommentForm) }} + {{ form_errors(editedCommentForm) }} + {{ form_widget(editedCommentForm.content) }} + + + {{ form_end(editedCommentForm) }} + +
    +
    + {% endif %} + + {% endfor %} +
    + {% else %} + {{ 'No comments'|trans }} + {% endif %} + + {% if appendCommentForm is not null %} +
    +

    {{ 'Write a new comment'|trans }}

    + + {{ form_start(appendCommentForm) }} + {{ form_errors(appendCommentForm) }} + {{ form_widget(appendCommentForm.content) }} + + + {{ form_end(appendCommentForm) }} + +
    + {% endif %} +
    \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/_list_item.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/_list_item.html.twig index 5750a3a2c..57784a20b 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Notification/_list_item.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/_list_item.html.twig @@ -52,9 +52,11 @@ {% macro content(c) %}
    -
    - {% include c.data.template with c.data.template_data %} -
    + {% if c.data is defined %} +
    + {% include c.data.template with c.data.template_data %} +
    + {% endif %}
    @@ -68,34 +70,44 @@
    {% if c.action_button is not defined or c.action_button != false %}
    - +
    {% endif %} {% endmacro %} @@ -107,15 +119,19 @@ {{ _self.header(_context) }} +
    + {{ _self.content(_context) }} +
    {% else %} {{ _self.title(_context) }} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/create.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/create.html.twig index ce5934e52..f15380083 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Notification/create.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/create.html.twig @@ -20,6 +20,8 @@ {{ form_row(form.title, { 'label': 'notification.subject'|trans }) }} {{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }} + + {% include handler.template(notification) with handler.templateData(notification) %}
    @@ -28,8 +30,6 @@
    - {% include handler.template(notification) with handler.templateData(notification) %} - {{ form_end(form) }}