From fd5efdcd4e979d1ffa9e012e4207ddaa795cbfc6 Mon Sep 17 00:00:00 2001
From: Laurent Laffont <llaffont@afi-sa.fr>
Date: Thu, 9 Sep 2021 12:01:48 +0200
Subject: [PATCH] dev #138425 Agenda / Multiple timings :

- display event period on agenda widget (Templating & Historic)
- fix fetch of next timing in current month
---
 VERSIONS_WIP/138425                           |   1 +
 library/Class/Article/EventTimingPeriod.php   |  49 ++++++
 library/Class/Article/SelectWithTimings.php   |   2 +-
 .../View/Helper/Article/RenderAbstract.php    |  20 ++-
 .../Library/View/Wrapper/Article.php          |  10 ++
 .../ArticlesMultipleTimingsCalendarTest.php   | 150 +++++++++++++++---
 .../ArticlesMultipleTimingsLoaderTest.php     |   2 +-
 7 files changed, 205 insertions(+), 29 deletions(-)
 create mode 100644 VERSIONS_WIP/138425
 create mode 100644 library/Class/Article/EventTimingPeriod.php

diff --git a/VERSIONS_WIP/138425 b/VERSIONS_WIP/138425
new file mode 100644
index 00000000000..c007496e6e5
--- /dev/null
+++ b/VERSIONS_WIP/138425
@@ -0,0 +1 @@
+ - ticket #138425 : Agenda avec horaires multiples : correction des dates de début / fin affichées. Affichage de la période de l'ensemble des horaires.
\ No newline at end of file
diff --git a/library/Class/Article/EventTimingPeriod.php b/library/Class/Article/EventTimingPeriod.php
new file mode 100644
index 00000000000..47f328c7eaf
--- /dev/null
+++ b/library/Class/Article/EventTimingPeriod.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_Article_EventTimingPeriod {
+  protected
+    $_article;
+
+  public function __construct($article) {
+    $this->_article = $article;
+  }
+
+
+  public function startEndDo($callback) {
+    if (!Class_AdminVar::get('ENABLE_ARTICLES_TIMINGS', 1))
+      return $this;
+
+    $timings = $this->_article->getEventTimings();
+    if (count($timings) < 2)
+      return $this;
+
+    $start = strtotime($timings[0]->getStart());
+    $end = strtotime(end($timings)->getEnd());
+
+    if (date('Ymd', $start) == date('Ymd', $end))
+      return $this;
+
+    $callback($start, $end);
+    return $this;
+  }
+}
diff --git a/library/Class/Article/SelectWithTimings.php b/library/Class/Article/SelectWithTimings.php
index 2ca761cab01..4c7f2b42649 100644
--- a/library/Class/Article/SelectWithTimings.php
+++ b/library/Class/Article/SelectWithTimings.php
@@ -28,7 +28,7 @@ class Class_Article_SelectWithTimings extends Class_Article_Select {
     $this->_select
       ->joinLeft('cms_article_timings',
                  'cms_article.id_article=cms_article_timings.article_id',
-                 ['start', 'end'])
+                 ['min(cms_article_timings.start) as start', 'min(cms_article_timings.end) as end'])
       ->group('id_article');
     return $this;
   }
diff --git a/library/ZendAfi/View/Helper/Article/RenderAbstract.php b/library/ZendAfi/View/Helper/Article/RenderAbstract.php
index b99f325c0f6..afae61839bd 100644
--- a/library/ZendAfi/View/Helper/Article/RenderAbstract.php
+++ b/library/ZendAfi/View/Helper/Article/RenderAbstract.php
@@ -29,7 +29,8 @@ abstract class ZendAfi_View_Helper_Article_RenderAbstract
                     . $this->renderTitreHeader($article)
                     . $this->renderDraftStatus($article)
                     . $this->renderArticleInfo($article)
-                    . $this->tagArticleEvent($article))
+                    . $this->tagArticleEvent($article)
+                    . $this->renderEventPeriod($article))
       . $this->_tag('div', $this->renderContent($article),
                     ['class' => 'article_content'])
       . $this->_tag('footer',
@@ -150,4 +151,21 @@ abstract class ZendAfi_View_Helper_Article_RenderAbstract
 
 
   public function renderTimings($article) { return ''; }
+
+
+  public function renderEventPeriod($article) {
+    $content = '';
+
+    (new Class_Article_EventTimingPeriod($article))
+      ->startEndDo(
+          function($start, $end) use (&$content) {
+            $content = $this->view->tag('span',
+                                        implode(' - ',
+                                                [strftime('%d %B', $start),
+                                                 strftime('%d %B', $end)]),
+                                        ['class' => 'calendar_event_period']);
+          });
+
+    return $content;
+  }
 }
