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

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\Image\Studio\Studio;
  11. use Contao\CoreBundle\Security\ContaoCorePermissions;
  12. use Contao\Model\Collection;
  13. /**
  14.  * Parent class for news modules.
  15.  *
  16.  * @property string $news_template
  17.  * @property mixed  $news_metaFields
  18.  *
  19.  * @author Leo Feyer <https://github.com/leofeyer>
  20.  */
  21. abstract class ModuleNews extends Module
  22. {
  23.     /**
  24.      * Sort out protected archives
  25.      *
  26.      * @param array $arrArchives
  27.      *
  28.      * @return array
  29.      */
  30.     protected function sortOutProtected($arrArchives)
  31.     {
  32.         if (empty($arrArchives) || !\is_array($arrArchives))
  33.         {
  34.             return $arrArchives;
  35.         }
  36.         $objArchive NewsArchiveModel::findMultipleByIds($arrArchives);
  37.         $arrArchives = array();
  38.         if ($objArchive !== null)
  39.         {
  40.             $security System::getContainer()->get('security.helper');
  41.             while ($objArchive->next())
  42.             {
  43.                 if ($objArchive->protected && !$security->isGranted(ContaoCorePermissions::MEMBER_IN_GROUPSStringUtil::deserialize($objArchive->groupstrue)))
  44.                 {
  45.                     continue;
  46.                 }
  47.                 $arrArchives[] = $objArchive->id;
  48.             }
  49.         }
  50.         return $arrArchives;
  51.     }
  52.     /**
  53.      * Parse an item and return it as string
  54.      *
  55.      * @param NewsModel $objArticle
  56.      * @param boolean   $blnAddArchive
  57.      * @param string    $strClass
  58.      * @param integer   $intCount
  59.      *
  60.      * @return string
  61.      */
  62.     protected function parseArticle($objArticle$blnAddArchive=false$strClass=''$intCount=0)
  63.     {
  64.         $objTemplate = new FrontendTemplate($this->news_template ?: 'news_latest');
  65.         $objTemplate->setData($objArticle->row());
  66.         if ($objArticle->cssClass)
  67.         {
  68.             $strClass ' ' $objArticle->cssClass $strClass;
  69.         }
  70.         if ($objArticle->featured)
  71.         {
  72.             $strClass ' featured' $strClass;
  73.         }
  74.         $objTemplate->class $strClass;
  75.         $objTemplate->newsHeadline $objArticle->headline;
  76.         $objTemplate->subHeadline $objArticle->subheadline;
  77.         $objTemplate->hasSubHeadline $objArticle->subheadline true false;
  78.         $objTemplate->linkHeadline $this->generateLink($objArticle->headline$objArticle$blnAddArchive);
  79.         $objTemplate->more $this->generateLink($GLOBALS['TL_LANG']['MSC']['more'], $objArticle$blnAddArchivetrue);
  80.         $objTemplate->link News::generateNewsUrl($objArticle$blnAddArchive);
  81.         $objTemplate->archive $objArticle->getRelated('pid');
  82.         $objTemplate->count $intCount// see #5708
  83.         $objTemplate->text '';
  84.         $objTemplate->hasText false;
  85.         $objTemplate->hasTeaser false;
  86.         $objTemplate->hasReader true;
  87.         // Clean the RTE output
  88.         if ($objArticle->teaser)
  89.         {
  90.             $objTemplate->hasTeaser true;
  91.             $objTemplate->teaser StringUtil::toHtml5($objArticle->teaser);
  92.             $objTemplate->teaser StringUtil::encodeEmail($objTemplate->teaser);
  93.         }
  94.         // Display the "read more" button for external/article links
  95.         if ($objArticle->source != 'default')
  96.         {
  97.             $objTemplate->text true;
  98.             $objTemplate->hasText true;
  99.             $objTemplate->hasReader false;
  100.         }
  101.         // Compile the news text
  102.         else
  103.         {
  104.             $id $objArticle->id;
  105.             $objTemplate->text = function () use ($id)
  106.             {
  107.                 $strText '';
  108.                 $objElement ContentModel::findPublishedByPidAndTable($id'tl_news');
  109.                 if ($objElement !== null)
  110.                 {
  111.                     while ($objElement->next())
  112.                     {
  113.                         $strText .= $this->getContentElement($objElement->current());
  114.                     }
  115.                 }
  116.                 return $strText;
  117.             };
  118.             $objTemplate->hasText = static function () use ($objArticle)
  119.             {
  120.                 return ContentModel::countPublishedByPidAndTable($objArticle->id'tl_news') > 0;
  121.             };
  122.         }
  123.         $arrMeta $this->getMetaFields($objArticle);
  124.         // Add the meta information
  125.         $objTemplate->date $arrMeta['date'] ?? null;
  126.         $objTemplate->hasMetaFields = !empty($arrMeta);
  127.         $objTemplate->numberOfComments $arrMeta['ccount'] ?? null;
  128.         $objTemplate->commentCount $arrMeta['comments'] ?? null;
  129.         $objTemplate->timestamp $objArticle->date;
  130.         $objTemplate->author $arrMeta['author'] ?? null;
  131.         $objTemplate->datetime date('Y-m-d\TH:i:sP'$objArticle->date);
  132.         $objTemplate->addImage false;
  133.         $objTemplate->addBefore false;
  134.         // Add an image
  135.         if ($objArticle->addImage)
  136.         {
  137.             $imgSize $objArticle->size ?: null;
  138.             // Override the default image size
  139.             if ($this->imgSize)
  140.             {
  141.                 $size StringUtil::deserialize($this->imgSize);
  142.                 if ($size[0] > || $size[1] > || is_numeric($size[2]) || ($size[2][0] ?? null) === '_')
  143.                 {
  144.                     $imgSize $this->imgSize;
  145.                 }
  146.             }
  147.             $figureBuilder System::getContainer()
  148.                 ->get(Studio::class)
  149.                 ->createFigureBuilder()
  150.                 ->from($objArticle->singleSRC)
  151.                 ->setSize($imgSize)
  152.                 ->setMetadata($objArticle->getOverwriteMetadata())
  153.                 ->enableLightbox((bool) $objArticle->fullsize);
  154.             // If the external link is opened in a new window, open the image link in a new window as well (see #210)
  155.             if ('external' === $objTemplate->source && $objTemplate->target)
  156.             {
  157.                 $figureBuilder->setLinkAttribute('target''_blank');
  158.             }
  159.             if (null !== ($figure $figureBuilder->buildIfResourceExists()))
  160.             {
  161.                 // Rebuild with link to news article if none is set
  162.                 if (!$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');