vendor/contao-community-alliance/events-contao-bindings/src/Subscribers/NewsSubscriber.php line 101

Open in your IDE?
  1. <?php
  2. /**
  3.  * This file is part of contao-community-alliance/events-contao-bindings
  4.  *
  5.  * (c) 2014-2018 The Contao Community Alliance
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  *
  10.  * This project is provided in good faith and hope to be usable by anyone.
  11.  *
  12.  * @package    ContaoCommunityAlliance\Contao\Bindings
  13.  * @subpackage Subscribers
  14.  * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
  15.  * @author     Tristan Lins <tristan.lins@bit3.de>
  16.  * @author     Sven Baumann <baumann.sv@gmail.com>
  17.  * @copyright  2018 The Contao Community Alliance.
  18.  * @license    https://github.com/contao-community-alliance/events-contao-bindings/blob/master/LICENSE LGPL-3.0
  19.  * @filesource
  20.  */
  21. namespace ContaoCommunityAlliance\Contao\Bindings\Subscribers;
  22. use Contao\ArticleModel;
  23. use Contao\CommentsModel;
  24. use Contao\ContentModel;
  25. use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
  26. use Contao\Date;
  27. use Contao\Environment;
  28. use Contao\FilesModel;
  29. use Contao\FrontendTemplate;
  30. use Contao\Input;
  31. use Contao\NewsArchiveModel;
  32. use Contao\NewsModel;
  33. use Contao\PageModel;
  34. use Contao\StringUtil;
  35. use Contao\Validator;
  36. use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents;
  37. use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\AddEnclosureToTemplateEvent;
  38. use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\AddImageToTemplateEvent;
  39. use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\GenerateFrontendUrlEvent;
  40. use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\GetContentElementEvent;
  41. use ContaoCommunityAlliance\Contao\Bindings\Events\News\GetNewsEvent;
  42. use ContaoCommunityAlliance\Contao\Bindings\Util\StringHelper;
  43. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  44. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  45. /**
  46.  * Subscriber for the news extension.
  47.  */
  48. class NewsSubscriber implements EventSubscriberInterface
  49. {
  50.     /**
  51.      * The contao framework.
  52.      *
  53.      * @var ContaoFrameworkInterface
  54.      */
  55.     protected $framework;
  56.     /**
  57.      * NewsSubscriber constructor.
  58.      *
  59.      * @param ContaoFrameworkInterface $framework The contao framework.
  60.      */
  61.     public function __construct(ContaoFrameworkInterface $framework)
  62.     {
  63.         $this->framework $framework;
  64.     }
  65.     /**
  66.      * Returns an array of event names this subscriber wants to listen to.
  67.      *
  68.      * @return array
  69.      */
  70.     public static function getSubscribedEvents()
  71.     {
  72.         return [
  73.             ContaoEvents::NEWS_GET_NEWS => 'handleNews',
  74.         ];
  75.     }
  76.     /**
  77.      * Render a news.
  78.      *
  79.      * @param GetNewsEvent             $event           The event.
  80.      *
  81.      * @param string                   $eventName       The event name.
  82.      *
  83.      * @param EventDispatcherInterface $eventDispatcher The event dispatcher.
  84.      *
  85.      * @return void
  86.      *
  87.      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  88.      * @SuppressWarnings(PHPMD.NPathComplexity)
  89.      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  90.      * @SuppressWarnings(PHPMD.Superglobals)
  91.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  92.      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  93.      */
  94.     public function handleNews(GetNewsEvent $event$eventNameEventDispatcherInterface $eventDispatcher)
  95.     {
  96.         if ($event->getNewsHtml()) {
  97.             return;
  98.         }
  99.         $newsArchiveModelAdapter $this->framework->getAdapter(NewsArchiveModel::class);
  100.         $newsModelAdapter        $this->framework->getAdapter(NewsModel::class);
  101.         /** @var NewsArchiveModel $newsArchiveModelAdapter */
  102.         /** @var NewsModel $newsModelAdapter */
  103.         $newsArchiveCollection $newsArchiveModelAdapter->findAll();
  104.         $newsArchiveIds        $newsArchiveCollection $newsArchiveCollection->fetchEach('id') : [];
  105.         $newsModel             $newsModelAdapter->findPublishedByParentAndIdOrAlias(
  106.             $event->getNewsId(),
  107.             $newsArchiveIds
  108.         );
  109.         if (!$newsModel) {
  110.             return;
  111.         }
  112.         $newsModel $newsModel->current();
  113.         /** @var FrontendTemplate $objTemplate */
  114.         $objTemplate $this->framework->createInstance(FrontendTemplate::class, $event->getTemplate());
  115.         $objTemplate->setData($newsModel->row());
  116.         $objTemplate->class          = (!empty($newsModel->cssClass) ? ' ' $newsModel->cssClass '');
  117.         $objTemplate->newsHeadline   $newsModel->headline;
  118.         $objTemplate->subHeadline    $newsModel->subheadline;
  119.         $objTemplate->hasSubHeadline $newsModel->subheadline true false;
  120.         $objTemplate->linkHeadline   $this->generateLink($eventDispatcher$newsModel->headline$newsModel);
  121.         $objTemplate->more           $this->generateLink(
  122.             $eventDispatcher,
  123.             $GLOBALS['TL_LANG']['MSC']['more'],
  124.             $newsModel,
  125.             false,
  126.             true
  127.         );
  128.         $objTemplate->link           $this->generateNewsUrl($eventDispatcher$newsModel);
  129.         $objTemplate->archive        $newsModel->getRelated('pid');
  130.         $objTemplate->count          0;
  131.         $objTemplate->text           '';
  132.         if (!empty($newsModel->teaser)) {
  133.             // Clean the RTE output.
  134.             /** @var StringUtil $stringUtilAdapter */
  135.             $stringUtilAdapter $this->framework->getAdapter(StringUtil::class);
  136.             $objTemplate->teaser $stringUtilAdapter->encodeEmail($stringUtilAdapter->toHtml5($newsModel->teaser));
  137.         }
  138.         // Display the "read more" button for external/article links.
  139.         if ($newsModel->source !== 'default') {
  140.             $objTemplate->text true;
  141.         } else {
  142.             /** @var ContentModel $contentModelAdapter */
  143.             $contentModelAdapter $this->framework->getAdapter(ContentModel::class);
  144.             // Compile the news text.
  145.             $objElement $contentModelAdapter->findPublishedByPidAndTable($newsModel->id'tl_news');
  146.             if ($objElement !== null) {
  147.                 while ($objElement->next()) {
  148.                     $getContentElementEvent = new GetContentElementEvent($objElement->id);
  149.                     $eventDispatcher->dispatch(ContaoEvents::CONTROLLER_GET_CONTENT_ELEMENT$getContentElementEvent);
  150.                     $objTemplate->text .= $getContentElementEvent->getContentElementHtml();
  151.                 }
  152.             }
  153.         }
  154.         $arrMeta $this->getMetaFields($newsModel);
  155.         // Add the meta information.
  156.         $objTemplate->date             $arrMeta['date'];
  157.         $objTemplate->hasMetaFields    = !empty($arrMeta);
  158.         $objTemplate->numberOfComments $arrMeta['ccount'];
  159.         $objTemplate->commentCount     $arrMeta['comments'];
  160.         $objTemplate->timestamp        $newsModel->date;
  161.         $objTemplate->author           $arrMeta['author'];
  162.         $objTemplate->datetime         date('Y-m-d\TH:i:sP'$newsModel->date);
  163.         $objTemplate->addImage false;
  164.         // Add an image.
  165.         if ($newsModel->addImage && !empty($newsModel->singleSRC)) {
  166.             /** @var FilesModel $filesModelAdapter */
  167.             $filesModelAdapter $this->framework->getAdapter(FilesModel::class);
  168.             $objModel $filesModelAdapter->findByUuid($newsModel->singleSRC);
  169.             if ($objModel === null) {
  170.                 /** @var Validator $validatorAdapter */
  171.                 $validatorAdapter $this->framework->getAdapter(Validator::class);
  172.                 if (!$validatorAdapter->isUuid($newsModel->singleSRC)) {
  173.                     $objTemplate->text '<p class="error">' $GLOBALS['TL_LANG']['ERR']['version2format'] . '</p>';
  174.                 }
  175.             } elseif (is_file(TL_ROOT '/' $objModel->path)) {
  176.                 // Do not override the field now that we have a model registry (see #6303).
  177.                 $arrArticle $newsModel->row();
  178.                 // Override the default image size.
  179.                 // This is always false!
  180.                 if (!empty($this->imgSize)) {
  181.                     $size deserialize($this->imgSize);
  182.                     if ($size[0] > || $size[1] > 0) {
  183.                         $arrArticle['size'] = $this->imgSize;
  184.                     }
  185.                 }
  186.                 $arrArticle['singleSRC'] = $objModel->path;
  187.                 $addImageToTemplateEvent = new AddImageToTemplateEvent($arrArticle$objTemplate);
  188.                 $eventDispatcher->dispatch(ContaoEvents::CONTROLLER_ADD_IMAGE_TO_TEMPLATE$addImageToTemplateEvent);
  189.             }
  190.         }
  191.         $objTemplate->enclosure = [];
  192.         // Add enclosures.
  193.         if ($newsModel->addEnclosure) {
  194.             $addEnclosureToTemplateEvent = new AddEnclosureToTemplateEvent($newsModel->row(), $objTemplate);
  195.             $eventDispatcher->dispatch(
  196.                 ContaoEvents::CONTROLLER_ADD_ENCLOSURE_TO_TEMPLATE,
  197.                 $addEnclosureToTemplateEvent
  198.             );
  199.         }
  200.         $news $objTemplate->parse();
  201.         $event->setNewsHtml($news);
  202.     }
  203.     /**
  204.      * Return the meta fields of a news article as array.
  205.      *
  206.      * @param NewsModel $objArticle The model.
  207.      *
  208.      * @return array
  209.      *
  210.      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  211.      * @SuppressWarnings(PHPMD.Superglobals)
  212.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  213.      */
  214.     protected function getMetaFields($objArticle)
  215.     {
  216.         $meta deserialize($this->news_metaFields);
  217.         if (!is_array($meta)) {
  218.             return [];
  219.         }
  220.         $return = [];
  221.         foreach ($meta as $field) {
  222.             switch ($field) {
  223.                 case 'date':
  224.                     /** @var Date $dateAdapter */
  225.                     $dateAdapter $this->framework->getAdapter(Date::class);
  226.                     $return['date'] = $dateAdapter->parse($GLOBALS['objPage']->datimFormat$objArticle->date);
  227.                     break;
  228.                 case 'author':
  229.                     if (($objAuthor $objArticle->getRelated('author')) !== null) {
  230.                         if (!empty($objAuthor->google)) {
  231.                             $return['author'] = $GLOBALS['TL_LANG']['MSC']['by'] .
  232.                                 ' <a href="https://plus.google.com/' $objAuthor->google .
  233.                                 '" rel="author" target="_blank">' $objAuthor->name '</a>';
  234.                         } else {
  235.                             $return['author'] = $GLOBALS['TL_LANG']['MSC']['by'] . ' ' $objAuthor->name;
  236.                         }
  237.                     }
  238.                     break;
  239.                 case 'comments':
  240.                     if ($objArticle->noComments || $objArticle->source !== 'default') {
  241.                         break;
  242.                     }
  243.                     /** @var CommentsModel $commentsModelAdapter */
  244.                     $commentsModelAdapter $this->framework->getAdapter(CommentsModel::class);
  245.                     $intTotal           $commentsModelAdapter->countPublishedBySourceAndParent(
  246.                         'tl_news',
  247.                         $objArticle->id
  248.                     );
  249.                     $return['ccount']   = $intTotal;
  250.                     $return['comments'] = sprintf($GLOBALS['TL_LANG']['MSC']['commentCount'], $intTotal);
  251.                     break;
  252.                 default:
  253.             }
  254.         }
  255.         return $return;
  256.     }
  257.     // @codingStandardsIgnoreStart - this is currently too complex but not worth the hassle of refactoring.
  258.     /**
  259.      * Generate a URL and return it as string.
  260.      *
  261.      * @param EventDispatcherInterface $eventDispatcher The event dispatcher.
  262.      *
  263.      * @param NewsModel                $objItem         The news model.
  264.      *
  265.      * @param boolean                  $blnAddArchive   Add the current archive parameter (news archive) (default: false).
  266.      *
  267.      * @return string
  268.      *
  269.      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  270.      * @SuppressWarnings(PHPMD.NPathComplexity)
  271.      * @SuppressWarnings(PHPMD.Superglobals)
  272.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  273.      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  274.      */
  275.     protected function generateNewsUrl(
  276.         EventDispatcherInterface $eventDispatcher,
  277.         NewsModel $objItem,
  278.         $blnAddArchive false
  279.     ) {
  280.         $url null;
  281.         switch ($objItem->source) {
  282.             // Link to an external page.
  283.             case 'external':
  284.                 if (substr($objItem->url07) === 'mailto:') {
  285.                     $url StringHelper::encodeEmail($objItem->url);
  286.                 } else {
  287.                     $url ampersand($objItem->url);
  288.                 }
  289.                 break;
  290.             // Link to an internal page.
  291.             case 'internal':
  292.                 if (($objTarget $objItem->getRelated('jumpTo')) !== null) {
  293.                     $generateFrontendUrlEvent = new GenerateFrontendUrlEvent($objTarget->row());
  294.                     $eventDispatcher->dispatch(
  295.                         ContaoEvents::CONTROLLER_GENERATE_FRONTEND_URL,
  296.                         $generateFrontendUrlEvent
  297.                     );
  298.                     $url $generateFrontendUrlEvent->getUrl();
  299.                 }
  300.                 break;
  301.             // Link to an article.
  302.             case 'article':
  303.                 /** @var ArticleModel $articleModelAdapter */
  304.                 $articleModelAdapter $this->framework->getAdapter(ArticleModel::class);
  305.                 if (($objArticle $articleModelAdapter->findByPk($objItem->articleId, ['eager' => true])) !== null
  306.                     && ($objPid $objArticle->getRelated('pid')) !== null
  307.                 ) {
  308.                     $generateFrontendUrlEvent = new GenerateFrontendUrlEvent(
  309.                         $objPid->row(),
  310.                         '/articles/' .
  311.                         ((!$GLOBALS['TL_CONFIG']['disableAlias'] && !empty($objArticle->alias)) ? $objArticle->alias $objArticle->id)
  312.                     );
  313.                     $eventDispatcher->dispatch(
  314.                         ContaoEvents::CONTROLLER_GENERATE_FRONTEND_URL,
  315.                         $generateFrontendUrlEvent
  316.                     );
  317.                     $url $generateFrontendUrlEvent->getUrl();
  318.                 }
  319.                 break;
  320.             default:
  321.         }
  322.         // Link to the default page.
  323.         if ($url === null) {
  324.             /** @var PageModel $pageModelAdapter */
  325.             $pageModelAdapter $this->framework->getAdapter(PageModel::class);
  326.             $objPage $pageModelAdapter->findByPk($objItem->getRelated('pid')->jumpTo);
  327.             if ($objPage === null) {
  328.                 $url ampersand(Environment::get('request'), true);
  329.             } else {
  330.                 $generateFrontendUrlEvent = new GenerateFrontendUrlEvent(
  331.                     $objPage->row(),
  332.                     (($GLOBALS['TL_CONFIG']['useAutoItem'] && !$GLOBALS['TL_CONFIG']['disableAlias']) ? '/' '/items/') .
  333.                     ((!$GLOBALS['TL_CONFIG']['disableAlias'] && !empty($objItem->alias)) ? $objItem->alias $objItem->id)
  334.                 );
  335.                 $eventDispatcher->dispatch(ContaoEvents::CONTROLLER_GENERATE_FRONTEND_URL$generateFrontendUrlEvent);
  336.                 $url $generateFrontendUrlEvent->getUrl();
  337.             }
  338.             /** @var Input $inputAdapter */
  339.             $inputAdapter $this->framework->getAdapter(Input::class);
  340.             // Add the current archive parameter (news archive).
  341.             if ($blnAddArchive && !empty($inputAdapter->get('month'))) {
  342.                 $url .= ($GLOBALS['TL_CONFIG']['disableAlias'] ? '&amp;' '?') . 'month=' $inputAdapter->get('month');
  343.             }
  344.         }
  345.         return $url;
  346.     }
  347.     // @codingStandardsIgnoreEnd
  348.     /**
  349.      * Generate a link and return it as string.
  350.      *
  351.      * @param EventDispatcherInterface $eventDispatcher The event dispatcher.
  352.      *
  353.      * @param string                   $strLink         The link text.
  354.      *
  355.      * @param NewsModel                $objArticle      The model.
  356.      *
  357.      * @param bool                     $blnAddArchive   Add the current archive parameter (news archive)
  358.      *                                                  (default: false).
  359.      *
  360.      * @param bool                     $blnIsReadMore   Determine if the link is a "read more" link.
  361.      *
  362.      * @return string
  363.      *
  364.      * @SuppressWarnings(PHPMD.Superglobals)
  365.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  366.      */
  367.     protected function generateLink(
  368.         EventDispatcherInterface $eventDispatcher,
  369.         $strLink,
  370.         $objArticle,
  371.         $blnAddArchive false,
  372.         $blnIsReadMore false
  373.     ) {
  374.         // Internal link.
  375.         if ($objArticle->source !== 'external') {
  376.             return sprintf(
  377.                 '<a href="%s" title="%s">%s%s</a>',
  378.                 $this->generateNewsUrl($eventDispatcher$objArticle$blnAddArchive),
  379.                 specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['readMore'], $objArticle->headline), true),
  380.                 $strLink,
  381.                 ($blnIsReadMore ' <span class="invisible">' $objArticle->headline '</span>' '')
  382.             );
  383.         }
  384.         /** @var StringUtil $stringUtilAdapter */
  385.         $stringUtilAdapter $this->framework->getAdapter(StringUtil::class);
  386.         // Encode e-mail addresses.
  387.         if (substr($objArticle->url07) === 'mailto:') {
  388.             $strArticleUrl $stringUtilAdapter->encodeEmail($objArticle->url);
  389.         } else {
  390.         // Ampersand URIs.
  391.             $strArticleUrl ampersand($objArticle->url);
  392.         }
  393.         // External link.
  394.         return sprintf(
  395.             '<a href="%s" title="%s"%s>%s</a>',
  396.             $strArticleUrl,
  397.             specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['open'], $strArticleUrl)),
  398.             $objArticle->target ' target="_blank"' '',
  399.             $strLink
  400.         );
  401.     }
  402. }