vendor/contao/news-bundle/src/Resources/contao/modules/ModuleNews.php line 81

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\Model\Collection;
  11. /**
  12.  * Parent class for news modules.
  13.  *
  14.  * @property string $news_template
  15.  * @property mixed  $news_metaFields
  16.  *
  17.  * @author Leo Feyer <https://github.com/leofeyer>
  18.  */
  19. abstract class ModuleNews extends Module
  20. {
  21.     /**
  22.      * Sort out protected archives
  23.      *
  24.      * @param array $arrArchives
  25.      *
  26.      * @return array
  27.      */
  28.     protected function sortOutProtected($arrArchives)
  29.     {
  30.         if (empty($arrArchives) || !\is_array($arrArchives))
  31.         {
  32.             return $arrArchives;
  33.         }
  34.         $this->import(FrontendUser::class, 'User');
  35.         $objArchive NewsArchiveModel::findMultipleByIds($arrArchives);
  36.         $arrArchives = array();
  37.         if ($objArchive !== null)
  38.         {
  39.             $blnFeUserLoggedIn System::getContainer()->get('contao.security.token_checker')->hasFrontendUser();
  40.             while ($objArchive->next())
  41.             {
  42.                 if ($objArchive->protected)
  43.                 {
  44.                     if (!$blnFeUserLoggedIn || !\is_array($this->User->groups))
  45.                     {
  46.                         continue;
  47.                     }
  48.                     $groups StringUtil::deserialize($objArchive->groups);
  49.                     if (empty($groups) || !\is_array($groups) || !\count(array_intersect($groups$this->User->groups)))
  50.                     {
  51.                         continue;
  52.                     }
  53.                 }
  54.                 $arrArchives[] = $objArchive->id;
  55.             }
  56.         }
  57.         return $arrArchives;
  58.     }
  59.     /**
  60.      * Parse an item and return it as string
  61.      *
  62.      * @param NewsModel $objArticle
  63.      * @param boolean   $blnAddArchive
  64.      * @param string    $strClass
  65.      * @param integer   $intCount
  66.      *
  67.      * @return string
  68.      */
  69.     protected function parseArticle($objArticle$blnAddArchive=false$strClass=''$intCount=0)
  70.     {
  71.         $objTemplate = new FrontendTemplate($this->news_template ?: 'news_latest');
  72.         $objTemplate->setData($objArticle->row());
  73.         if ($objArticle->cssClass)
  74.         {
  75.             $strClass ' ' $objArticle->cssClass $strClass;
  76.         }
  77.         if ($objArticle->featured)
  78.         {
  79.             $strClass ' featured' $strClass;
  80.         }
  81.         $objTemplate->class $strClass;
  82.         $objTemplate->newsHeadline $objArticle->headline;
  83.         $objTemplate->subHeadline $objArticle->subheadline;
  84.         $objTemplate->hasSubHeadline $objArticle->subheadline true false;
  85.         $objTemplate->linkHeadline $this->generateLink($objArticle->headline$objArticle$blnAddArchive);
  86.         $objTemplate->more $this->generateLink($GLOBALS['TL_LANG']['MSC']['more'], $objArticle$blnAddArchivetrue);
  87.         $objTemplate->link News::generateNewsUrl($objArticle$blnAddArchive);
  88.         $objTemplate->archive $objArticle->getRelated('pid');
  89.         $objTemplate->count $intCount// see #5708
  90.         $objTemplate->text '';
  91.         $objTemplate->hasText false;
  92.         $objTemplate->hasTeaser false;
  93.         // Clean the RTE output
  94.         if ($objArticle->teaser)
  95.         {
  96.             $objTemplate->hasTeaser true;
  97.             $objTemplate->teaser StringUtil::toHtml5($objArticle->teaser);
  98.             $objTemplate->teaser StringUtil::encodeEmail($objTemplate->teaser);
  99.         }
  100.         // Display the "read more" button for external/article links
  101.         if ($objArticle->source != 'default')
  102.         {
  103.             $objTemplate->text true;
  104.             $objTemplate->hasText true;
  105.         }
  106.         // Compile the news text
  107.         else
  108.         {
  109.             $id $objArticle->id;
  110.             $objTemplate->text = function () use ($id)
  111.             {
  112.                 $strText '';
  113.                 $objElement ContentModel::findPublishedByPidAndTable($id'tl_news');
  114.                 if ($objElement !== null)
  115.                 {
  116.                     while ($objElement->next())
  117.                     {
  118.                         $strText .= $this->getContentElement($objElement->current());
  119.                     }
  120.                 }
  121.                 return $strText;
  122.             };
  123.             $objTemplate->hasText = static function () use ($objArticle)
  124.             {
  125.                 return ContentModel::countPublishedByPidAndTable($objArticle->id'tl_news') > 0;
  126.             };
  127.         }
  128.         $arrMeta $this->getMetaFields($objArticle);
  129.         // Add the meta information
  130.         $objTemplate->date $arrMeta['date'];
  131.         $objTemplate->hasMetaFields = !empty($arrMeta);
  132.         $objTemplate->numberOfComments $arrMeta['ccount'];
  133.         $objTemplate->commentCount $arrMeta['comments'];
  134.         $objTemplate->timestamp $objArticle->date;
  135.         $objTemplate->author $arrMeta['author'];
  136.         $objTemplate->datetime date('Y-m-d\TH:i:sP'$objArticle->date);
  137.         $objTemplate->addImage false;
  138.         // Add an image
  139.         if ($objArticle->addImage && $objArticle->singleSRC)
  140.         {
  141.             $objModel FilesModel::findByUuid($objArticle->singleSRC);
  142.             if ($objModel !== null && is_file(System::getContainer()->getParameter('kernel.project_dir') . '/' $objModel->path))
  143.             {
  144.                 // Do not override the field now that we have a model registry (see #6303)
  145.                 $arrArticle $objArticle->row();
  146.                 // Override the default image size
  147.                 if ($this->imgSize)
  148.                 {
  149.                     $size StringUtil::deserialize($this->imgSize);
  150.                     if ($size[0] > || $size[1] > || is_numeric($size[2]) || ($size[2][0] ?? null) === '_')
  151.                     {
  152.                         $arrArticle['size'] = $this->imgSize;
  153.                     }
  154.                 }
  155.                 $arrArticle['singleSRC'] = $objModel->path;
  156.                 $this->addImageToTemplate($objTemplate$arrArticlenullnull$objModel);
  157.                 // Link to the news article if no image link has been defined (see #30)
  158.                 if (!$objTemplate->fullsize && !$objTemplate->imageUrl)
  159.                 {
  160.                     // Unset the image title attribute
  161.                     $picture $objTemplate->picture;
  162.                     unset($picture['title']);
  163.                     $objTemplate->picture $picture;
  164.                     // Link to the news article
  165.                     $objTemplate->href $objTemplate->link;
  166.                     $objTemplate->linkTitle StringUtil::specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['readMore'], $objArticle->headline), true);
  167.                     // If the external link is opened in a new window, open the image link in a new window, too (see #210)
  168.                     if ($objTemplate->source == 'external' && $objTemplate->target && strpos($objTemplate->attributes'target="_blank"') === false)
  169.                     {
  170.                         $objTemplate->attributes .= ' target="_blank"';
  171.                     }
  172.                 }
  173.             }
  174.         }
  175.         $objTemplate->enclosure = array();
  176.         // Add enclosures
  177.         if ($objArticle->addEnclosure)
  178.         {
  179.             $this->addEnclosuresToTemplate($objTemplate$objArticle->row());
  180.         }
  181.         // HOOK: add custom logic
  182.         if (isset($GLOBALS['TL_HOOKS']['parseArticles']) && \is_array($GLOBALS['TL_HOOKS']['parseArticles']))
  183.         {
  184.             foreach ($GLOBALS['TL_HOOKS']['parseArticles'] as $callback)
  185.             {
  186.                 $this->import($callback[0]);
  187.                 $this->{$callback[0]}->{$callback[1]}($objTemplate$objArticle->row(), $this);
  188.             }
  189.         }
  190.         // Tag the news (see #2137)
  191.         if (System::getContainer()->has('fos_http_cache.http.symfony_response_tagger'))
  192.         {
  193.             $responseTagger System::getContainer()->get('fos_http_cache.http.symfony_response_tagger');
  194.             $responseTagger->addTags(array('contao.db.tl_news.' $objArticle->id));
  195.         }
  196.         return $objTemplate->parse();
  197.     }
  198.     /**
  199.      * Parse one or more items and return them as array
  200.      *
  201.      * @param Collection $objArticles
  202.      * @param boolean    $blnAddArchive
  203.      *
  204.      * @return array
  205.      */
  206.     protected function parseArticles($objArticles$blnAddArchive=false)
  207.     {
  208.         $limit $objArticles->count();
  209.         if ($limit 1)
  210.         {
  211.             return array();
  212.         }
  213.         $count 0;
  214.         $arrArticles = array();
  215.         $uuids = array();
  216.         foreach ($objArticles as $objArticle)
  217.         {
  218.             if ($objArticle->addImage && $objArticle->singleSRC)
  219.             {
  220.                 $uuids[] = $objArticle->singleSRC;
  221.             }
  222.         }
  223.         // Preload all images in one query so they are loaded into the model registry
  224.         FilesModel::findMultipleByUuids($uuids);
  225.         foreach ($objArticles as $objArticle)
  226.         {
  227.             $arrArticles[] = $this->parseArticle($objArticle$blnAddArchive, ((++$count == 1) ? ' first' '') . (($count == $limit) ? ' last' '') . ((($count 2) == 0) ? ' odd' ' even'), $count);
  228.         }
  229.         return $arrArticles;
  230.     }
  231.     /**
  232.      * Return the meta fields of a news article as array
  233.      *
  234.      * @param NewsModel $objArticle
  235.      *
  236.      * @return array
  237.      */
  238.     protected function getMetaFields($objArticle)
  239.     {
  240.         $meta StringUtil::deserialize($this->news_metaFields);
  241.         if (!\is_array($meta))
  242.         {
  243.             return array();
  244.         }
  245.         /** @var PageModel $objPage */
  246.         global $objPage;
  247.         $return = array();
  248.         foreach ($meta as $field)
  249.         {
  250.             switch ($field)
  251.             {
  252.                 case 'date':
  253.                     $return['date'] = Date::parse($objPage->datimFormat$objArticle->date);
  254.                     break;
  255.                 case 'author':
  256.                     /** @var UserModel $objAuthor */
  257.                     if (($objAuthor $objArticle->getRelated('author')) instanceof UserModel)
  258.                     {
  259.                         $return['author'] = $GLOBALS['TL_LANG']['MSC']['by'] . ' <span itemprop="author">' $objAuthor->name '</span>';
  260.                     }
  261.                     break;
  262.                 case 'comments':
  263.                     if ($objArticle->noComments || $objArticle->source != 'default')
  264.                     {
  265.                         break;
  266.                     }
  267.                     $bundles System::getContainer()->getParameter('kernel.bundles');
  268.                     if (!isset($bundles['ContaoCommentsBundle']))
  269.                     {
  270.                         break;
  271.                     }
  272.                     $intTotal CommentsModel::countPublishedBySourceAndParent('tl_news'$objArticle->id);
  273.                     $return['ccount'] = $intTotal;
  274.                     $return['comments'] = sprintf($GLOBALS['TL_LANG']['MSC']['commentCount'], $intTotal);
  275.                     break;
  276.             }
  277.         }
  278.         return $return;
  279.     }
  280.     /**
  281.      * Generate a URL and return it as string
  282.      *
  283.      * @param NewsModel $objItem
  284.      * @param boolean   $blnAddArchive
  285.      *
  286.      * @return string
  287.      *
  288.      * @deprecated Deprecated since Contao 4.1, to be removed in Contao 5.
  289.      *             Use News::generateNewsUrl() instead.
  290.      */
  291.     protected function generateNewsUrl($objItem$blnAddArchive=false)
  292.     {
  293.         @trigger_error('Using ModuleNews::generateNewsUrl() has been deprecated and will no longer work in Contao 5.0. Use News::generateNewsUrl() instead.'E_USER_DEPRECATED);
  294.         return News::generateNewsUrl($objItem$blnAddArchive);
  295.     }
  296.     /**
  297.      * Generate a link and return it as string
  298.      *
  299.      * @param string    $strLink
  300.      * @param NewsModel $objArticle
  301.      * @param boolean   $blnAddArchive
  302.      * @param boolean   $blnIsReadMore
  303.      *
  304.      * @return string
  305.      */
  306.     protected function generateLink($strLink$objArticle$blnAddArchive=false$blnIsReadMore=false)
  307.     {
  308.         $blnIsInternal $objArticle->source != 'external';
  309.         $strReadMore $blnIsInternal $GLOBALS['TL_LANG']['MSC']['readMore'] : $GLOBALS['TL_LANG']['MSC']['open'];
  310.         $strArticleUrl News::generateNewsUrl($objArticle$blnAddArchive);
  311.         return sprintf(
  312.             '<a href="%s" title="%s"%s itemprop="url">%s%s</a>',
  313.             $strArticleUrl,
  314.             StringUtil::specialchars(sprintf($strReadMore$blnIsInternal $objArticle->headline $strArticleUrl), true),
  315.             ($objArticle->target && !$blnIsInternal ' target="_blank" rel="noreferrer noopener"' ''),
  316.             ($blnIsReadMore $strLink '<span itemprop="headline">' $strLink '</span>'),
  317.             ($blnIsReadMore && $blnIsInternal '<span class="invisible"> ' $objArticle->headline '</span>' '')
  318.         );
  319.     }
  320. }
  321. class_alias(ModuleNews::class, 'ModuleNews');