vendor/contao/calendar-bundle/src/Resources/contao/modules/ModuleEventlist.php line 52

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Contao.
  4.  *
  5.  * (c) Leo Feyer
  6.  *
  7.  * @license LGPL-3.0-or-later
  8.  */
  9. namespace Contao;
  10. use Contao\CoreBundle\Exception\PageNotFoundException;
  11. use Contao\CoreBundle\Image\Studio\Studio;
  12. use Patchwork\Utf8;
  13. /**
  14.  * Front end module "event list".
  15.  *
  16.  * @property bool   $cal_noSpan
  17.  * @property string $cal_template
  18.  * @property int    $cal_limit
  19.  * @property string $cal_order
  20.  * @property array  $cal_calendar
  21.  * @property string $cal_format
  22.  * @property bool   $cal_ignoreDynamic
  23.  * @property int    $cal_readerModule
  24.  * @property bool   $cal_hideRunning
  25.  * @property string $cal_featured
  26.  *
  27.  * @author Leo Feyer <https://github.com/leofeyer>
  28.  */
  29. class ModuleEventlist extends Events
  30. {
  31.     /**
  32.      * Current date object
  33.      * @var Date
  34.      */
  35.     protected $Date;
  36.     /**
  37.      * Template
  38.      * @var string
  39.      */
  40.     protected $strTemplate 'mod_eventlist';
  41.     /**
  42.      * Display a wildcard in the back end
  43.      *
  44.      * @return string
  45.      */
  46.     public function generate()
  47.     {
  48.         $request System::getContainer()->get('request_stack')->getCurrentRequest();
  49.         if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request))
  50.         {
  51.             $objTemplate = new BackendTemplate('be_wildcard');
  52.             $objTemplate->wildcard '### ' Utf8::strtoupper($GLOBALS['TL_LANG']['FMD']['eventlist'][0]) . ' ###';
  53.             $objTemplate->title $this->headline;
  54.             $objTemplate->id $this->id;
  55.             $objTemplate->link $this->name;
  56.             $objTemplate->href 'contao/main.php?do=themes&amp;table=tl_module&amp;act=edit&amp;id=' $this->id;
  57.             return $objTemplate->parse();
  58.         }
  59.         $this->cal_calendar $this->sortOutProtected(StringUtil::deserialize($this->cal_calendartrue));
  60.         // Return if there are no calendars
  61.         if (empty($this->cal_calendar) || !\is_array($this->cal_calendar))
  62.         {
  63.             return '';
  64.         }
  65.         // Show the event reader if an item has been selected
  66.         if ($this->cal_readerModule 0  && (isset($_GET['events']) || (Config::get('useAutoItem') && isset($_GET['auto_item']))))
  67.         {
  68.             return $this->getFrontendModule($this->cal_readerModule$this->strColumn);
  69.         }
  70.         // Tag the calendars (see #2137)
  71.         if (System::getContainer()->has('fos_http_cache.http.symfony_response_tagger'))
  72.         {
  73.             $responseTagger System::getContainer()->get('fos_http_cache.http.symfony_response_tagger');
  74.             $responseTagger->addTags(array_map(static function ($id) { return 'contao.db.tl_calendar.' $id; }, $this->cal_calendar));
  75.         }
  76.         return parent::generate();
  77.     }
  78.     /**
  79.      * Generate the module
  80.      */
  81.     protected function compile()
  82.     {
  83.         /** @var PageModel $objPage */
  84.         global $objPage;
  85.         $blnClearInput false;
  86.         $intYear Input::get('year');
  87.         $intMonth Input::get('month');
  88.         $intDay Input::get('day');
  89.         // Handle featured events
  90.         $blnFeatured null;
  91.         if ($this->cal_featured == 'featured')
  92.         {
  93.             $blnFeatured true;
  94.         }
  95.         elseif ($this->cal_featured == 'unfeatured')
  96.         {
  97.             $blnFeatured false;
  98.         }
  99.         // Jump to the current period
  100.         if (!isset($_GET['year']) && !isset($_GET['month']) && !isset($_GET['day']))
  101.         {
  102.             switch ($this->cal_format)
  103.             {
  104.                 case 'cal_year':
  105.                     $intYear date('Y');
  106.                     break;
  107.                 case 'cal_month':
  108.                     $intMonth date('Ym');
  109.                     break;
  110.                 case 'cal_day':
  111.                     $intDay date('Ymd');
  112.                     break;
  113.             }
  114.             $blnClearInput true;
  115.         }
  116.         $blnDynamicFormat = (!$this->cal_ignoreDynamic && \in_array($this->cal_format, array('cal_day''cal_month''cal_year')));
  117.         // Create the date object
  118.         try
  119.         {
  120.             if ($blnDynamicFormat && $intYear)
  121.             {
  122.                 $this->Date = new Date($intYear'Y');
  123.                 $this->cal_format 'cal_year';
  124.                 $this->headline .= ' ' date('Y'$this->Date->tstamp);
  125.             }
  126.             elseif ($blnDynamicFormat && $intMonth)
  127.             {
  128.                 $this->Date = new Date($intMonth'Ym');
  129.                 $this->cal_format 'cal_month';
  130.                 $this->headline .= ' ' Date::parse('F Y'$this->Date->tstamp);
  131.             }
  132.             elseif ($blnDynamicFormat && $intDay)
  133.             {
  134.                 $this->Date = new Date($intDay'Ymd');
  135.                 $this->cal_format 'cal_day';
  136.                 $this->headline .= ' ' Date::parse($objPage->dateFormat$this->Date->tstamp);
  137.             }
  138.             else
  139.             {
  140.                 $this->Date = new Date();
  141.             }
  142.         }
  143.         catch (\OutOfBoundsException $e)
  144.         {
  145.             throw new PageNotFoundException('Page not found: ' Environment::get('uri'));
  146.         }
  147.         list($intStart$intEnd$strEmpty) = $this->getDatesFromFormat($this->Date$this->cal_format);
  148.         // Get all events
  149.         $arrAllEvents $this->getAllEvents($this->cal_calendar$intStart$intEnd$blnFeatured);
  150.         $sort = ($this->cal_order == 'descending') ? 'krsort' 'ksort';
  151.         // Sort the days
  152.         $sort($arrAllEvents);
  153.         // Sort the events
  154.         foreach (array_keys($arrAllEvents) as $key)
  155.         {
  156.             $sort($arrAllEvents[$key]);
  157.         }
  158.         $arrEvents = array();
  159.         // Remove events outside the scope
  160.         foreach ($arrAllEvents as $days)
  161.         {
  162.             foreach ($days as $day=>$events)
  163.             {
  164.                 // Skip events before the start day if the "shortened view" option is not set.
  165.                 // Events after the end day are filtered in the Events::addEvent() method (see #8782).
  166.                 if (!$this->cal_noSpan && $day $intStart)
  167.                 {
  168.                     continue;
  169.                 }
  170.                 foreach ($events as $event)
  171.                 {
  172.                     // Use repeatEnd if > 0 (see #8447)
  173.                     if ($event['startTime'] > $intEnd || ($event['repeatEnd'] ?: $event['endTime']) < $intStart)
  174.                     {
  175.                         continue;
  176.                     }
  177.                     // Hide running events
  178.                     if ($this->cal_hideRunning && $event['begin'] < $intStart)
  179.                     {
  180.                         continue;
  181.                     }
  182.                     // Skip occurrences in the past
  183.                     if ($event['repeatEnd'] && $event['end'] < $intStart)
  184.                     {
  185.                         continue;
  186.                     }
  187.                     // Hide running non-recurring events (see #30)
  188.                     if ($this->cal_hideRunning && !$event['recurring'] && $event['startTime'] < time())
  189.                     {
  190.                         continue;
  191.                     }
  192.                     $event['firstDay'] = $GLOBALS['TL_LANG']['DAYS'][date('w'$day)];
  193.                     $event['firstDate'] = Date::parse($objPage->dateFormat$day);
  194.                     $arrEvents[] = $event;
  195.                 }
  196.             }
  197.         }
  198.         unset($arrAllEvents);
  199.         $total = \count($arrEvents);
  200.         $limit $total;
  201.         $offset 0;
  202.         // Overall limit
  203.         if ($this->cal_limit 0)
  204.         {
  205.             $total min($this->cal_limit$total);
  206.             $limit $total;
  207.         }
  208.         // Pagination
  209.         if ($this->perPage 0)
  210.         {
  211.             $id 'page_e' $this->id;
  212.             $page Input::get($id) ?? 1;
  213.             // Do not index or cache the page if the page number is outside the range
  214.             if ($page || $page max(ceil($total/$this->perPage), 1))
  215.             {
  216.                 throw new PageNotFoundException('Page not found: ' Environment::get('uri'));
  217.             }
  218.             $offset = ($page 1) * $this->perPage;
  219.             $limit min($this->perPage $offset$total);
  220.             $objPagination = new Pagination($total$this->perPageConfig::get('maxPaginationLinks'), $id);
  221.             $this->Template->pagination $objPagination->generate("\n  ");
  222.         }
  223.         $strMonth '';
  224.         $strDate '';
  225.         $strEvents '';
  226.         $dayCount 0;
  227.         $eventCount 0;
  228.         $headerCount 0;
  229.         $uuids = array();
  230.         for ($i=$offset$i<$limit$i++)
  231.         {
  232.             if ($arrEvents[$i]['addImage'] && $arrEvents[$i]['singleSRC'])
  233.             {
  234.                 $uuids[] = $arrEvents[$i]['singleSRC'];
  235.             }
  236.         }
  237.         // Preload all images in one query so they are loaded into the model registry
  238.         FilesModel::findMultipleByUuids($uuids);
  239.         // Parse events
  240.         for ($i=$offset$i<$limit$i++)
  241.         {
  242.             $event $arrEvents[$i];
  243.             $blnIsLastEvent false;
  244.             // Last event on the current day
  245.             if (($i+1) == $limit || !isset($arrEvents[($i+1)]['firstDate']) || $event['firstDate'] != $arrEvents[($i+1)]['firstDate'])
  246.             {
  247.                 $blnIsLastEvent true;
  248.             }
  249.             $objTemplate = new FrontendTemplate($this->cal_template ?: 'event_list');
  250.             $objTemplate->setData($event);
  251.             // Month header
  252.             if ($strMonth != $event['month'])
  253.             {
  254.                 $objTemplate->newMonth true;
  255.                 $strMonth $event['month'];
  256.             }
  257.             // Day header
  258.             if ($strDate != $event['firstDate'])
  259.             {
  260.                 $headerCount 0;
  261.                 $objTemplate->header true;
  262.                 $objTemplate->classHeader = ((($dayCount 2) == 0) ? ' even' ' odd') . (($dayCount == 0) ? ' first' '') . (($event['firstDate'] == $arrEvents[($limit-1)]['firstDate']) ? ' last' '');
  263.                 $strDate $event['firstDate'];
  264.                 ++$dayCount;
  265.             }
  266.             // Show the teaser text of redirect events (see #6315)
  267.             if (\is_bool($event['details']) && $event['source'] == 'default')
  268.             {
  269.                 $objTemplate->hasDetails false;
  270.             }
  271.             $objTemplate->hasReader $event['source'] == 'default';
  272.             // Add the template variables
  273.             $objTemplate->classList $event['class'] . ((($headerCount 2) == 0) ? ' even' ' odd') . (($headerCount == 0) ? ' first' '') . ($blnIsLastEvent ' last' '') . ' cal_' $event['parent'];
  274.             $objTemplate->classUpcoming $event['class'] . ((($eventCount 2) == 0) ? ' even' ' odd') . (($eventCount == 0) ? ' first' '') . ((($offset $eventCount 1) >= $limit) ? ' last' '') . ' cal_' $event['parent'];
  275.             $objTemplate->readMore StringUtil::specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['readMore'], $event['title']));
  276.             $objTemplate->more $GLOBALS['TL_LANG']['MSC']['more'];
  277.             $objTemplate->locationLabel $GLOBALS['TL_LANG']['MSC']['location'];
  278.             // Short view
  279.             if ($this->cal_noSpan)
  280.             {
  281.                 $objTemplate->day $event['day'];
  282.                 $objTemplate->date $event['date'];
  283.             }
  284.             else
  285.             {
  286.                 $objTemplate->day $event['firstDay'];
  287.                 $objTemplate->date $event['firstDate'];
  288.             }
  289.             $objTemplate->addImage false;
  290.             $objTemplate->addBefore false;
  291.             // Add an image
  292.             if ($event['addImage'])
  293.             {
  294.                 /** @var CalendarEventsModel $eventModel */
  295.                 $eventModel CalendarEventsModel::findByPk($event['id']);
  296.                 $imgSize $eventModel->size ?: null;
  297.                 // Override the default image size
  298.                 if ($this->imgSize)
  299.                 {
  300.                     $size StringUtil::deserialize($this->imgSize);
  301.                     if ($size[0] > || $size[1] > || is_numeric($size[2]) || ($size[2][0] ?? null) === '_')
  302.                     {
  303.                         $imgSize $this->imgSize;
  304.                     }
  305.                 }
  306.                 $figureBuilder System::getContainer()->get(Studio::class)->createFigureBuilder();
  307.                 $figure $figureBuilder
  308.                     ->from($event['singleSRC'])
  309.                     ->setSize($imgSize)
  310.                     ->setMetadata($eventModel->getOverwriteMetadata())
  311.                     ->enableLightbox((bool) $eventModel->fullsize)
  312.                     ->buildIfResourceExists();
  313.                 if (null !== $figure)
  314.                 {
  315.                     // Rebuild with link to event if none is set
  316.                     if (!$figure->getLinkHref())
  317.                     {
  318.                         $figure $figureBuilder
  319.                             ->setLinkHref($event['href'])
  320.                             ->setLinkAttribute('title'$objTemplate->readMore)
  321.                             ->build();
  322.                     }
  323.                     $figure->applyLegacyTemplateData($objTemplate$eventModel->imagemargin$eventModel->floating);
  324.                 }
  325.             }
  326.             $objTemplate->enclosure = array();
  327.             // Add enclosure
  328.             if ($event['addEnclosure'])
  329.             {
  330.                 $this->addEnclosuresToTemplate($objTemplate$event);
  331.             }
  332.             // schema.org information
  333.             $objTemplate->getSchemaOrgData = static function () use ($objTemplate$event): array
  334.             {
  335.                 $jsonLd Events::getSchemaOrgData((new CalendarEventsModel())->setRow($event));
  336.                 if ($objTemplate->addImage && $objTemplate->figure)
  337.                 {
  338.                     $jsonLd['image'] = $objTemplate->figure->getSchemaOrgData();
  339.                 }
  340.                 return $jsonLd;
  341.             };
  342.             $strEvents .= $objTemplate->parse();
  343.             ++$eventCount;
  344.             ++$headerCount;
  345.         }
  346.         // No events found
  347.         if (!$strEvents)
  348.         {
  349.             $strEvents "\n" '<div class="empty">' $strEmpty '</div>' "\n";
  350.         }
  351.         // See #3672
  352.         $this->Template->headline $this->headline;
  353.         $this->Template->events $strEvents;
  354.         $this->Template->eventCount $eventCount;
  355.         // Clear the $_GET array (see #2445)
  356.         if ($blnClearInput)
  357.         {
  358.             Input::setGet('year'null);
  359.             Input::setGet('month'null);
  360.             Input::setGet('day'null);
  361.         }
  362.     }
  363. }
  364. class_alias(ModuleEventlist::class, 'ModuleEventlist');