vendor/madeyourday/contao-rocksolid-custom-elements/src/Element/CustomElement.php line 51

Open in your IDE?
  1. <?php
  2. /*
  3.  * Copyright MADE/YOUR/DAY OG <mail@madeyourday.net>
  4.  *
  5.  * For the full copyright and license information, please view the LICENSE
  6.  * file that was distributed with this source code.
  7.  */
  8. namespace MadeYourDay\RockSolidCustomElements\Element;
  9. use Contao\ContentElement;
  10. use Contao\ContentModel;
  11. use Contao\Image\PictureConfiguration;
  12. use Contao\Input;
  13. use Contao\ModuleModel;
  14. use Contao\StringUtil;
  15. use Contao\System;
  16. use Contao\Validator;
  17. use MadeYourDay\RockSolidColumns\Element\ColumnsStart;
  18. use MadeYourDay\RockSolidCustomElements\Template\CustomTemplate;
  19. use MadeYourDay\RockSolidCustomElements\CustomElements;
  20. use Symfony\Component\HttpFoundation\Request;
  21. /**
  22.  * Custom content element and frontend module
  23.  *
  24.  * @author Martin Auswöger <martin@madeyourday.net>
  25.  */
  26. class CustomElement extends ContentElement
  27. {
  28.     /**
  29.      * @var string Template
  30.      */
  31.     protected $strTemplate 'rsce_default';
  32.     /**
  33.      * Find the correct template and parse it
  34.      *
  35.      * @return string Parsed template
  36.      */
  37.     public function generate()
  38.     {
  39.         $this->strTemplate $this->customTpl ?: $this->type;
  40.         // Return output for the backend if in BE mode
  41.         if (($output $this->rsceGetBackendOutput()) !== null) {
  42.             return $output;
  43.         }
  44.         try {
  45.             return parent::generate();
  46.         }
  47.         catch (\Exception $exception) {
  48.             if (System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest(System::getContainer()->get('request_stack')->getCurrentRequest() ?? Request::create(''))) {
  49.                 $template = new CustomTemplate($this->strTemplate);
  50.                 $template->setData($this->Template->getData());
  51.                 $this->Template $template;
  52.                 return $this->Template->parse();
  53.             }
  54.             throw $exception;
  55.         }
  56.     }
  57.     /**
  58.      * Generate backend output if TL_MODE is set to BE
  59.      *
  60.      * @return string|null Backend output or null
  61.      */
  62.     public function rsceGetBackendOutput()
  63.     {
  64.         if (!System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest(System::getContainer()->get('request_stack')->getCurrentRequest() ?? Request::create(''))) {
  65.             return null;
  66.         }
  67.         $config CustomElements::getConfigByType($this->type) ?: array();
  68.         // Handle newsletter output the same way as the frontend
  69.         if (!empty($config['isNewsletter'])) {
  70.             if (Input::get('do') === 'newsletter') {
  71.                 return null;
  72.             }
  73.             foreach(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $entry) {
  74.                 $method $entry['class'] . '::' $entry['function'];
  75.                 if (
  76.                     $entry['file'] === System::getContainer()->getParameter('kernel.project_dir') . '/system/modules/newsletter/classes/Newsletter.php'
  77.                     || $entry['file'] === System::getContainer()->getParameter('kernel.project_dir') . '/vendor/contao/newsletter-bundle/src/Resources/contao/classes/Newsletter.php'
  78.                     || $entry['file'] === System::getContainer()->getParameter('kernel.project_dir') . '/vendor/contao/newsletter-bundle/contao/classes/Newsletter.php'
  79.                     || $method === 'Contao\\Newsletter::send'
  80.                     || $method === 'tl_newsletter::listNewsletters'
  81.                 ) {
  82.                     return null;
  83.                 }
  84.             }
  85.         }
  86.         if (!empty($config['beTemplate'])) {
  87.             if (!isset($this->arrData['wildcard'])) {
  88.                 $label CustomElements::getLabelTranslated($config['label']);
  89.                 $this->arrData['wildcard'] = '### ' mb_strtoupper(is_array($label) ? $label[0] : $label) . ' ###';
  90.             }
  91.             if (!isset($this->arrData['title'])) {
  92.                 $this->arrData['title'] = $this->headline;
  93.             }
  94.             if (
  95.                 !isset($this->arrData['link'])
  96.                 && !isset($this->arrData['href'])
  97.                 && $this->objModel instanceof ModuleModel
  98.             ) {
  99.                 $this->arrData['link'] = $this->name;
  100.                 $this->arrData['href'] = StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', ['do' => 'themes''table' => 'tl_module''act' => 'edit''id' => $this->id]));
  101.             }
  102.             $this->strTemplate $config['beTemplate'];
  103.             return null;
  104.         }
  105.         if (
  106.             in_array($this->type$GLOBALS['TL_WRAPPERS']['start'])
  107.             || in_array($this->type$GLOBALS['TL_WRAPPERS']['stop'])
  108.             || in_array($this->type$GLOBALS['TL_WRAPPERS']['separator'])
  109.         ) {
  110.             return '';
  111.         }
  112.         return null;
  113.     }
  114.     /**
  115.      * Parse the json data and pass it to the template
  116.      *
  117.      * @return void
  118.      */
  119.     public function compile()
  120.     {
  121.         // Add an image
  122.         if ($this->addImage && trim($this->singleSRC)) {
  123.             $figure System::getContainer()
  124.                 ->get('contao.image.studio')
  125.                 ->createFigureBuilder()
  126.                 ->from($this->singleSRC)
  127.                 ->setSize(StringUtil::deserialize($this->arrData['size'] ?? null) ?: null)
  128.                 ->enableLightbox((bool) ($this->arrData['fullsize'] ?? false))
  129.                 ->setLightboxSize(StringUtil::deserialize($this->arrData['lightboxSize'] ?? null) ?: null)
  130.                 ->setMetadata((new ContentModel())->setRow($this->arrData)->getOverwriteMetadata())
  131.                 ->buildIfResourceExists();
  132.             if ($figure) {
  133.                 $figure->applyLegacyTemplateData($this->Templatenull$this->arrData['floating'] ?? null);
  134.             }
  135.         }
  136.         $data = array();
  137.         if ($this->rsce_data && substr($this->rsce_data01) === '{') {
  138.             $data json_decode($this->rsce_data);
  139.         }
  140.         $data $this->deserializeDataRecursive($data);
  141.         foreach ($data as $key => $value) {
  142.             $this->Template->$key $value;
  143.         }
  144.         $self $this;
  145.         $this->Template->getImageObject = function() use($self) {
  146.             return call_user_func_array(array($self'getImageObject'), func_get_args());
  147.         };
  148.         $this->Template->getColumnClassName = function() use($self) {
  149.             return call_user_func_array(array($self'getColumnClassName'), func_get_args());
  150.         };
  151.         $this->addFragmentControllerDefaults();
  152.     }
  153.     /**
  154.      * Deserialize all data recursively
  155.      *
  156.      * @param  array|object $data data array or object
  157.      * @return array|object       data passed in with deserialized values
  158.      */
  159.     protected function deserializeDataRecursive($data)
  160.     {
  161.         foreach ($data as $key => $value) {
  162.             if (is_string($value) && trim($value)) {
  163.                 if (is_object($data)) {
  164.                     $data->$key StringUtil::deserialize($value);
  165.                 }
  166.                 else {
  167.                     $data[$key] = StringUtil::deserialize($value);
  168.                 }
  169.             }
  170.             else if (is_array($value) || is_object($value)) {
  171.                 if (is_object($data)) {
  172.                     $data->$key $this->deserializeDataRecursive($value);
  173.                 }
  174.                 else {
  175.                     $data[$key] = $this->deserializeDataRecursive($value);
  176.                 }
  177.             }
  178.         }
  179.         if ($data instanceof \stdClass) {
  180.             $return = new class extends \stdClass{
  181.                 public function __get($name) {
  182.                     return null;
  183.                 }
  184.             };
  185.             foreach ($data as $key => $value) {
  186.                 $return->$key $value;
  187.             }
  188.             $data $return;
  189.         }
  190.         return $data;
  191.     }
  192.     /**
  193.      * Get an image object from id/uuid and an optional size configuration
  194.      *
  195.      * @param  int|string                        $id         ID, UUID string or binary
  196.      * @param  string|array|PictureConfiguration $size       [width, height, mode] optionally serialized or a config object
  197.      * @param  int                               $maxSize    Gets passed to addImageToTemplate as $intMaxWidth
  198.      * @param  string                            $lightboxId Gets passed to addImageToTemplate as $strLightboxId
  199.      * @param  array                             $item       Gets merged and passed to addImageToTemplate as $arrItem
  200.      * @return object                                        Image object (similar as addImageToTemplate)
  201.      */
  202.     public function getImageObject($id$size null$deprecated null$lightboxId null$item = array())
  203.     {
  204.         if (!$id) {
  205.             return null;
  206.         }
  207.         $figure System::getContainer()
  208.             ->get('contao.image.studio')
  209.             ->createFigureBuilder()
  210.             ->from($id)
  211.             ->setSize($size)
  212.             ->enableLightbox((bool) ($item['fullsize'] ?? false))
  213.             ->setLightboxGroupIdentifier($lightboxId)
  214.             ->setLightboxSize(StringUtil::deserialize($item['lightboxSize'] ?? null) ?: null)
  215.             ->setMetadata((new ContentModel())->setRow($item)->getOverwriteMetadata())
  216.             ->buildIfResourceExists();
  217.         if (null === $figure) {
  218.             return null;
  219.         }
  220.         return (object) array_merge($figure->getLegacyTemplateData(), ['figure' => $figure]);
  221.     }
  222.     /**
  223.      * Get the column class name for the specified index
  224.      *
  225.      * @param  int    $index Index of the column
  226.      * @return string        Class name(s)
  227.      */
  228.     public function getColumnClassName($index)
  229.     {
  230.         if (!class_exists(ColumnsStart::class)) {
  231.             return '';
  232.         }
  233.         $config ColumnsStart::getColumnsConfiguration($this->arrData);
  234.         $classes = array('rs-column');
  235.         foreach ($config as $name => $media) {
  236.             $classes array_merge($classes$media[$index count($media)]);
  237.             if ($index count($media)) {
  238.                 $classes[] = '-' $name '-first-row';
  239.             }
  240.         }
  241.         return implode(' '$classes);
  242.     }
  243.     private function addFragmentControllerDefaults()
  244.     {
  245.         $this->Template->template ??= $this->Template->getName();
  246.         $this->Template->as_editor_view ??= System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest(System::getContainer()->get('request_stack')->getCurrentRequest() ?? Request::create(''));
  247.         $this->Template->data ??= $this->objModel $this->objModel->row() : $this->arrData;
  248.         $this->Template->nested_fragments ??= [];
  249.         $this->Template->section ??= $this->strColumn;
  250.         $this->Template->properties ??= [];
  251.         $this->Template->element_html_id ??= $this->Template->cssID[0] ?? null;
  252.         $this->Template->element_css_classes ??= trim(($this->Template->cssID[1] ?? '') . ' ' implode(' '$this->objModel ? (array) $this->objModel->classes : []));
  253.         if (
  254.             (!\is_string($this->Template->headline) && $this->Template->headline !== null)
  255.             || (!\is_string($this->Template->hl) && $this->Template->hl !== null)
  256.         ) {
  257.             return;
  258.         }
  259.         // Legacy templates access the text using `$this->headline`, twig templates use `headline.text`
  260.         $this->Template->headline = new class($this->Template->headline$this->Template->hl) implements \Stringable
  261.         {
  262.             public ?string $text;
  263.             public ?string $tag_name;
  264.             public function __construct(?string $text, ?string $tag_name)
  265.             {
  266.                 $this->text $text;
  267.                 $this->tag_name $tag_name;
  268.             }
  269.             public function __toString(): string
  270.             {
  271.                 return $this->text ?? '';
  272.             }
  273.             public function __invoke(): string
  274.             {
  275.                 return $this->text ?? '';
  276.             }
  277.         };
  278.         // The parent::generate() method overwrites the template headline with $this->headline
  279.         // so we need to set it to the same callable object here
  280.         $this->headline $this->Template->getData()['headline'];
  281.     }
  282. }