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

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