diff --git a/library/templates/Intonation/Library/View/Wrapper/Article.php b/library/templates/Intonation/Library/View/Wrapper/Article.php
index 250ce04f6a0..d6ea3d4c3bc 100644
--- a/library/templates/Intonation/Library/View/Wrapper/Article.php
+++ b/library/templates/Intonation/Library/View/Wrapper/Article.php
@@ -290,6 +290,16 @@ class Intonation_Library_View_Wrapper_Article extends Intonation_Library_View_Wr
         ->setTag('span')
         ->setText($this->_eventsText($end))
         ->setClass('badge_event_end');
+
+
+    $period = new Class_Article_EventTimingPeriod($this->_model);
+    $period->startEndDo(function($start, $end) use (&$badges) {
+      $badges []= (new Intonation_Library_Badge)
+        ->setTag('span')
+        ->setText(strftime('%d %B', $start) . ' - ' . strftime('%d %B', $end))
+        ->setClass('badge_event_period');
+    });
+
     return $badges;
   }
 
diff --git a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsCalendarTest.php b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsCalendarTest.php
index 54f2d6ba7f3..52fc2e702b8 100644
--- a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsCalendarTest.php
+++ b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsCalendarTest.php
@@ -19,9 +19,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
-
-class ArticlesMultipleTimingsCalendarTest extends ViewHelperTestCase {
-protected $_storm_default_to_volatile = true;
+abstract class ArticlesMultipleTimingsCalendarTestCase extends AbstractControllerTestCase {
+    protected $_storm_default_to_volatile = true;
 
 
   public function setUp() {
@@ -70,6 +69,28 @@ protected $_storm_default_to_volatile = true;
                                                                           $apero_timing_two]]);
 
 
+    $dessert = Class_Article::newInstance(['id' => 2,
+                                           'titre' => 'Dessert',
+                                           'events_debut' => '2021-06-12 09:00']);
+
+    $digeo = Class_Article::newInstance(['id' => 3,
+                                         'titre' => 'Digeo',
+                                         'description' => 'Calendar should not display calendar event period when timings on same day',
+                                         'events_debut' => '2021-06-13 21:00',
+                                         'events_fin' => '2021-06-13 22:00',
+                                         'event_timings' => [
+                                                             $this->fixture(Class_Article_EventTiming::class,
+                                                                            ['id' => 4,
+                                                                             'start' => '2021-06-13 21:00:00',
+                                                                             'end' => '2021-06-13 22:00:00']),
+
+                                                             $this->fixture(Class_Article_EventTiming::class,
+                                                                            ['id' => 5,
+                                                                             'start' => '2021-06-13 23:00:00',
+                                                                             'end' => '2021-06-13 23:30:00'])
+                                         ]]);
+
+
     $this->onLoaderOfModel(Class_Article::class)
          ->whenCalled('getArticlesByPreferences')
          ->with([
@@ -98,9 +119,8 @@ protected $_storm_default_to_volatile = true;
                  'event_end_after' => '',
                  'limit' => 3])
          ->answers([$apero_second_fetch,
-                    Class_Article::newInstance(['id' => 2,
-                                                'titre' => 'Dessert',
-                                                'events_debut' => '2021-06-12 09:00'])
+                    $dessert,
+                    $digeo
                     ])
 
          ->whenCalled('getArticlesByPreferences')
@@ -113,27 +133,41 @@ protected $_storm_default_to_volatile = true;
                  'id_lieu' => '',
                  'custom_fields' => [],
                  'published' => true])
-         ->answers([])
+         ->answers([]);
+
+    ZendAfi_View_Helper_CalendarContent::setTimeSource(new TimeSourceForTest('2021-06-01 09:00:00'));
 
-         ->beStrict();
+    Class_AdminVar::set('TEMPLATING', 1);
+    Class_AdminVar::set('ENABLE_ARTICLES_TIMINGS', 1);
+  }
+}
 
 
-    ZendAfi_View_Helper_CalendarContent::setTimeSource(new TimeSourceForTest('2021-06-01 09:00:00'));
 
