vendor/contao/calendar-bundle/src/Resources/contao/classes/Events.php line 97

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\Security\ContaoCorePermissions;
  11. /**
  12.  * Provide methods to get all events of a certain period from the database.
  13.  *
  14.  * @property bool $cal_noSpan
  15.  */
  16. abstract class Events extends Module
  17. {
  18.     /**
  19.      * Current URL
  20.      * @var string
  21.      */
  22.     protected $strUrl;
  23.     /**
  24.      * Today 00:00:00
  25.      * @var string
  26.      */
  27.     protected $intTodayBegin;
  28.     /**
  29.      * Today 23:59:59
  30.      * @var string
  31.      */
  32.     protected $intTodayEnd;
  33.     /**
  34.      * Current events
  35.      * @var array
  36.      */
  37.     protected $arrEvents = array();
  38.     /**
  39.      * URL cache array
  40.      * @var array
  41.      */
  42.     private static $arrUrlCache = array();
  43.     /**
  44.      * Sort out protected archives
  45.      *
  46.      * @param array $arrCalendars
  47.      *
  48.      * @return array
  49.      */
  50.     protected function sortOutProtected($arrCalendars)
  51.     {
  52.         if (empty($arrCalendars) || !\is_array($arrCalendars))
  53.         {
  54.             return $arrCalendars;
  55.         }
  56.         $objCalendar CalendarModel::findMultipleByIds($arrCalendars);
  57.         $arrCalendars = array();
  58.         if ($objCalendar !== null)
  59.         {
  60.             $security System::getContainer()->get('security.helper');
  61.             while ($objCalendar->next())
  62.             {
  63.                 if ($objCalendar->protected && !$security->isGranted(ContaoCorePermissions::MEMBER_IN_GROUPSStringUtil::deserialize($objCalendar->groupstrue)))
  64.                 {
  65.                     continue;
  66.                 }
  67.                 $arrCalendars[] = $objCalendar->id;
  68.             }
  69.         }
  70.         return $arrCalendars;
  71.     }
  72.     /**
  73.      * Get all events of a certain period
  74.      *
  75.      * @param array   $arrCalendars
  76.      * @param integer $intStart
  77.      * @param integer $intEnd
  78.      * @param boolean $blnFeatured
  79.      *
  80.      * @return array
  81.      */
  82.     protected function getAllEvents($arrCalendars$intStart$intEnd$blnFeatured null)
  83.     {
  84.         if (!\is_array($arrCalendars))
  85.         {
  86.             return array();
  87.         }
  88.         $this->arrEvents = array();
  89.         foreach ($arrCalendars as $id)
  90.         {
  91.             // Get the events of the current period
  92.             $objEvents CalendarEventsModel::findCurrentByPid($id$intStart$intEnd, array('showFeatured' => $blnFeatured));
  93.             if ($objEvents === null)
  94.             {
  95.                 continue;
  96.             }
  97.             while ($objEvents->next())
  98.             {
  99.                 $this->addEvent($objEvents$objEvents->startTime$objEvents->endTime$intStart$intEnd$id);
  100.                 // Recurring events
  101.                 if ($objEvents->recurring)
  102.                 {
  103.                     $arrRepeat StringUtil::deserialize($objEvents->repeatEach);
  104.                     if (!isset($arrRepeat['unit'], $arrRepeat['value']) || $arrRepeat['value'] < 1)
  105.                     {
  106.                         continue;
  107.                     }
  108.                     $count 0;
  109.                     $intStartTime $objEvents->startTime;
  110.                     $intEndTime $objEvents->endTime;
  111.                     $strtotime '+ ' $arrRepeat['value'] . ' ' $arrRepeat['unit'];
  112.                     while ($intEndTime $intEnd)
  113.                     {
  114.                         if ($objEvents->recurrences && $count++ >= $objEvents->recurrences)
  115.                         {
  116.                             break;
  117.                         }
  118.                         $intStartTime strtotime($strtotime$intStartTime);
  119.                         $intEndTime strtotime($strtotime$intEndTime);
  120.                         // Stop if the upper boundary is reached (see #8445)
  121.                         if ($intStartTime === false || $intEndTime === false)
  122.                         {
  123.                             break;
  124.                         }
  125.                         // Skip events outside the scope
  126.                         if ($intEndTime $intStart || $intStartTime $intEnd)
  127.                         {
  128.                             continue;
  129.                         }
  130.                         $this->addEvent($objEvents$intStartTime$intEndTime$intStart$intEnd$id);
  131.                     }
  132.                 }
  133.             }
  134.         }
  135.         // Sort the array
  136.         foreach (array_keys($this->arrEvents) as $key)
  137.         {
  138.             ksort($this->arrEvents[$key]);
  139.         }
  140.         // HOOK: modify the result set
  141.         if (isset($GLOBALS['TL_HOOKS']['getAllEvents']) && \is_array($GLOBALS['TL_HOOKS']['getAllEvents']))
  142.         {
  143.             foreach ($GLOBALS['TL_HOOKS']['getAllEvents'] as $callback)
  144.             {
  145.                 $this->import($callback[0]);
  146.                 $this->arrEvents $this->{$callback[0]}->{$callback[1]}($this->arrEvents$arrCalendars$intStart$intEnd$this);
  147.             }
  148.         }
  149.         return $this->arrEvents;
  150.     }
  151.     /**
  152.      * Add an event to the array of active events
  153.      *
  154.      * @param CalendarEventsModel $objEvents
  155.      * @param integer             $intStart
  156.      * @param integer             $intEnd
  157.      * @param integer             $intBegin
  158.      * @param integer             $intLimit
  159.      * @param integer             $intCalendar
  160.      */
  161.     protected function addEvent($objEvents$intStart$intEnd$intBegin$intLimit$intCalendar)
  162.     {
  163.         /** @var PageModel $objPage */
  164.         global $objPage;
  165.         // Backwards compatibility (4th argument was $strUrl)
  166.         if (\func_num_args() > 6)
  167.         {
  168.             trigger_deprecation('contao/calendar-bundle''4.0''Calling "Contao\Events::addEvent()" with 7 arguments has been deprecated and will no longer work in Contao 5.0. Do not pass $strUrl as 4th argument anymore.');
  169.             $intLimit func_get_arg(5);
  170.             $intCalendar func_get_arg(6);
  171.         }
  172.         $intDate $intStart;
  173.         $intKey date('Ymd'$intStart);
  174.         $strDate Date::parse($objPage->dateFormat$intStart);
  175.         $strDay $GLOBALS['TL_LANG']['DAYS'][date('w'$intStart)];
  176.         $strMonth $GLOBALS['TL_LANG']['MONTHS'][(date('n'$intStart)-1)];
  177.         $span Calendar::calculateSpan($intStart$intEnd);
  178.         if ($span 0)
  179.         {
  180.             $strDate Date::parse($objPage->dateFormat$intStart) . $GLOBALS['TL_LANG']['MSC']['cal_timeSeparator'] . Date::parse($objPage->dateFormat$intEnd);
  181.             $strDay '';
  182.         }
  183.         $strTime '';
  184.         if ($objEvents->addTime)
  185.         {
  186.             if ($span 0)
  187.             {
  188.                 $strDate Date::parse($objPage->datimFormat$intStart) . $GLOBALS['TL_LANG']['MSC']['cal_timeSeparator'] . Date::parse($objPage->datimFormat$intEnd);
  189.             }
  190.             elseif ($intStart == $intEnd)
  191.             {
  192.                 $strTime Date::parse($objPage->timeFormat$intStart);
  193.             }
  194.             else
  195.             {
  196.                 $strTime Date::parse($objPage->timeFormat$intStart) . $GLOBALS['TL_LANG']['MSC']['cal_timeSeparator'] . Date::parse($objPage->timeFormat$intEnd);
  197.             }
  198.         }
  199.         $until '';
  200.         $recurring '';
  201.         // Recurring event
  202.         if ($objEvents->recurring)
  203.         {
  204.             $arrRange StringUtil::deserialize($objEvents->repeatEach);
  205.             if (isset($arrRange['unit'], $arrRange['value']))
  206.             {
  207.                 if ($arrRange['value'] == 1)
  208.                 {
  209.                     $repeat $GLOBALS['TL_LANG']['MSC']['cal_single_' $arrRange['unit']];
  210.                 }
  211.                 else
  212.                 {
  213.                     $repeat sprintf($GLOBALS['TL_LANG']['MSC']['cal_multiple_' $arrRange['unit']], $arrRange['value']);
  214.                 }
  215.                 if ($objEvents->recurrences 0)
  216.                 {
  217.                     $until ' ' sprintf($GLOBALS['TL_LANG']['MSC']['cal_until'], Date::parse($objPage->dateFormat$objEvents->repeatEnd));
  218.                 }
  219.                 if ($objEvents->recurrences && $intEnd time())
  220.                 {
  221.                     $recurring sprintf($GLOBALS['TL_LANG']['MSC']['cal_repeat_ended'], $repeat$until);
  222.                 }
  223.                 elseif ($objEvents->addTime)
  224.                 {
  225.                     $recurring sprintf($GLOBALS['TL_LANG']['MSC']['cal_repeat'], $repeat$untildate('Y-m-d\TH:i:sP'$intStart), $strDate . ($strTime ' ' $strTime ''));
  226.                 }
  227.                 else
  228.                 {
  229.                     $recurring sprintf($GLOBALS['TL_LANG']['MSC']['cal_repeat'], $repeat$untildate('Y-m-d'$intStart), $strDate);
  230.                 }
  231.             }
  232.         }
  233.         // Tag the event (see #2137)
  234.         if (System::getContainer()->has('fos_http_cache.http.symfony_response_tagger'))
  235.         {
  236.             $responseTagger System::getContainer()->get('fos_http_cache.http.symfony_response_tagger');
  237.             $responseTagger->addTags(array('contao.db.tl_calendar_events.' $objEvents->id));
  238.         }
  239.         // Store raw data
  240.         $arrEvent $objEvents->row();
  241.         // Overwrite some settings
  242.         $arrEvent['date'] = $strDate;
  243.         $arrEvent['time'] = $strTime;
  244.         $arrEvent['datetime'] = $objEvents->addTime date('Y-m-d\TH:i:sP'$intStart) : date('Y-m-d'$intStart);
  245.         $arrEvent['day'] = $strDay;
  246.         $arrEvent['month'] = $strMonth;
  247.         $arrEvent['parent'] = $intCalendar;
  248.         $arrEvent['calendar'] = $objEvents->getRelated('pid');
  249.         $arrEvent['link'] = $objEvents->title;
  250.         $arrEvent['target'] = '';
  251.         $arrEvent['title'] = StringUtil::specialchars($objEvents->titletrue);
  252.         $arrEvent['href'] = $this->generateEventUrl($objEvents);
  253.         $arrEvent['class'] = $objEvents->cssClass ' ' $objEvents->cssClass '';
  254.         $arrEvent['recurring'] = $recurring;
  255.         $arrEvent['until'] = $until;
  256.         $arrEvent['begin'] = $intStart;
  257.         $arrEvent['end'] = $intEnd;
  258.         $arrEvent['details'] = '';
  259.         $arrEvent['hasDetails'] = false;
  260.         $arrEvent['hasTeaser'] = false;
  261.         // Override the link target
  262.         if ($objEvents->source == 'external' && $objEvents->target)
  263.         {
  264.             $arrEvent['target'] = ' target="_blank" rel="noreferrer noopener"';
  265.         }
  266.         // Clean the RTE output
  267.         if ($arrEvent['teaser'])
  268.         {
  269.             $arrEvent['hasTeaser'] = true;
  270.             $arrEvent['teaser'] = StringUtil::encodeEmail($arrEvent['teaser']);
  271.         }
  272.         // Display the "read more" button for external/article links
  273.         if ($objEvents->source != 'default')
  274.         {
  275.             $arrEvent['hasDetails'] = true;
  276.         }
  277.         // Compile the event text
  278.         else
  279.         {
  280.             $id $objEvents->id;
  281.             $arrEvent['details'] = function () use ($id)
  282.             {
  283.                 $strDetails '';
  284.                 $objElement ContentModel::findPublishedByPidAndTable($id'tl_calendar_events');
  285.                 if ($objElement !== null)
  286.                 {
  287.                     while ($objElement->next())
  288.                     {
  289.                         $strDetails .= $this->getContentElement($objElement->current());
  290.                     }
  291.                 }
  292.                 return $strDetails;
  293.             };
  294.             $arrEvent['hasDetails'] = static function () use ($id)
  295.             {
  296.                 return ContentModel::countPublishedByPidAndTable($id'tl_calendar_events') > 0;
  297.             };
  298.         }
  299.         // Get today's start and end timestamp
  300.         if ($this->intTodayBegin === null)
  301.         {
  302.             $this->intTodayBegin strtotime('00:00:00');
  303.         }
  304.         if ($this->intTodayEnd === null)
  305.         {
  306.             $this->intTodayEnd strtotime('23:59:59');
  307.         }
  308.         // Mark past and upcoming events (see #3692)
  309.         if ($intEnd $this->intTodayBegin)
  310.         {
  311.             $arrEvent['class'] .= ' bygone';
  312.         }
  313.         elseif ($intStart $this->intTodayEnd)
  314.         {
  315.             $arrEvent['class'] .= ' upcoming';
  316.         }
  317.         else
  318.         {
  319.             $arrEvent['class'] .= ' current';
  320.         }
  321.         if ($arrEvent['featured'] == 1)
  322.         {
  323.             $arrEvent['class'] .= ' featured';
  324.         }
  325.         $this->arrEvents[$intKey][$intStart][] = $arrEvent;
  326.         // Multi-day event
  327.         for ($i=1$i<=$span$i++)
  328.         {
  329.             // Only show first occurrence
  330.             if ($this->cal_noSpan)
  331.             {
  332.                 break;
  333.             }
  334.             $intDate strtotime('+1 day'$intDate);
  335.             if ($intDate $intLimit)
  336.             {
  337.                 break;
  338.             }
  339.             $this->arrEvents[date('Ymd'$intDate)][$intDate][] = $arrEvent;
  340.         }
  341.     }
  342.     /**
  343.      * Generate a URL and return it as string
  344.      *
  345.      * @param CalendarEventsModel $objEvent
  346.      * @param boolean             $blnAbsolute
  347.      *
  348.      * @return string
  349.      */
  350.     public static function generateEventUrl($objEvent$blnAbsolute=false)
  351.     {
  352.         $strCacheKey 'id_' $objEvent->id . ($blnAbsolute '_absolute' '');
  353.         // Load the URL from cache
  354.         if (isset(self::$arrUrlCache[$strCacheKey]))
  355.         {
  356.             return self::$arrUrlCache[$strCacheKey];
  357.         }
  358.         // Initialize the cache
  359.         self::$arrUrlCache[$strCacheKey] = null;
  360.         switch ($objEvent->source)
  361.         {
  362.             // Link to an external page
  363.             case 'external':
  364.                 if (=== strncmp($objEvent->url'mailto:'7))
  365.                 {
  366.                     self::$arrUrlCache[$strCacheKey] = StringUtil::encodeEmail($objEvent->url);
  367.                 }
  368.                 else
  369.                 {
  370.                     self::$arrUrlCache[$strCacheKey] = StringUtil::ampersand($objEvent->url);
  371.                 }
  372.                 break;
  373.             // Link to an internal page
  374.             case 'internal':
  375.                 if (($objTarget $objEvent->getRelated('jumpTo')) instanceof PageModel)
  376.                 {
  377.                     /** @var PageModel $objTarget */
  378.                     self::$arrUrlCache[$strCacheKey] = StringUtil::ampersand($blnAbsolute $objTarget->getAbsoluteUrl() : $objTarget->getFrontendUrl());
  379.                 }
  380.                 break;
  381.             // Link to an article
  382.             case 'article':
  383.                 if (($objArticle ArticleModel::findByPk($objEvent->articleId)) instanceof ArticleModel && ($objPid $objArticle->getRelated('pid')) instanceof PageModel)
  384.                 {
  385.                     $params '/articles/' . ($objArticle->alias ?: $objArticle->id);
  386.                     /** @var PageModel $objPid */
  387.                     self::$arrUrlCache[$strCacheKey] = StringUtil::ampersand($blnAbsolute $objPid->getAbsoluteUrl($params) : $objPid->getFrontendUrl($params));
  388.                 }
  389.                 break;
  390.         }
  391.         // Link to the default page
  392.         if (self::$arrUrlCache[$strCacheKey] === null)
  393.         {
  394.             $objPage PageModel::findByPk($objEvent->getRelated('pid')->jumpTo);
  395.             if (!$objPage instanceof PageModel)
  396.             {
  397.                 self::$arrUrlCache[$strCacheKey] = StringUtil::ampersand(Environment::get('request'));
  398.             }
  399.             else
  400.             {
  401.                 $params = (Config::get('useAutoItem') ? '/' '/events/') . ($objEvent->alias ?: $objEvent->id);
  402.                 self::$arrUrlCache[$strCacheKey] = StringUtil::ampersand($blnAbsolute $objPage->getAbsoluteUrl($params) : $objPage->getFrontendUrl($params));
  403.             }
  404.         }
  405.         return self::$arrUrlCache[$strCacheKey];
  406.     }
  407.     /**
  408.      * Return the schema.org data from an event
  409.      *
  410.      * @param CalendarEventsModel $objEvent
  411.      *
  412.      * @return array
  413.      */
  414.     public static function getSchemaOrgData(CalendarEventsModel $objEvent): array
  415.     {
  416.         $htmlDecoder System::getContainer()->get('contao.string.html_decoder');
  417.         $jsonLd = array(
  418.             '@type' => 'Event',
  419.             'identifier' => '#/schema/events/' $objEvent->id,
  420.             'name' => $htmlDecoder->inputEncodedToPlainText($objEvent->title),
  421.             'url' => self::generateEventUrl($objEvent),
  422.             'startDate' => $objEvent->addTime date('Y-m-d\TH:i:sP'$objEvent->startTime) : date('Y-m-d'$objEvent->startTime)
  423.         );
  424.         if ($objEvent->teaser)
  425.         {
  426.             $jsonLd['description'] = $objEvent->teaser;
  427.         }
  428.         if ($objEvent->location)
  429.         {
  430.             $jsonLd['location'] = array(
  431.                 '@type' => 'Place',
  432.                 'name' => $htmlDecoder->inputEncodedToPlainText($objEvent->location)
  433.             );
  434.             if ($objEvent->address)
  435.             {
  436.                 $jsonLd['location']['address'] = array(
  437.                     '@type' => 'PostalAddress',
  438.                     'description' => $htmlDecoder->inputEncodedToPlainText($objEvent->address)
  439.                 );
  440.             }
  441.         }
  442.         return $jsonLd;
  443.     }
  444.     /**
  445.      * Return the beginning and end timestamp and an error message as array
  446.      *
  447.      * @param Date   $objDate
  448.      * @param string $strFormat
  449.      *
  450.      * @return array
  451.      */
  452.     protected function getDatesFromFormat(Date $objDate$strFormat)
  453.     {
  454.         switch ($strFormat)
  455.         {
  456.             case 'cal_day':
  457.                 return array($objDate->dayBegin$objDate->dayEnd$GLOBALS['TL_LANG']['MSC']['cal_emptyDay']);
  458.             default:
  459.             case 'cal_month':
  460.                 return array($objDate->monthBegin$objDate->monthEnd$GLOBALS['TL_LANG']['MSC']['cal_emptyMonth']);
  461.             case 'cal_year':
  462.                 return array($objDate->yearBegin$objDate->yearEnd$GLOBALS['TL_LANG']['MSC']['cal_emptyYear']);
  463.             case 'cal_all'// 1970-01-01 00:00:00 - 2106-02-07 07:28:15
  464.                 return array(0min(4294967295PHP_INT_MAX), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  465.             case 'next_7':
  466.                 return array(time(), strtotime('+7 days'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  467.             case 'next_14':
  468.                 return array(time(), strtotime('+14 days'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  469.             case 'next_30':
  470.                 return array(time(), strtotime('+1 month'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  471.             case 'next_90':
  472.                 return array(time(), strtotime('+3 months'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  473.             case 'next_180':
  474.                 return array(time(), strtotime('+6 months'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  475.             case 'next_365':
  476.                 return array(time(), strtotime('+1 year'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  477.             case 'next_two':
  478.                 return array(time(), strtotime('+2 years'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  479.             case 'next_cur_month':
  480.                 return array(time(), strtotime('last day of this month 23:59:59'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  481.             case 'next_cur_year':
  482.                 return array(time(), strtotime('last day of december this year 23:59:59'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  483.             case 'next_next_month':
  484.                 return array(strtotime('first day of next month 00:00:00'), strtotime('last day of next month 23:59:59'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  485.             case 'next_next_year':
  486.                 return array(strtotime('first day of january next year 00:00:00'), strtotime('last day of december next year 23:59:59'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  487.             case 'next_all'// 2106-02-07 07:28:15
  488.                 return array(time(), min(4294967295PHP_INT_MAX), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  489.             case 'past_7':
  490.                 return array(strtotime('-7 days'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  491.             case 'past_14':
  492.                 return array(strtotime('-14 days'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  493.             case 'past_30':
  494.                 return array(strtotime('-1 month'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  495.             case 'past_90':
  496.                 return array(strtotime('-3 months'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  497.             case 'past_180':
  498.                 return array(strtotime('-6 months'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  499.             case 'past_365':
  500.                 return array(strtotime('-1 year'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  501.             case 'past_two':
  502.                 return array(strtotime('-2 years'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  503.             case 'past_cur_month':
  504.                 return array(strtotime('first day of this month 00:00:00'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  505.             case 'past_cur_year':
  506.                 return array(strtotime('first day of january this year 00:00:00'), time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  507.             case 'past_prev_month':
  508.                 return array(strtotime('first day of last month 00:00:00'), strtotime('last day of last month 23:59:59'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  509.             case 'past_prev_year':
  510.                 return array(strtotime('first day of january last year 00:00:00'), strtotime('last day of december last year 23:59:59'), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  511.             case 'past_all'// 1970-01-01 00:00:00
  512.                 return array(0time(), $GLOBALS['TL_LANG']['MSC']['cal_empty']);
  513.         }
  514.     }
  515. }
  516. class_alias(Events::class, 'Events');