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

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->authorModel $arrMeta['authorModel'] ?? null;
  129.         $objTemplate->datetime date('Y-m-d\TH:i:sP'$objArticle->date);
  130.         $objTemplate->addImage false;
  131.         $objTemplate->addBefore false;
  132.         // Add an image
  133.         if ($objArticle->addImage)
  134.         {
  135.             $imgSize $objArticle->size ?: null;
  136.             // Override the default image size
  137.             if ($this->imgSize)
  138.             {
  139.                 $size StringUtil::deserialize($this->imgSize);
  140.                 if ($size[0] > || $size[1] > || is_numeric($size[2]) || ($size[2][0] ?? null) === '_')
  141.                 {
  142.                     $imgSize $this->imgSize;
  143.                 }
  144.             }
  145.             $figureBuilder System::getContainer()
  146.                 ->get('contao.image.studio')
  147.                 ->createFigureBuilder()
  148.                 ->from($objArticle->singleSRC)
  149.                 ->setSize($imgSize)
  150.                 ->setOverwriteMetadata($objArticle->getOverwriteMetadata())
  151.                 ->enableLightbox((bool) $objArticle->fullsize);
  152.             // If the external link is opened in a new window, open the image link in a new window as well (see #210)
  153.             if ('external' === $objTemplate->source && $objTemplate->target)
  154.             {
  155.                 $figureBuilder->setLinkAttribute('target''_blank');
  156.             }
  157.             if (null !== ($figure $figureBuilder->buildIfResourceExists()))
  158.             {
  159.                 // Rebuild with link to the news article if we are not in a
  160.                 // newsreader and there is no link yet. $intCount will only be
  161.                 // set by the news list and news archive modules (see #5851).
  162.                 if ($intCount && !$figure->getLinkHref())
  163.                 {
  164.                     $linkTitle StringUtil::specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['readMore'], $objArticle->headline), true);
  165.                     $figure $figureBuilder
  166.                         ->setLinkHref($objTemplate->link)
  167.                         ->setLinkAttribute('title'$linkTitle)
  168.                         ->build();
  169.                 }
  170.                 $figure->applyLegacyTemplateData($objTemplate$objArticle->imagemargin$objArticle->floating);
  171.             }
  172.         }
  173.         $objTemplate->enclosure = array();
  174.         // Add enclosures
  175.         if ($objArticle->addEnclosure)
  176.         {
  177.             $this->addEnclosuresToTemplate($objTemplate$objArticle->row());
  178.         }
  179.         // HOOK: add custom logic
  180.         if (isset($GLOBALS['TL_HOOKS']['parseArticles']) && \is_array($GLOBALS['TL_HOOKS']['parseArticles']))
  181.         {
  182.             foreach ($GLOBALS['TL_HOOKS']['parseArticles'] as $callback)
  183.             {
  184.                 $this->import($callback[0]);
  185.                 $this->{$callback[0]}->{$callback[1]}($objTemplate$objArticle->row(), $this);
  186.             }
  187.         }
  188.         // Tag the news (see #2137)
  189.         if (System::getContainer()->has('fos_http_cache.http.symfony_response_tagger'))
  190.         {
  191.             $responseTagger System::getContainer()->get('fos_http_cache.http.symfony_response_tagger');
  192.             $responseTagger->addTags(array('contao.db.tl_news.' $objArticle->id));
  193.         }
  194.         // schema.org information
  195.         $objTemplate->getSchemaOrgData = static function () use ($objTemplate$objArticle): array
  196.         {
  197.             $jsonLd News::getSchemaOrgData($objArticle);
  198.             if ($objTemplate->addImage && $objTemplate->figure)
  199.             {
  200.                 $jsonLd['image'] = $objTemplate->figure->getSchemaOrgData();
  201.             }
  202.             return $jsonLd;
  203.         };
  204.         return $objTemplate->parse();
  205.     }
  206.     /**
  207.      * Parse one or more items and return them as array
  208.      *
  209.      * @param Collection $objArticles
  210.      * @param boolean    $blnAddArchive
  211.      *
  212.      * @return array
  213.      */
  214.     protected function parseArticles($objArticles$blnAddArchive=false)
  215.     {
  216.         $limit $objArticles->count();
  217.         if ($limit 1)
  218.         {
  219.             return array();
  220.         }
  221.         $count 0;
  222.         $arrArticles = array();
  223.         $uuids = array();
  224.         foreach ($objArticles as $objArticle)
  225.         {
  226.             if ($objArticle->addImage && $objArticle->singleSRC)
  227.             {
  228.                 $uuids[] = $objArticle->singleSRC;
  229.             }
  230.         }
  231.         // Preload all images in one query, so they are loaded into the model registry
  232.         FilesModel::findMultipleByUuids($uuids);
  233.         foreach ($objArticles as $objArticle)
  234.         {
  235.             $arrArticles[] = $this->parseArticle($objArticle$blnAddArchive, ((++$count == 1) ? ' first' '') . (($count == $limit) ? ' last' '') . ((($count 2) == 0) ? ' odd' ' even'), $count);
  236.         }
  237.         return $arrArticles;
  238.     }
  239.     /**
  240.      * Return the meta fields of a news article as array
  241.      *
  242.      * @param NewsModel $objArticle
  243.      *
  244.      * @return array
  245.      */
  246.     protected function getMetaFields($objArticle)
  247.     {
  248.         $meta StringUtil::deserialize($this->news_metaFields);
  249.         if (!\is_array($meta))
  250.         {
  251.             return array();
  252.         }
  253.         /** @var PageModel $objPage */
  254.         global $objPage;
  255.         $return = array();
  256.         foreach ($meta as $field)
  257.         {
  258.             switch ($field)
  259.             {
  260.                 case 'date':
  261.                     $return['date'] = Date::parse($objPage->datimFormat$objArticle->date);
  262.                     break;
  263.                 case 'author':
  264.                     /** @var UserModel $objAuthor */
  265.                     if (($objAuthor $objArticle->getRelated('author')) instanceof UserModel)
  266.                     {
  267.                         $return['author'] = $GLOBALS['TL_LANG']['MSC']['by'] . ' ' $objAuthor->name;
  268.                         $return['authorModel'] = $objAuthor;
  269.                     }
  270.                     break;
  271.                 case 'comments':
  272.                     if ($objArticle->noComments || $objArticle->source != 'default')
  273.                     {
  274.                         break;
  275.                     }
  276.                     $bundles System::getContainer()->getParameter('kernel.bundles');
  277.                     if (!isset($bundles['ContaoCommentsBundle']))
  278.                     {
  279.                         break;
  280.                     }
  281.                     $intTotal CommentsModel::countPublishedBySourceAndParent('tl_news'$objArticle->id);
  282.                     $return['ccount'] = $intTotal;
  283.                     $return['comments'] = sprintf($GLOBALS['TL_LANG']['MSC']['commentCount'], $intTotal);
  284.                     break;
  285.             }
  286.         }
  287.         return $return;
  288.     }
  289.     /**
  290.      * Generate a URL and return it as string
  291.      *
  292.      * @param NewsModel $objItem
  293.      * @param boolean   $blnAddArchive
  294.      *
  295.      * @return string
  296.      *
  297.      * @deprecated Deprecated since Contao 4.1, to be removed in Contao 5.
  298.      *             Use News::generateNewsUrl() instead.
  299.      */
  300.     protected function generateNewsUrl($objItem$blnAddArchive=false)
  301.     {
  302.         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.');
  303.         return News::generateNewsUrl($objItem$blnAddArchive);
  304.     }
  305.     /**
  306.      * Generate a link and return it as string
  307.      *
  308.      * @param string    $strLink
  309.      * @param NewsModel $objArticle
  310.      * @param boolean   $blnAddArchive
  311.      * @param boolean   $blnIsReadMore
  312.      *
  313.      * @return string
  314.      */
  315.     protected function generateLink($strLink$objArticle$blnAddArchive=false$blnIsReadMore=false)
  316.     {
  317.         $blnIsInternal $objArticle->source != 'external';
  318.         $strReadMore $blnIsInternal $GLOBALS['TL_LANG']['MSC']['readMore'] : $GLOBALS['TL_LANG']['MSC']['open'];
  319.         $strArticleUrl News::generateNewsUrl($objArticle$blnAddArchive);
  320.         return sprintf(
  321.             '<a href="%s" title="%s"%s>%s%s</a>',
  322.             $strArticleUrl,
  323.             StringUtil::specialchars(sprintf($strReadMore$blnIsInternal $objArticle->headline $strArticleUrl), true),
  324.             ($objArticle->target && !$blnIsInternal ' target="_blank" rel="noreferrer noopener"' ''),
  325.             $strLink,
  326.             ($blnIsReadMore && $blnIsInternal '<span class="invisible"> ' $objArticle->headline '</span>' '')
  327.         );
  328.     }
  329. }
  330. class_alias(ModuleNews::class, 'ModuleNews');