-    $params = ['division' => '2',
-               'type_module' => 'CALENDAR',
-               'preferences' => ['titre' => 'Agenda']];
-    $helper = new ZendAfi_View_Helper_Accueil_Calendar(2, $params);
-    $helper->setView(new ZendAfi_Controller_Action_Helper_View());
-    $this->html = $helper->getBoite();
+
+class ArticlesMultipleTimingsCalendarInHistoricTest extends ArticlesMultipleTimingsCalendarTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $profile = $this->fixture(Class_Profil::class,
+                              ['id' => 34,
+                               'template' => 'Historic'])
+                    ->beCurrentProfil();
+    $profile_patcher = (new Class_Template_ProfilePatcher(null))->setProfile($profile);
+    $profile_patcher
+      ->addWidget(Class_Systeme_ModulesAccueil_Calendrier::CODE,
+                  2,
+                  ['titre' => 'Agenda',
+                   'mode-affichage' => 'wall']);
+
+    $this->dispatch('/');
   }
 
 
   /** @test */
   public function calendarShouldContainsOnlyOneArticleApero() {
     $this
-      ->assertXPathCount($this->html,
-                         '//a[@class="calendar_event_title"][contains(@href, "cms/articleview/id/1")]',
+      ->assertXPathCount('//span[@class="title"]/a[contains(@href, "cms/articleview/id/1")]',
                          1);
   }
 
@@ -141,26 +175,90 @@ protected $_storm_default_to_volatile = true;
   /** @test */
   public function calendarShouldContainsArticleWithFirstEventsDebut2021_06_01() {
     $this
-      ->assertXPathContentContains($this->html,
-                                   '//span[@class="calendar_event_date"]',
+      ->assertXPathContentContains('//span[@class="calendar_event_date"]',
                                    'mardi 01 juin 2021');
 
   }
 
 
   /** @test */
-  public function calendarShouldContainsTwoArticles() {
+  public function calendarShouldContainsThreeArticles() {
     $this
-      ->assertXPathCount($this->html,
-                         '//a[@class="calendar_event_title"]',
-                         2);
+      ->assertXPathCount('//article',
+                         3);
   }
 
 
   /** @test */
   public function calendarSecondArticleShouldBeDessert() {
-      $this
-      ->assertXPath($this->html,
-                    '//a[@class="calendar_event_title"][contains(@href, "cms/articleview/id/2")]');
+    $this
+      ->assertXPath('//span[@class="title"]/a[contains(@href, "cms/articleview/id/2")]');
+  }
+
+
+  /** @test */
+  public function articleAperoEventDataShouldContainsSpanCalendarEventPeriod_01Juin_10Juin() {
+    $this
+      ->assertXPathContentContains('//article[1]//span[@class="calendar_event_period"]',
+                                   '01 juin - 10 juin');
+  }
+
+
+  /** @test */
+  public function articleDessertEventDataShouldNotContainsSpanCalendarEventPeriod() {
+    $this
+      ->assertNotXPath('//article[2]//span[@class="calendar_event_period"]');
+  }
+}
+
+
+
+
+class ArticlesMultipleTimingsCalendarInMuscleTest extends ArticlesMultipleTimingsCalendarTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $profile = $this->fixture(Class_Profil::class,
+                              ['id' => 34,
+                               'template' => 'MUSCLE'])
+                    ->beCurrentProfil();
+
+    $profile_patcher = (new Class_Template_ProfilePatcher(null))->setProfile($profile);
+    $profile_patcher
+      ->addWidget(Intonation_Library_Widget_Carousel_Agenda_Definition::CODE,
+                  Class_Profil::DIV_MAIN,
+                  ['rendering' => 'card-description',
+                   'layout' => 'wall',
+                   'size' => 3]);
+
+    $this->dispatch('/');
+  }
+
+
+  /** @test */
+  public function articleAperoEventDataShouldContainsBadgeEventPeriod_01Juin_10Juin() {
+    $this
+      ->assertXPathContentContains('//div[@class="masonry-brick"][1]//span[contains(@class,"badge_event_period")]',
+                                   '01 juin - 10 juin');
+  }
+
+
+  /** @test */
+  public function articleAperoEventDataShouldNotContainsSpanCalendarEventPeriod() {
+    $this
+      ->assertNotXPath('//span[@class="calendar_event_period"]');
+  }
+
+
+  /** @test */
+  public function divCardTitleShouldContainsArticleDigeo() {
+    $this->assertXPathContentContains('//div[contains(@class, "card-title")]/a', 'Digeo');
+  }
+
+
+  /** @test */
+  public function onlyOneArticleShouldContainsSpanCalendarEventPeriod() {
+    $this
+      ->assertXPathCount('//span[contains(@class,"badge_event_period")]', 1);
   }
 }
diff --git a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsLoaderTest.php b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsLoaderTest.php
index 884846da7f9..91db60b3963 100644
--- a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsLoaderTest.php
+++ b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsLoaderTest.php
@@ -53,7 +53,7 @@ class ArticlesMultipleTimingsLoaderTest extends ArticleLoaderGetArticlesByPrefer
 
 
   public function assertSelect($expected) {
-    $this->assertEquals("SELECT `cms_article`.*, `cms_article_timings`.`start`, `cms_article_timings`.`end` FROM `cms_article` ".$expected,
+    $this->assertEquals("SELECT `cms_article`.*, min(cms_article_timings.start) AS `start`, min(cms_article_timings.end) AS `end` FROM `cms_article` ".$expected,
                         str_replace("\n", "", $this->select->assemble()));
   }
 
-- 
GitLab